如何看网站的语言,唐山模板建站定制网站,清远清城区,永嘉县住房和城乡建设局网站背景
网络延迟是网络上的主要性能瓶颈之一。在最坏的情况下#xff0c;客户端打开一个链接需要DNS查询#xff08;1个 RTT#xff09;#xff0c;TCP握手#xff08;1个 RTT#xff09;#xff0c;TLS 握手#xff08;2个RTT#xff09;#xff0c;以及最后的 HTTP …背景
网络延迟是网络上的主要性能瓶颈之一。在最坏的情况下客户端打开一个链接需要DNS查询1个 RTTTCP握手1个 RTTTLS 握手2个RTT以及最后的 HTTP 请求和响应可以看出客户端收到第一个 HTTP 响应的首字节需要5个 RTT 的时间而首字节时间对 web 体验非常重要可以体现在网站的首屏时间直接影响用户判断网站的快慢所以首字节时间TTFB是网站和服务器响应速度的重要指标下面我们来看影响 SSL 握手的几个方面
TCP_NODELAY
我们知道小包的载荷率非常小若网络上出现大量的小包则网络利用率比较低就像客运汽车来一个人发一辆车可想而知这效率将会很差这就是典型的 TCP 小包问题为了解决这个问题所以就有了 Nigle 算法算法思想很简单就是将多个即将发送的小包缓存和合并成一个大包然后一次性发送出去就像客运汽车满员发车一样这样效率就提高了很多所以内核协议栈会默认开启 Nigle 算法优化。Night 算法认为只要当发送方还没有收到前一次发送 TCP 报文段的的 ACK 时发送方就应该一直缓存数据直到数据达到可以发送的大小即 MSS 大小然后再统一合并到一起发送出去如果收到上一次发送的 TCP 报文段的 ACK 则立马将缓存的数据发送出去。虽然效率提高了但对于急需交付的小包可能就不适合了比如 SSL 握手期间交互的小包应该立即发送而不应该等到发送的数据达到 MSS 大小才发送所以SSL 握手期间应该关闭 Nigle 算法内核提供了关闭 Nigle 算法的选项 TCP_NODELAY对应的 tengine/nginx 代码如下 需要注意的是这块代码是2017年5月份才提交的代码使用老版本的 tengine/nginx 需要自己打 patch。
TCP Delay Ack
与 Nigle 算法对应的网络优化机制叫 TCP 延迟确认也就是 TCP Delay Ack这个是针对接收方来讲的机制由于 ACK 包是有效 payload 比较少的小包如果频繁的发 ACK 包也会导致网络额外的开销同样出现前面提到的小包问题效率低下因此延迟确认机制会让接收方将多个收到数据包的 ACK 打包成一个 ACK 包返回给发送方从而提高网络传输效率跟 Nigle 算法一样内核也会默认开启 TCP Delay Ack 优化。进一步讲接收方在收到数据后并不会立即回复 ACK而是延迟一定时间一般ACK 延迟发送的时间为 200ms每个操作系统的这个时间可能略有不同但这个 200ms 并非收到数据后需要延迟的时间系统有一个固定的定时器每隔 200ms 会来检查是否需要发送 ACK 包这样可以合并多个 ACK 从而提高效率所以如果我们去抓包时会看到有时会有 200ms 左右的延迟。但是对于 SSL 握手来说200ms 的延迟对用户体验影响很大如下图 9号包是客户端的 ACK对 7号服务器端发的证书包进行确认这两个包相差了将近 200ms这个就是客户端的 delay ack这样这次 SSL 握手时间就超过 200ms 了。那怎样优化呢其实只要我们尽量少发送小包就可以避免比如上面的截图只要将7号和10号一起发送就可以避免 delay ack这是因为内核协议栈在回复 ACK 时如果收到的数据大于1个 MSS 时会立即 ACK内核源码如下 知道了问题的原因所在以及如何避免那就看应用层的发送数据逻辑了由于是在 SSL 握手期间所以应该跟 SSL 写内核有关系查看 openssl 的源码 默认写 buffer 大小是 4k当证书比较大时就容易分多次写内核从而触发客户端的 delay ack。 接下来查看 tengine 有没有调整这个 buffer 的地方还真有下图第903行 那不应该有 delay ack 啊…… 无奈之下只能上 gdb 大法了调试之后发现果然没有调用到 BIO_set_write_buffer_size原因是 rbio 和 wbio 相等了那为啥以前没有这种情况现在才有呢难道是升级 openssl 的原因继续查 openssl-1.0.2 代码 openssl-1.1.1 的 SSL_get_wbio 有了变化 原因终于找到了使用老版本就没有这个问题。就不细去看 bbio 的实现了修复也比较简单就用老版本的实现即可所以就打了个 patch 重新编译打包后测试问题得到了修复。使用新版 openssl 遇到同样问题的同学可以在此地方打 patch。
Session 复用
完整的 SSL 握手需要2个 RTTSSL Session 复用则只需要1个 RTT大大缩短了握手时间另外 Session 复用避免了密钥交换的 CPU 运算大大降低 CPU 的消耗所以服务器必须开启 Session 复用来提高服务器的性能和减少握手时间SSL 中有两种 Session 复用方式
服务端 Session Cache 大概原理跟网页 SESSION 类似服务端将上次完整握手的会话信息缓存在服务器上然后将 session id 告知客户端下次客户端会话复用时带上这个 session id即可恢复出 SSL 握手需要的会话信息然后客户端和服务器采用相同的算法即可生成会话密钥完成握手。
这种方式是最早优化 SSL 握手的手段在早期都是单机模式下并没有什么问题但是现在都是分布式集群模式这种方式的弊端就暴露出来了拿 CDN 来说一个节点内有几十台机器前端采用 LVS 来负载均衡那客户端的 SSL 握手请求到达哪台机器并不是固定的这就导致 Session 复用率比较低。所以后来出现了 Session Ticket 的优化方案之后再细讲。那服务端 Session Cache 这种复用方式如何在分布式集群中优化呢无非有两种手段一是 LVS 根据 Session ID 做一致性 hash二是 Session Cache 分布式缓存第一种方式比较简单修改一下 LVS 就可以实现但这样可能导致 Real Server 负载不均我们用了第二种方式在节点内部署一个 redis然后 Tengine 握手时从 redis 中查找是否存在 Session存在则复用不存在则将 Session 缓存到 redis 并做完整握手当然每次与 redis 交互也有时间消耗需要做多级缓存这里就不展开了。核心的实现主要用到 ssl_session_fetch_by_lua_file 和 ssl_session_store_by_lua_file在 lua 里面做一些操作 redis 和缓存即可。
Session Ticket 上面讲到了服务端 Session Cache 在分布式集群中的弊端Session Ticket 是用来解决该弊端的优化方式原理跟网页的 Cookie 类似客户端缓存会话信息当然是加密的简称 session ticket下次握手时将该 session ticket 通过 client hello 的扩展字段发送给服务器服务器用配置好的解密 key 解密该 ticket解密成功后得到会话信息可以直接复用不必再做完整握手和密钥交换大大提高了效率和性能那客户端是怎么得到这个 session ticket 的呢当然是服务器在完整握手后生成和用加密 key 后给它的。可见这种方式不需要服务器缓存会话信息天然支持分布式集群的会话复用。这种方式也有弊端并不是所有客户端或者 SDK 都支持但主流浏览器都支持。所以目前服务端 Session Cache 和 Session Ticket 都会存在未来将以 Session Ticket 为主。
Tengine 开启 Session Ticket 也很简单 ssl_session_tickets on;ssl_session_timeout 48h;ssl_session_ticket_key ticket.key; #需要集群内所有机器的 ticket.key 内容48字节一致
全文完
原文链接 本文为云栖社区原创内容未经允许不得转载。