一台 4 核 8G 的 CentOS 7.9 上 nginx 挂了二十个站点,每个站点证书都带三条中间证书,nginx 的 SSL_do_handshake 耗时监控显示 P99 到 800ms。

一开始怀疑是「ssl_buffer_size 保持默认的 16K,小响应也被塞进大包」,查了一圈,方向不对。

原因不止一个,得分开看。每个域名各自加载证书链,握手时要把整条链都发出去,包大了 RTT 就多。少数情况下是没开 session 缓存,每次新连接都要完整握手。

不容易想到的是,证书里 SHA1 签名或密钥长度偏大,验签时间长。

量一次握手的实际耗时

curl -s -o /dev/null -w 'connect=%{time_connect} tls=%{time_appconnect}\n' https://a.example.com/

然后开启 session 缓存与 ticket

printf 'ssl_session_cache shared:SSL:50m;\nssl_session_timeout 1d;\nssl_session_tickets on;\n' > /etc/nginx/conf.d/ssl.conf && nginx -t && nginx -s reload

接着把发送缓冲区压小

printf 'ssl_buffer_size 4k;\n' >> /etc/nginx/conf.d/ssl.conf && nginx -s reload

复测并确认会话复用率

openssl s_client -connect a.example.com:443 -reconnect </dev/null 2>&1 | grep -i 'Reused'

改之前我把相关日志按时间截了一段存着,事后对时间线用得上。

改完先别撤,确认这几项。time_appconnect 从 800ms 降到 200ms 以内,二次连接的 Reused 显示为 True,页面首屏时间同步下降。

补一句。证书链只留必要的中间证书,多一条就多一个包;HTTP/2 能省掉多域名并发握手的开销,但证书本身该瘦身还是要瘦身。报错文案容易误导,真正的原因往往在相邻那一层的日志里。

作者 9823189