性能优化:SSL 会话复用与 OCSP Stapling
说到 SSL/TLS 的握手延迟,我估计不少同学都吃过这个亏。我自己在早期做金融类 App 时,就遇到过用户反馈「加载太慢」的问题。查了半天,发现罪魁祸首就是每次请求都要重新握手。嗯,今天我们就来聊聊怎么解决这个问题。
SSL 会话复用:别每次都重新认识
先想想看,你和朋友见面,第一次需要自我介绍、交换名片。第二次见面呢?你肯定说「嗨,又见面了」。SSL 会话复用就是这个道理。
说白了,就是客户端和服务器在第一次完整握手后,把会话信息缓存起来。下次再连接时,直接复用之前的会话参数,省掉一大半的握手步骤。
两种复用方式
| 方式 | 原理 | 延迟节省 |
|---|---|---|
| Session ID | 服务端生成唯一 ID,客户端保存 | 约 40-60% |
| Session Ticket | 服务端加密票据,客户端保存 | 约 50-70% |
我个人更推荐 Session Ticket。为什么?因为 Session ID 需要服务端维护缓存,分布式环境下很麻烦。我在项目中就踩过这个坑——多台服务器之间没同步 Session ID,导致用户频繁重新握手。
关键配置示例(Android OkHttp):
// 启用会话复用
OkHttpClient client = new OkHttpClient.Builder()
.connectionPool(new ConnectionPool(5, 5, TimeUnit.MINUTES))
.build();
// 自定义 SSLSocketFactory 时注意
// 一定要复用同一个 SSLContext 实例
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagers, new SecureRandom());
// 同一个 SSLContext 创建的 SSLSocketFactory 才能复用会话
这里有个细节:同一个 SSLContext 创建的 SSLSocketFactory 才能复用会话。我曾经见过有人每次请求都 new 一个 SSLContext,结果会话复用完全没生效。
OCSP Stapling:把证明提前准备好
证书吊销检查,说白了就是验证你的证书有没有被拉黑。传统做法是客户端自己去查,这又增加一次网络请求。OCSP Stapling 的思路很简单:服务端自己提前查好,握手时直接塞给客户端。
你想想看,这就像你去机场,安检人员提前帮你查好了黑名单,你直接过就行。省了排队时间。
实现要点
- 服务端需要支持 OCSP Stapling(Nginx 1.3+、Apache 2.3+)
- Android 端需要 API 21+ 才原生支持
- 低版本 Android 需要自己实现
避坑指南:我曾经在 Android 4.4 设备上遇到过 OCSP Stapling 不生效的问题。后来发现是系统没有实现对应的扩展。解决方案是手动校验 Stapling 响应。
// Android 端验证 OCSP Stapling 示例
SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
socket.startHandshake();
// 获取服务端返回的 OCSP 响应
// 注意:API 24+ 才支持直接获取
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
SSLParameters params = socket.getSSLParameters();
// 检查是否启用了 OCSP Stapling
// 实际项目中需要更完整的验证逻辑
}
实战中的性能数据
我在一个日活百万的 App 上做过测试,数据如下:
| 场景 | 握手耗时 | 提升比例 |
|---|---|---|
| 首次握手(无优化) | 约 350ms | - |
| 会话复用 | 约 120ms | 65% |
| 会话复用 + OCSP Stapling | 约 90ms | 74% |
嗯,数据很直观。但要注意,这些优化只在短连接场景下效果明显。如果是长连接(比如 WebSocket),握手的次数本来就少,优化空间有限。
注意:会话复用不是万能的。如果服务端证书更换了,或者会话超时了,还是要重新完整握手。另外,Session Ticket 的加密密钥要定期轮换,否则有安全风险。
整体优化流程
下面这张图是我自己总结的优化决策流程,你可以参考:
这个流程我用了好几年,基本覆盖了大部分场景。核心思路就是:能复用就复用,能提前查就提前查。
一些额外的建议
- 连接池大小要合理:不是越大越好。我一般设 5-10 个连接,超过这个数反而增加管理开销。
- 超时时间要匹配:会话超时和服务端配置要一致。我曾经遇到过客户端设了 10 分钟,服务端 5 分钟就过期了,导致复用失败。
- 低版本兼容:Android 4.x 设备上,OCSP Stapling 基本不可用。这时候要回退到传统验证方式。
总结一下:SSL 会话复用和 OCSP Stapling 是减少握手延迟最有效的两个手段。前者省掉重复握手,后者省掉证书验证的网络请求。两者配合使用,能把握手延迟降低 70% 以上。但要注意,这些优化不是银弹,需要根据实际场景来配置。
公众号:蓝海资料掘金营,微信deep3321