HTTPS优化之动态调整 TLS record size

Dynamic TLS Record Size

 

背景知识

1:TCP 分段

网络报文对的格式一般都是  mac header + ip header + tcp header + tcp payload

由于端对端的网络之间存在不同的链路环境,一个报文传输过程中,经过不同的网络设备,不同的网络设备的MTU不通,即

允许传输 ip层数据(算上ip头)不超过MTU大小的报文,如果ip层数据大于MTU,则进行IP分片,将IP层的负载分在不同的ip头的传输。

TCP为了尽可能避免下层出现这种分片,需要通过TCP握手协商出一个MSS来限制自己的payload大小。

上图中,由于MSS限制,一个5000字节的数据调用send 接口发送后,被分成了4个TCP包。

 

2:考虑TCP负载为SSL的情况

 

 

上图中,一个完整的SSL封装,可能被放置在多个TCP负载中传输。

 

3:SSL发送:
流程图如上图所示不再赘述。


4:SSL接收:
    SSL首先调用read 5字节的 SSL头,从头中读取len字段,该字段指示了SSL负载大小,于是SSL根据这个这个len 去read这个后续的数据,read完len指定的数据之后执行后续数据处理操作,例如解密等

 

 

 

5:Record size
    TLS协议总共包括2层协议,最底层为record procotol,其负载有 handshake protocol、alert procotol、change cipher protocol、application protocol。
    上图中,SSL头+密文数据 这种,就是record protocol + application protocol这种模式。而动态record size的优化也是针对这种模式。
    不考虑明文数据被加密后,长度的变化,即假设明文数据被加密后与密文数据一致,record size可以简单理解为 送入 SSL_write接口的明文数据的长度。而动态record size就是对于large的明文数据,进行分割,分批次送入SSL_write(和TCP协议栈根据MSS分段数据有异曲同工之妙)。

 


不同record size对性能的影响


1:小 record size
(1):问题显而易见,SSL头、SSL负载比肯定变大;其次依据小的record size分割大的数据,肯定增加调用SSL write的次数,重复执行部分代码;最后,使用过硬件加速的同学肯定知道,加解密卡中加解密的性能指标更多的是跟 次/秒 有关,即固定每秒 6000次/秒 运算,如果每次传入1B,那么吞吐就是6KB/s,如果每次传入16KB,那么吞吐就是93MB/s,前者显然没有充分利用硬件资源。


(2):好处就是,小record size,其产生的完整的SSL报文,可以存放在单个TCP负载中,即接收端收到一个TCP负载就可以进行解密(因为包含了完整的ssl payload)。在处理HTTP响应中,很多情况可以根据HTTP header中的信息就可以处理下一步了,而不需要等到实体到来。如果header 和 body 在一个SSL报文中,很可能因为body太大,导致SSL报文分在多个TCP负载中,接收端为了能够处理header,必须读完整个超大的SSL报文,才能进行解密。


2:大 record size
(1):解决了小 record size 中(1)中的问题。
(2):制造了小 record size 中(2)中的问题。

 

 

 


3:TCP的行为对record size 的要求

    之前已经说过,如果一个SSL报文过大,将被放在多个TCP负载中传输,如果中间某个TCP包丢包,导致重传等情况产生,会严重影响接收端接受一个完整SSL报文的时间,进而影响响应速度。

    其次TCP在连接初始阶段处于慢启动阶段,发包受限于cwnd(拥塞控制窗口),连接初始时cwnd为3,从一个round trip来看,其值增长的逻辑是“每收到n个ack就能发送2n个包”,从某一时刻来看,其增长的逻辑是 cwnd += ackd_packet_num,不管怎么样,连接初始阶段,TCP发包会被cwnd。这就导致如果一个SSL报文过大,假设他被放在了10个TCP负载中,那么发送端第一次只会发送3个。。。


4:对record size的取值
    综上所述,record size 大小不是一成不变的,需要动态调整,cloudflare 的方案如下

 

    https://blog.cloudflare.com/optimizing-tls-over-tcp-to-reduce-latency/

 

    https://github.com/cloudflare/sslconfig/blob/master/patches/nginx__dynamic_tls_records.patch


4个变量控制
ssl_dyn_rec_size_lo
ssl_dyn_rec_size_hi
ssl_dyn_rec_threshold
ssl_dyn_rec_timeout


    初始阶段,record size设置为ssl_dyn_rec_size_lo,发送完ssl_dyn_rec_threshold个包之后,record size设置为ssl_dyn_rec_size_hi;再次发送完ssl_dyn_rec_threshold之后,将record size设置为 ssl_buffer_size(nginx默认的size)。
    如果 应用层过了ssl_dyn_rec_timeout个时间内,没法送过数据,将record size设置为为ssl_dyn_rec_size_lo,重复上述步骤。
    ssl_dyn_rec_timeout是有必要的,TCP协议也有类似的操作,socket如果idle了一定时间,其cwnd也会被设置为初始值,cloudflare这么设置也契合了TCP的这个特性。

 

 

 

Cloudflare patch的问题

    我觉得不是他们提供的方案有问题,而是可能有些东西不能对外宣布。

    首先,ssl_dyn_rec_size_lo和ssl_dyn_rec_size_hi大小看到应该是渐变的而不是简单的翻一番。

    其次,结果SSL握手之后,由于证书可能就好几kb,拥塞窗口早就扩大了,所以lo值肯定不是默认值13xx那么简单。

这些变量的值应该需要根据特定的网络、业务环节特定的进行设置才能起到所谓的优化。

 

如果HTTPS作为前端卸载用,即类似Nginx的SSL卸载,Nginx作为代理服务器,Nginx upstream收到的后端的包大小其实也是1k左右,这个功能基本没什么用,因为即使record size扩的再大,ngx_ssl_write_chain的入参数据也就1k。

 

但是如果机器开启了GRO,TRO等,收包时会将小包合并成大包,然后一并处理,这种情况下的反向代理,是有效果的。

 
总结:
这个功能上线效果大不大,和实际场景有关系。
1、 你调的再大的record size,你解密的量是固定的(即应用数据量是固定的),减少 ssl_read的操作,不代表 能减少 端 的性能损耗。相对 对称加解密的性能损耗 而言,代码中多少调用几次函数,真的影响不大。
2、和网络时延也有一定关系,网络时延大,record size调大,一段能被解密的应用数据,就被分成多个tcp包,从而客户端得等较久才能收完能解密的数据量。
3、和客户端性能也有关系,如果客户端性能差,那条record size 基本没效果。tcp 性能够?解密性能够?应用性能够(你能够read完)。
4、花里胡哨。
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值