面对日益复杂的技术世界,App 在开发、上线和运维阶段所遭遇的问题也越来越多。这些形形色色的问题可能来自整个链路的任意环节,而不仅仅是代码层面。
对于开发者来说,排查手段已经不再局限于构建代码过程中的调试,往往需要扩充排查方法,从多种途径对问题进行分析和定位。这篇文章会和大家分享 mPaaS 开发者的一例小程序网络性能问题排查之旅。
问题背景
“笑联科技”反馈基于 mPaaS 开发的 App 中,其集成的小程序访问客户自建的 Web API 存在连接慢的性能问题。问题复现视频如下:
从问题复现的情况看,打开小程序后,页面数据的加载有一个“漫长”的等待过程。
和开发者沟通后了解到,页面初始化所必须的部分数据是通过自有的 Web API 获取到的,数据返回慢会导致页面加载的等待。另外开发者也提到,这个问题存在地域性和偶发性,既部分地区的部分用户在一段时间内会被这个问题严重困扰。
问题分析与排查
如前文所述,数据是通过 Web API 获取的,自然我们希望通过外部手段去确认这个 Web API 本身是否存在性能问题。
然而,通过浏览器或 Postman 等工具去访问该 Web API ,均无法复现问题,后端的响应都是毫秒级。但是因为开发者提到该问题存在地域性和偶发性,因此无法直接排除部分原因。
由于我们并不是 App 的直接开发者,对于这类问题,一种常规的手段是抓取 HTTP 报文来观察和理解 App 背后的行为特征。比较幸运的是,我们的测试用 iOS 手机可以复现问题,通过 Charles 抓取 App 报文,我们有如下发现:
当 Charles 开启 SSL Decryption 时(中间人解密 HTTPS Body 模式),问题无法复现。
当 Charles 关闭 SSL Decryption 时,问题可以复现,数据加载明显存在一个 3s 的等待情况。
上述现象 2 和 3 强烈暗示问题可能和 HTTPS/SSL 协议层面相关(开启 SSL Decryption 时,HTTPS 连接由 Mac 笔记本和服务器进行;关闭 SSL Decryption 时,HTTPS 连接由 iPhone 和服务器进行)。
对于 SSL 层面的问题,则需要抓取 TCP 报文做进一步的确认和分析。使用 Wireshark 进行网络层抓包(基本抓包步骤:iPhone 正常接入网络;iPhone 通过数据线连接到 Mac 上,并对手机网卡搭建一个虚拟映射;Wireshark 在该映射上进行抓包;详细步骤参考这里)。
问题复现并抓取到相关报文后,首先确认问题,如下图所示:
通过上图日志可以看到,在 TLS 握手阶段,客户端在流程上延迟了 3s 才把 Client Key Exchange 消息发给服务端,而正常情况下,不应该存在如此漫长的等待情况。于此同时,开发者在 Debug 包上的前端嵌入调试也确认了相关情况,如下图所示:
接下来需要搞清楚,客户端为何在握手阶段等待了如此之久以及这 3s 期间客户端在做什么?放开网络包过滤条件后,通过阅读网络包的上下文,我们有了进一步的发现,如下图所示:
在上图中,可以看到客户端一直尝试与一个 IP 为 243.185.187.39 的站点建立 TCP 连接,但均遭遇失败。这里会有两个疑问:1. 这个站点是干什么的?2. 为何客户端要在 TLS 握手过程中先去连该站点?
通过同一个网络包中的 DNS 查询记录,尝试反查该 IP 对应的域名地址,发现该站域名为:a771.dscq.akamai.net:
通过公网搜索该域名,得知这个域名是 Let's Encrypt (全球最大的免费证书机构)证书的 OCSP (Online Certificate Status Protocol,用于校验证书状态) 地址,但需要进一步确认该证据。在网络包中,查看服务端返回证书帧的详细信息,确认证书的 OCSP 地址为 http://ocsp.int-x3.letsencrypt.org:
由于该 OCSP 地址和网络包中看到的不一致,本地通过 nslookup 再进一步确认:a771.dscq.akamai.net 是 ocsp.int-x3.letsencrypt.org 的一个 CNAME 地址(这种配置一般为了站点加速):
结合上述情况和公开资料,可以确认在 3s 的等待期内,客户端尝试去连接证书提供的 OCSP 站点地址,意图确认证书的吊销状态。仔细观察会发现,本地解析出的 IP 地址和手机端抓包看到的 IP 地址是不一样的,这里大概率是 Let's Encryped 证书的 OCSP 地址被“污染”导致的。
到这里,我们可以看到问题的小结为:客户端需要和 https://api.xiaolianhb.com/
建立 HTTPS 连接,在 TLS 握手阶段,客户端无法连上该站点证书提供的 OCSP 地址,因此无法确认证书的吊销状态,3s 后触发超时放行机制,客户端和站点正常建立 HTTPS 连接,请求发送和数据返回流程得以进行。
既然 Let's Encrypt 证书的 OCSP 域名被“污染”已经成为事实,因此,要解决该问题,最快的解决方案是更换站点证书,保证 TLS 握手流程的顺畅。
小结
在这个案例中,我们可以看到有些时候,问题和代码、SDK 亦或是系统 Bug 并无直接关联,异常情况可能来自一个意想不到的地方。
回到问题症状上,更进一步的疑问是:为何桌面浏览器或网络工具受影响较小?为何 Android 手机不受影响?这些症状细节上的差异,均和不同系统或工具对协议的实现形式相关。
开发者很难在一开始的规划阶段就能把这些细枝末节的问题都预估到,因此,出现问题之后,深入的问题剖析配合日志解读往往是理解程序行为背后逻辑的重要手段。
CodeDay#5:深入探索支付宝终端
在过去的一年中,我们通过与众多终端开发者在能力对接、需求沟通中发现,愈来愈多的研发团队面临业务需求爆发时难以找到有效的方式进行高并发支撑。
大家的问题呈现出了共性特征:如何实现动态发布?如何进一步提升研发效率?支付宝是否有最佳实践?
因此,此次 CodeDay 我们把焦点放在“支付宝终端”,尝试通过 4 个议题分享,带领大家了解支付宝作为一款超级 App,如何借助容器化技术实现动态发布、更新能力,并沉淀出一套可复用的技术体系。
1 月 23 日,我们广州见。
延伸阅读