记录性能优化的小事情-记录1

在过去的一周里,按照前一篇的优化目标,我review了一遍核心接口实现,并逐一进行了改善。总结起来,串行改并行,单次改批量对性能提升贡献最大,原因很简单:

假设一次rpc花费时间在[1,10]ms之间,一个接口调用5次,那么最差可能是50ms,而改为并行最差就是10ms。

关于etag

在yii2.0中,可以针对Response组件的beforeSend事件做hook(配置中添加on beforeSend的处理函数)实现。因为公司项目封装统一的接口返回方法,所以我直接修改该方法添加etag支持。

先对返回体做json_encode,然后计算md5作为etag值,如果请求里的etag和本次计算相同,那么返回304表示内容没有变化。

这里$curEtag之前加了一个W/的前缀的原因,是因为nginx在开启gzip的情况下不会将etag传回给客户端,理由大概是nginx如果对response做出任何修改,fcgi返回的etag都可能失去原本的意义,因此Nginx做了此限制。而在nginx1.7.3版本之后nginx提出weak etag概念,表达了一种逻辑上的response相同,通过etag添加W/前缀,nginx即可识别并返回客户端了。

下一步怎么做

经常说不要做过早的优化,其实没有目标的优化也是无用功。经过对日志分析得到如下的时间轴,优化的目标就很明确了(点击看大图)。

需要说明,公司的app以hybird方式运行,简单理解就是前端组件包是提前下载到端上的,运行时采用native和web混合的方式,作为web页只需要访问后端数据接口进行页面进一步渲染即可。

 dns

解析dns耗时是可以理解的(网络延迟,dns递归解析,运营商质量等因素),经验值大概在60ms。同时,dns存在劫持问题,稳定性取决于运营商。

优化的思路有2方面:

  • 不走运营商dns,通过ip直连自己的dns服务器进行dns解析,这样可以避免运营商不稳定与不安全因素。这个dns服务器可以基于http自定义协议实现,也可以走socket自定义协议。这个过程一般是native代码进行实现的,适用于hybird app。
  • 预解析dns,可以避免第一个访问请求的dns耗时,例如在app启动后就进行一次域名解析缓存到本地,后续的请求就免去了域名解析的耗时(也有基于html meta的dns-prefetch方式支持预解析)。如果预解析是通过系统dns接口实现的,那么后续请求可以直接通过原生ajax发起,因为dns cache是是操作系统默认行为。如果预解析是自定义方式获取的,那么原生ajax是无法完成域名到ip映射的,因此网络请求必须经过native代码进一步加工完成。

tcp-connect&ssl

tcp连接3路握手是同步的过程,花费1.5个rtt(round-trip time)的样子,经验值大概60ms这个量级,ssl也是类似的。既然http必须建立tcp连接,那么优化的思路就是尽量减少建立连接的次数。

优化思路2个方面:

  • 提前建立连接,和dns思路类似。在启动app后,或者即将访问某个域名之前就将tcp连接建立起来。最简单的办法就是在合适的时机(预测用户下一步要打开的域名是什么),提前发起一次http请求,这样既可以实现dns预解析,也可以实现tcp预连接。当然,webserver要配置开启keep-alive,避免连接立即被关闭。
  • 避免建立连接,也就是尽量复用域名,比如全站只用1个域名,那么就可以始终复用一个连接,例如dns,tcp,ssl握手的时间都可以节约下来。如果此前规划了多个域名用于服务划分,后续希望复用域名提升性能,那么可以采用反向代理,但同时需要关注反向代理增加了一次网络访问是否带来了更大的延时。

network

提升网络响应速度很重要,因为中国网民wifi,4g,3g,2g网络混杂,质量不一。

对于不经常变更的静态内容,通过cdn可以加速用户就近访问,获得更快的下载速度。同时,http协议支持cache-control,last-modify,etag等缓存手段,可以减少/避免网络传输的数据量,从而提升访问速度。

对于动态内容,我们一般通过业务逻辑实现数据级的缓存(redis),只能实现后端接口加速而无法避免网络拥塞带来的延时。

优化思路有2方面:

  • 既然一定要传输,那么压缩数据大小就可以提升下载速度。通过精简接口的返回值,开启gzip压缩,都可以减少数据量,少传肯定会更快。
  • 如果内容没有变化,直接避免传输。这里可以基于last-modify或者etag实现,本文最开始基于response的etag就实现了这样的功能。

除了上述优化的实施落地以及收益评估,下一步会更加关注前端性能提升,到时候有新的心得体会再记录下来吧。

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~