课程实录 | 借助 F5 NGINX 交付极致用户体验(下)
原文作者:廖健雄 - F5 资深解决方案顾问
转载来源:NGINX 中文官网
NGINX 唯一中文官方社区 ,尽在 nginx.org.cn
编者按——本文为 NGINX 线上讲座“借助 F5 NGINX 交付极致用户体验”的课程实录的下篇。点击此处阅读本次课程实录的上篇。
在本节课程中,廖健雄老师详细介绍了用户访问体验的内容和量化,以及如何通过 Brotli 压缩、动态 webp 转换、HTTP/2 部署优化等具有可实操性的方式来提升用户访问体验。
本次分享主要分为三部分。首先,介绍什么是用户体验。其次,从技术角度分享提升用户体验的手段和方法。最后,廖健雄老师将与大家分享在某股份制商业银行的案例,详细介绍如何帮助客户提升和交付极致用户体验。
下面我们将围绕数据压缩、优化 SSL/TLS、HTTP/2 这几个特性,来跟大家分享我们在某股份制大行的用户访问提升的案例。
交付极致用户体验
HTTP/2 部署优化
自 90 年代诞生以来,HTTP 作为互联网的基石已发展了近 30 年的发展。从最初的 HTTP/0.9、HTTP/1.0 ,到 HTTP/1.1 的普及,再到如今的 HTTP/2 和 HTTP/3,HTTP 协议在伴随互联网的发展中不断优化、进步。
根据 2023 年 11 月 W3Techs.com 的统计数据,目前全球所有网站均支持 HTTP/1.1,35.6% 的网站支持 HTTP/2,27% 的网站支持最新的 HTTP/3。
HTTP/1.1 采用文本传输方式,HTTP/2.0 相比 HTTP/1.1 的优化集中在 HTTP 协议层。HTTP/2.0 的特点包括引入了二进制传输概念,可以有效利用网络资源提升性能。这种二进制传输方式更紧凑,减少协议开销,提升页面加载速度。
HTTP/2.0 还引入了对用户体验提升比较明显的能力,包括多路复用、头部压缩。在 TLS 层面,HTTP/2 存在一定争议。绝大多数浏览器的实现要求 HTTP/2 一定要求使用 HTTPS 协议,但实际上 HTTP/2 的 RPC 规范里并没有对 TLS 做强制要求,因此在这方面存在一些争议。
相较于 HTTP/2,HTTP/3 最大的变化是,在传输层,从原先的 TCP 转变为 UDP,并引入了基于 UDP 的 QUIC 协议。这一改变显著减少了在建立 TCP 连接时产生的时延。在 UDP 的基础上,HTTP/3 引入了 0-RTT 概念,可以在未建立连接的情况下即时发送数据。这些优化措施进一步提升了用户的访问体验。此外,HTTP/3 还解决了 HTTP/2 中存在的头部阻塞问题。
由于 HTTP/3 是新发布的,今天我们还是主要围绕 HTTP/2 展开介绍。HTTP/2 对用户访问体验提升比较明显的两个特性,一个是多路复用,一个是头部压缩。
HTTP/1.1 采用文本传输,每个字符可能需要 8 个 bit,HTTP/2.0 引入了二进制传输,相当于是帧化的方式来传输,将请求分为 header 帧和 data 帧。在连接层面上,HTTP/1.1 的每一个请求,需要等待上一个请求的响应,收到上一个请求之后才能发起下一个请求,属于串行的方式。
HTTP/2 实现了多路复用,在每一个流里面,不同 frame 可以穿插在一起,因此,它可以一次性的发送多个请求,每一个请求都是 stream 方式呈现,从而极大的减少请求之间的 RTT,来提升用户访问体验。
HTTP/2 引入了头部压缩 HPACK。
HTTP/1.1 中,在同一条连接上发起多个请求,而它们头部(header)存在一些重复内容。例如,都是使用 GET 方法,具有相同的 scheme、host 和 user-agent、以及相同的 cookie,路径可能会不一样。
HTTP/1.1 也有压缩特性,是对 body 压缩,而不是对 header 压缩。因此意味着在同一条连接的传输请求,许多内容是重复的,造成了这种传输方式效率比较低。
HTTP/2 引入了头部压缩,它的核心理念是利用索引表来追踪请求的 header。索引表分为静态和动态两部分。静态索引表由 HPACK 预先定义了 61 个项,涵盖了比较主流和通用的字段。其他项放到了动态表里,比如,user-agent 字段包含很多不同的 user-agent 内容,就会放在动态表里。
当发起一个请求时,客户端会将相应的 user-agent 放入动态表中。服务端在收到请求后,也会放到对应的动态表里。当再次发起请求时,我们就不用发送完整的信息了,只需要发送这个 user-agent 的索引。当一个连接上发布的请求越来越多的时候,动态表也会越来越丰富,因此下一个请求的压缩效率就会提升。
Cloudflare 网站有一篇 blog,提到 HPACK 是 HTTP/2 的杀手锏。如上图所示,我们来看 HTTP/1.1 和 HTTP/2 的对比,假设 body (橙色)已经进行了极致的压缩,HTTP/1.1 和 HTTP/2 的 body 基本一致。双方的头部相差比较大,整体上,HPACK 可以实现一半以上请求的压缩效果。数据量越小,传输速度越快,对用户体验的提升也比较明显。
在部署 HTTP/2 之后,我们会遇到一个有意思的问题。网站并非一次性处理所有请求,而是每次只处理 10 个请求,后续的请求会被阻塞掉,这不是理想状态,我们希望请求能够尽可能一次性全部发送,除非请求之间存在关联。
经过研究发现,HTTP/2 请求的并发数量是由 stream 的数量决定的。在 stream 中,每个请求按每个 stream 发送,也就是说 stream 的数量决定了请求的数量。
最初,我们在 Big IP 上通过 HTTP/2 的 profile 来实现,HTTP/2 默认每个连接的 stream 并发数为 10 个。相比之下,NGINX 的 stream 默认并发数是 128 个,这就是请求只发送 10 个后被阻塞的原因。
此外,还有一个参数叫 receive windows,它也决定了客户端可以一次性发送 data 帧的数量,大家可以根据实际情况做相应的调整。
这张图是用户部署了 HTTP/2 之后,提供的一些对比数据。这家用户本身就是以良好的用户体验著称的,部署了 HTTP/2 之后,用户访问体验也得到了显著提升。
比如,文件的下载时间从原先的 358 毫秒减少到 308 毫秒,时间减少了 14%。APP 启动时进行的相关初始化工作,可能会带来短暂的白屏时间,白屏时间从 239 毫秒降低至 219 毫秒,减少了 8%。页面加载时间从 657 毫秒降到 565 毫秒,同样减少了 14%。由此可见,用户体验已经很优秀的前提下,也是可以通过部署 HTTP/2 获得持续提升。
Brotli 压缩
在数据压缩方面,Brotli 压缩是另一个值得关注的特性。相较于 gzip 压缩,Brotli 压缩能够提供更好的压缩比和更高的编解码效率。比如,用 Brotli 压缩,JavaScript files 的压缩效率可以提升 14%,HTML files 能够实现 21% 的压降,CSS files 能实现 17% 的压降。同时,Brotli 压缩和解压的效率均高于 gzip 压缩。
上图左侧可以看到对比。我们知道不同的压缩 level 下,压缩效率和 CPU 开销成反比。因此我们需要根据实际需求进行平衡。通过对比 GZIP(6) 和 Brotli(4) 发现,Brotli(4) 的压缩效率更高,压缩速度提升了 21%,压缩后的文件大小也提升了 9%。
因此,我们建议在部署中采用 Brotli 压缩。值得注意的是,目前许多浏览器,包括 chrome 和火狐,只在 HTTPS 访问中会带上 Brotli 压缩的 tag,而在 HTTP 访问中不会带上。
Br 的部署相对比较简单。对于 NGINX 的用户,NGINX 的官方镜像仓库已经打包好 Br 作为第三方动态模块。因为它已经编译好,用户可以直接下载,只需在 NGINX 命令上使用 load_module 方式进行加载,并开启 Br,即可实现 Br 的功能。对于开源用户,也可以在第三方网站,如谷歌的 GitHub 上 ,将 Br 打包上去。
有人可能会问,我同时配置了 gzip 和 Br,它们是如何工作的?实际上,它会根据客户端请求的 Accept-Encoding 字段以及 response 响应 encoding 字段进行综合判断。如果客户端既支持 gzip 又支持 Br,并且上游没有进行压缩,那么它会优先使用 Br 压缩并返回给客户端。
根据客户提供的数据,部署 Br 后,与原始的 gzip 相比,在文本部分的流量压降了 18%。
动态 webp 转换
接下来讲第三个特性,webp 图片的转换。
如上图,我们对比了 jpg 和 webp 两种格式的图片。从肉眼观察来看,两者在质量上无明显差异。然而,数据却揭示了其中的差异:左侧的 jpg 图片大小为 946KB,本地下载需 47 毫秒;而右侧的 webp 图片仅有 316KB,本地下载仅需 15 毫秒。
Webp 是谷歌提出的,采用了更加复杂的预测算法,从而在图片层面上实现了更高的压缩比。相较于传统的格式如 jpg 和 png,webp 能够将图片大小降低 50% - 60%。随着下载资源大小的压降,时延也相应减少,从而提升了用户体验。
鉴于 webp 的优越性,我们是否可以将 WebServer 中的图片全部转换成 webp 格式?基于这种想法,我们最早做 webp 改造的实现思路是,通过相应的工具在每个 web 服务器上生成 jpg 和 webp 两种格式的图片,以 webp 作为后缀。同时,在 NGINX 作为反向代理时,会判断客户端是否支持 webp。若支持,则返回相应的 webp 图片;若上游没有提供 webp 格式,则会尝试返回原图。
以上是理想的状态,但在许多场景下,由于改造这一项工作,可能需要协调多个团队或部门一起进行,会带来巨大的管理成本,因此,我们要考虑其他方法。
最终,我们采用的方式是在 NGINX 的反向代理层面设计一个实时图片转换模块,即 image autowebp。当上游服务返回 jpg 或 png 图片时,若客户端支持,将自动将其转换为 webp 格式返回给客户端。
这一使用场景适用于 NGINX 作为统一接入层、API 网关或 L7 处理等场景。这样就可以与上游服务器做解耦,不需要上游服务器进行改造,这是一种相对理想的模型。
这里还会遇到另一个问题,图片改造是 CPU 高密集型操作,若对每个请求进行实时转换,反而会增加延迟。原先下载 jpg 图片可能只需 45 毫秒,而进行图片转换后可能需要 500 毫秒。
为解决这一问题,我们和 NGINX 的 Cache 进行整合,即当某资源或图片已完成转换时,部署 Cache 后,复用已转换好的 webp 资源,从而减少实时处理的时延开销。
最终效果是,通过 webp 的动态转换,客户端下载图片的时间从 55 毫秒降至 15 毫秒,提升效果显著。此外,图片大小也压降了 50%-60%,进一步节约了线路带宽,实现了降本增效的作用。
落地案例分享
上面讲的 3 个 feature 都已经在一个案例中落地实施了,接下来我们详细介绍本案例。本案例是我们协助某股份大行提升了网银及手机银行的用户访问体验。从客户端至服务端,整个解决方案仅包含三个核心组件。
首先,我们部署了入口的高性能负载均衡硬件。其次,SSL 卸载池是采用了 VE+QAT 卡的方式,实现了 SSL 卸载池的弹性拓展,在这一层面部署了 HTTP/2 的 profile。另外,在上游服务器之前,他们部署了 NGINX 作为反向代理,将 Br 压缩、webp 图片格式转换集成在这一层。
具体来说,相当于我们在 NGINX 中开发了三个模块。第一个模块负责分流,将需要图片格式转换的请求转发至内部处理,否则直接转发到业务处理再转发到上游服务器。这一模块还集成了 Br 压缩和缓存功能,对于重复请求,缓存可以直接返回客户端,无需实时进行图片转换。
在图片格式实时转换模块,考虑到图片实时转换对 CPU 资源要求较高,我们实施了限流机制。当请求超过预设限制时,例如 RPS 为 5,第 6 个请求将被旁路处理。这一解决方案不仅提升了用户访问体验,还显著降低了互联网出口带宽开销,图片大小降低了 50-60%,文本压降了 18%。在当前降本增效的大背景下,这一方案无疑为企业带来了巨大的创新价值。
这一完整方案得到了客户的认可,我们还帮助客户进行了国家专利的申请。在帮助客户提升用户访问体验的同时,还完成了创新的 KPI。
NGINX 唯一中文官方社区 ,尽在 nginx.org.cn
版权声明: 本文为 InfoQ 作者【NGINX开源社区】的原创文章。
原文链接:【http://xie.infoq.cn/article/060bdf687724ff6402007e72c】。文章转载请联系作者。
评论