优化 Tengine HTTPS 握手时间

背景

网络延迟是网络上的主要性能瓶颈之一。在最坏的情况下,客户端打开一个链接需要DNS查询(1个 RTT),TCP握手(1个 RTT),TLS 握手(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个 RTT,SSL 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字节)一致

(全文完)


原文链接
本文为云栖社区原创内容,未经允许不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/518160.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

揭秘!如何用Flutter设计一个100%准确的埋点框架?

阿里妹导读:用户行为埋点是用来记录用户在操作时的一系列行为,也是业务做判断的核心数据依据,如果缺失或者不准确将会给业务带来不可恢复的损失。闲鱼将业务代码从Native迁移到Flutter上过程中,发现原先Native体系上的埋点方案无法…

安装rzsz

yum install -y lrzsz

抽象思想解读Linux进程描述符

来源 | 嵌入式客栈责编 | Carol头图 | CSDN 下载自视觉中国内核是怎么工作的?首先要理解进程管理,进程调度,本文开始阅读进程管理部分,首先从进程的抽象描述开始。抽象是软件工程的灵魂,而对于Linux操作系统而言&#…

CentOS Linux 7.7 安装kafka zookeeper

文章目录一、软件下载1. zookeeper2. kafka二、安装与启动2.1. jdk2.2. zookeeper2.3. kafka三、 kafka 基本演示一、软件下载 1. zookeeper http://archive.apache.org/dist/zookeeper/zookeeper-3.5.7/apache-zookeeper-3.5.7-bin.tar.gz 2. kafka https://archive.apach…

《Java开发手册》2019最新版发布!

致全球Java开发者: 代码是二进制世界的交流方式,极致的代码是我们的荣耀。 2017年春天,《阿里巴巴Java开发手册》发布,我们希望在涵盖编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程规约、设计规约等7个维度上为开…

IDEA 惊天 bug:进程已结束,退出代码 1073741819

来源 | 沉默王二责编 | Carol头图 | CSDN 下载自视觉中国今天要写的文章中涉及到一串代码,关于 Undertow 的一个入门示例,贴出来大家看一下。public class UndertowTest {public static void main(final String[] args) {Undertow server Undertow.buil…

python3-Anaconda3 基本使用

1、下载 最新版本官网下载: https://www.anaconda.com/distribution/ 历史版本 清华镜像(国内首选): https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 历史版本 官网镜像: https://repo.anaconda.com/archi…

淘宝应用柔性架构的探索

导读:随着淘宝业务的飞速发展,微服务架构在持续演进的过程中,也受到了越来越多的挑战:如同步模型带来的资源利用率有限、依赖调用并发度有限、下游故障引发应用自身出问题;又如静态限流随着业务代码的演进、依赖拓扑的…

iOS13 一次Crash定位 - 被释放的NSURL.host

每年一次的iOS升级,都会给开发者带来一些适配工作,一些原本工作正常的代码可能就会发生崩溃。 本文讲到了一种 CoreFoundation 对象的内存管理方式在iOS13上遇到的问题。 1. 问题 iOS 13 Beta 版本上,手淘出现了一个必现的崩溃: …

面试官吐槽:“Python程序员就是不行!”网友:我能把你面哭!

最近几年,Python莫名火了起来,很多公司都想赶上这“莫名”的热潮,招聘到大牛人才。但是,最近一个HR在社交网站的吐槽又火了:那么问题来了,市面上为什么鲜有企业满意的优秀的Python程序员?企业到…

python3-pandas 数据结构 Series、DataFrame 基础

Pandas 应用 Pandas 的主要数据结构是 Series (一维数据)与 DataFrame(二维数据),这两种数据结构足以处理金融、统计、社会科学、工程等领域里的大多数典型用例。 数据结构 Series 是一种类似于一维数组的对象&#xf…

十年磨一剑 | 淘宝如何打造承载亿级流量的首页?

阿里妹导读:手机淘宝作为整个互联网领域旗舰 APP 之一,装机量和用户访问量都是名列前茅的。而首页作为打开手机淘宝的门面,是淘宝电商领域的主要流量入口和服务消费者的核心阵地,其业务的复杂性之高、系统的稳定性之重都有着极高的…

switchhosts 没有修改hosts的权限解决方案

使用swtichHost工具切换开发环境时候提示没有权限问题,如下图。。 解决方案有两点 1、进入 C:\Windows\System32\drivers\etc右键点击hosts的属性查看 属性的只读是否被勾选了,如果被勾选了将勾选勾去掉 上述完成后以管理员身份运行(管理员…

闲鱼亿级商品结构化背后的思考和演进

1. 缘起 闲鱼是一个典型的C2C场景的闲置交易平台。每个在闲鱼的用户都能享受到自由交易的乐趣。在这里,可能你只要简单的输入商品名,商品价格,库存等信息就能完成一个商品的发布。即便是发布以后,你也可以随时修改价格&#xff0…

QingStor NeonSAN跻身四强 新风口下的青云QingCloud正在厚积薄发

人类以日新月异的速度刷新着科技的成果,其中存储的发展历史尤其悠久,堪称万年进化史。自文明诞生以来,我们就一直在寻求能够更有效存储信息的方式,从4万年前的洞穴壁画、6000年前泥板上的楔形文字,到今天正在普及的SSD…

python3-pandas DataFrame 索引、bool索引、pandas 字符串方法

1、DataFrame 索引 1.1 普通索引取值 pandas 取行或者列的注意点: 方括号写数组,表示取行,对行进行操作方括号写字符串,表示取列,对列进行操作 import pandas as pd import numpy as np # pandas 取行或者列的注意…

记一次Cassandra Java堆外内存排查经历

背景 最近准备上线cassandra这个产品,同事在做一些小规格ECS(8G)的压测。压测时候比较容易触发OOM Killer,把cassandra进程干掉。问题是8G这个规格我配置的heap(Xmx)并不高(约6.5g)已经留出了足够的空间给系统。只有可能是Java堆…

程序员内功修炼系列:10 张图解谈 Linux 物理内存和虚拟内存

来源 | 后端技术学堂责编 | Carol封图 | CSDN 付费下载于视觉中国我们都知道,程序可没这么好骗,任你内存管理把虚拟地址空间玩出花来,到最后还是要给程序实实在在的物理内存,不然程序就要罢工了。所以物理内存这么重要的资源一定要…

阿里高级技术专家方法论:如何写复杂业务代码?

阿里妹导读:张建飞是阿里巴巴高级技术专家,一直在致力于应用架构和代码复杂度的治理。最近,他在看零售通商品域的代码。面对零售通如此复杂的业务场景,如何在架构和代码层面进行应对,是一个新课题。结合实际的业务场景…

Android Studio 安装教程

注意安装之前请配置好java 和 Android SDK 1、下载 官网地址: https://developer.android.google.cn/studio/ 点击下载后,需要同意协议: 2、安装 1、双击程序 2、一路 next,如果想修改路径可自行修改。 3、安装完成点击Fi…