大促时SSL握手开销会显著消耗服务器连接数,高并发下握手请求瞬间堆满队列,导致大量用户访问超时,最有效的解法是把TLS握手从源站剥离或大幅减少握手次数。
大促期间,每个新连接都要经历TCP握手加TLS握手,前后多次往返,连接数被占住但不产生业务,看起来在线人数不高,服务器却已经报负载过高,下面拆开看这笔账到底怎么算。
大促SSL握手延迟高,问题出在哪
握手协议的三次往返开销
SSL握手不是一次完成,而是客户端和服务端来回交换证书、密钥参数、结束确认,TLS 1.2时代完整握手需要两次TCP往返,加上TCP握手本身,总共三次往返才能开始传业务数据,TLS 1.3虽然压缩到一次往返,但大促时大量用户用的是老旧APP或系统,仍会走TLS 1.2。
每个未完成握手的连接都占着一个文件描述符、一块内核内存缓冲,以及服务器上的CPU算力,这些资源在握手完成前不能复用,并发一大,连接数就成了瓶颈。
大促流量模型下的连接数放大效应
平时用户打开网站后长连接复用率高,一个TCP连接能承载多次请求,大促时,用户从消息推送、分享链接、广告落地页进入,几乎每个都是新连接,新连接必须走完整握手流程,而且很多用户连页面没加载完就划走,连接被白白占用。
大促的流量洪峰不是平滑上升,而是瞬间爆量,比如秒杀开始的几秒内,每秒新建连接数可能是平日的几十倍,服务器明明还有CPU和带宽,但连接表被握手中的半开连接塞满了。
多数人把连接数当在线人数,其实握手占的是“临时席位”
运维在看监控时常问:在线人数才几万,怎么连接数满了?因为在线人数是活跃业务连接,而握手阶段的连接属于“临时席位”,服务端维护着一个半连接队列,握手未完成时连接挂在队列里,不参与业务处理,队列长度由内核参数限制,超出后就丢包,大促时临时席位被占满,新连接进不来,表现就是用户转圈、超时、页面打不开。
SSL握手性能对比:HTTP/2、TLS1.3与会话复用
HTTP/2与TLS1.3谁更省连接数
HTTP/2的多路复用能让一个连接同时跑多个请求,显著降低物理连接数量,但TLS握手本身还是要走,TLS 1.3把握手从两次往返减到一次,对新建连接时延友好,下表列一下常见组合的表现:
| 协议组合 | 握手往返次数 | 连接复用能力 | 大促典型表现 |
|---|---|---|---|
| HTTP/1.1 + TLS 1.2 | TCP 1次 + TLS 2次 | 低,每个请求排队 | 连接数迅速打满 |
| HTTP/2 + TLS 1.2 | TCP 1次 + TLS 2次 | 高,一个连接并发多请求 | 总连接少,但握手CPU高 |
| HTTP/2 + TLS 1.3 | TCP 1次 + TLS 1次 | 高,且握手快 | 综合表现最好 |
| HTTP/3 + QUIC | 0-RTT | 高 | 需客户端支持,普及度有限 |
大促新连接比例高,即使用了TLS 1.3,重复握手的绝对数量也不少,关键还是通过复用机制把握手的次数压下去。
会话复用机制:把握手的“记忆”存起来
SSL握手可以复用之前协商好的会话密钥,服务端开启会话缓存后,客户端第二次连接可以跳过重新交换证书的过程,nginx里配置ssl_session_cache和ssl_session_timeout就能生效。
但大促场景有个现实问题:访客来自各种渠道,很多是首次访问,没有缓存的会话凭证,移动网络下IP经常变化,也会降低会话命中率,行业共识认为,复用机制能缓解压力,但不能完全依赖它,如果服务端没配置共享会话缓存,多台服务器轮询时,用户每次被分发到不同机器,复用就失效了。
硬件加速与软件卸载的取舍
SSL握手主要的CPU开销是非对称加密,硬件加速卡或专用SSL芯片能把握手性能提升好几倍,但价格不低,大促临时租用硬件方案不容易买到,而且硬件卡驱动、负载均衡配合都是额外工作。
更常见的做法是软件层用异步OpenSSL引擎配合多核优化,nginx 1.15+支持ssl_async,让加密操作交给异步线程池处理,避免阻塞worker进程,这个方案对已有服务器零成本,适合长期运行,如果你用云厂商的负载均衡,直接在LB上挂证书做TLS终止,后端用HTTP回源,握手压力就全部转移到LB了。
SSL握手慢怎么优化:从时间、价格到地域选型
减少握手次数的实操路径
- 开启HTTP/2,让同一客户端只建立少量连接。
- 开启TLS会话缓存,并设置合适的过期时间,nginx配置参考:
ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_protocols TLSv1.2 TLSv1.3; ssl_early_data on; # 如需TLS1.3 0-RTT,可选
- 在负载均衡层终止TLS,后端走内网HTTP短连接,把握手集中到入口节点。
- 静态资源和图片走CDN,源站只处理动态请求。
- 对内部API启用连接池,避免每次请求都重建连接。
调整服务器参数与负载均衡配置
内核参数决定半连接队列大小,常用调整:
sysctl -w net.core.somaxconn=65535 sysctl -w net.ipv4.tcp_max_syn_backlog=65535
nginx需要把listen指令的backlog调大:
listen 443 ssl backlog=65535;
负载均衡器的健康检查也会消耗连接,如果后端每3秒扫一次,几百台后端被扫描时,本身就造成了不必要的握手,把健康检查改成每10秒一次,能腾出不少连接资源,这些操作在大促前一天做都来得及,但一定要压测后上线。
地域节点选择:别让握手绕远路
用户离服务器越远,握手往返时延越高,一个华东用户访问上海服务器SSL握手延迟可能只有20毫秒,但如果源站放在贵州,一次握手两次RTT加上网络丢包,延迟就变成200毫秒,大促用户分布全国,最好把TLS终止节点放到用户附近,也就是CDN边缘节点,边缘节点完成握手后,通过专线回源,源站只处理干净的HTTP请求,CDN的SSL握手费用一般按请求数计费,大促期间流量大,费用会明显上涨,但比源站宕机损失小得多。
SSL握手失败怎么办:三步定位与应急
第一步抓包确认瓶颈在客户端还是服务端
在源站上执行tcpdump -i eth0 port 443 -w ssl.pcap,客户端访问一次,然后看ClientHello发出的时间和ServerHello返回的时间,如果ServerHello很快就发出来,说明问题不在服务端能力,而在网络丢包或客户端设备,如果ServerHello迟迟不响应,再检查服务端CPU和连接队列。
第二步检查连接队列与证书链
执行ss -lnt | grep 443,观察Send-Q数值,如果很大说明backlog已经堆满,接着看证书链:证书链太长或OCSP装订没开启,会额外消耗往返,开启OCSP stapling后,服务端把证书状态一起发给客户端,省掉一次查询,检查方法:
openssl s_client -connect 域名:443 -status
输出里能看到OCSP响应是否被返回。
第三步大促前压测与预案
最稳妥的方法是提前压测,用wrk带TLS跑一遍:
wrk -t 8 -c 2000 -d 60s https://你的域名/
观察连接数和握手耗时,如果压测没问题但大促当天崩了,多半是真实用户的网络环境和压测机不同,需要加入多地域、多网络运营商、移动端模拟的混合压测。
预案要明确:当握手队列接近上限时,先扩容LB和CDN带宽,再降低非核心域的TLS安全性或回源优先级,最后启动限流保护源站,顺序不能反,否则源站先倒下。
大促握手开销不是拦路虎,关键在于把握手的“临时状态”从源站剥离、把复用机制打开,下次大促前,先压测一下你的握手链路。
大促SSL握手连接数消耗与优化:三个常见问题
大促时SSL握手连接数消耗太大,先升级服务器还是先上CDN?
多数情况下先上CDN,CDN边缘节点把TLS握手吃掉,源站连接数压力直接下降,升级服务器只是提高单机队列上限,如果入口流量依旧集中在一个IP,还是会被握手打穿,CDN还能顺便扛DDOS和静态请求,大促期间扩容也更灵活,源站如果只对回源IP放开访问,握手开销几乎为零。
本地压测握手很快,大促当天却大量超时,差别在哪?
压测机通常和服务端在同一机房或内网,网络质量好,真实用户走公网,跨运营商、跨地域,TCP重传率在晚高峰明显上升,大促用户从分享链接进入,很多是冷启动,没有会话缓存,握手必须走完整流程,另外一个关键点是压测时连接数平缓增加,而大促是瞬时涌进来,内核backlog来不及消化,压测应该用分布式负载机模拟不同地域的真实网络条件,并把并发模型改成阶梯式突增。
小成本商家怎么做SSL握手优化?
最直接的办法是用免费证书配合CDN的TLS终止,源站只开放给CDN回源IP,回源走HTTP或内网,源站本身开启TLS会话缓存和HTTP/2,调大backlog,设置合理的keepalive超时,这些操作不花钱,nginx配置改完重启即可,如果是自建服务器跑本地业务,建议把TLS证书丢给内网网关或负载均衡,让网关统一处理,成本有限时,优先把握手逻辑放在最小入口。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/773009.html


