bigywater.tech
Java · 359

SSLSessionContext:管理 TLS 会话缓存

调了会话缓存大小与超时,线上却没有任何变化?这篇用本机的 TLS 服务端把原因找出来:JDK 默认用会话票据,服务端 SSLSessionContext 里根本没有缓存。

0 个
默认设置下服务端 SSLSessionContext.getIds() 里的会话数(会话票据让服务端不缓存)
6 / 6 vs 0 / 6
服务端缓存设为 3、6 个客户端按序重连:默认设置(全部恢复)与关闭票据扩展后(全部完整握手)
0.88 vs 6.10 ms
TLS 1.2 恢复与完整握手的握手时间中位数(RSA-3072,本机回环)

默认值与设置边界

实测 · 本机执行输出
  [defaults]
    服务端 SSLSessionContext 默认:getSessionCacheSize = 20480(0 表示不限制),getSessionTimeout = 86400 秒;客户端:20480 / 86400 秒
    类 sun.security.ssl.SSLSessionContextImpl;getIds() 初始为空? true
    SSLContext.getDefault() 的服务端 / 客户端上下文是同一个对象吗? false;两个不同 SSLContext 的服务端上下文是同一个吗? false
    setSessionTimeout(0) → 之后 getSessionTimeout = 0
    setSessionTimeout(1) → 之后 getSessionTimeout = 1
    setSessionTimeout(7200) → 之后 getSessionTimeout = 7200
    setSessionTimeout(-1) → IllegalArgumentException: null
    setSessionCacheSize(0) → getSessionCacheSize = 0
    setSessionCacheSize(1) → getSessionCacheSize = 1
    setSessionCacheSize(100) → getSessionCacheSize = 100
    setSessionCacheSize(-1) → IllegalArgumentException: null

默认设置下的恢复

实测 · 本机执行输出
  [resume]
    [TLSv1.2] 同一个客户端 SSLContext 连续连接 4 次:
        第 1 次:握手 67.7 ms;session id ea26adc48398…(32B);创建时间 89051;协议/套件 TLSv1.2/TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;服务端缓存里的 id [];客户端缓存里的 id [ea26adc48398…(32B)]
        第 2 次:握手 2.9 ms;session id ea26adc48398…(32B);创建时间 89051;协议/套件 TLSv1.2/TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;服务端缓存里的 id [];客户端缓存里的 id [ea26adc48398…(32B)]
        第 3 次:握手 2.2 ms;session id ea26adc48398…(32B);创建时间 89051;协议/套件 TLSv1.2/TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;服务端缓存里的 id [];客户端缓存里的 id [ea26adc48398…(32B)]
        第 4 次:握手 2.3 ms;session id ea26adc48398…(32B);创建时间 89051;协议/套件 TLSv1.2/TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;服务端缓存里的 id [];客户端缓存里的 id [ea26adc48398…(32B)]
        换一个新的客户端 SSLContext 再连:握手 34.6 ms;id a9311a84ebc5…(32B);服务端缓存里的 id 数 0
    [TLSv1.3] 同一个客户端 SSLContext 连续连接 4 次:
        第 1 次:握手 49.0 ms;session id b8654d259638…(32B);创建时间 89244;协议/套件 TLSv1.3/TLS_AES_256_GCM_SHA384;服务端缓存里的 id [];客户端缓存里的 id [62a8015bb8b9…(32B), b8654d259638…(32B)]
        第 2 次:握手 5.5 ms;session id 62a8015bb8b9…(32B);创建时间 89244;协议/套件 TLSv1.3/TLS_AES_256_GCM_SHA384;服务端缓存里的 id [];客户端缓存里的 id [b8654d259638…(32B), 9e9c4cb212c6…(32B)]
        第 3 次:握手 3.8 ms;session id 9e9c4cb212c6…(32B);创建时间 89244;协议/套件 TLSv1.3/TLS_AES_256_GCM_SHA384;服务端缓存里的 id [];客户端缓存里的 id [06102b87a936…(32B), b8654d259638…(32B)]
        第 4 次:握手 3.3 ms;session id 06102b87a936…(32B);创建时间 89244;协议/套件 TLSv1.3/TLS_AES_256_GCM_SHA384;服务端缓存里的 id [];客户端缓存里的 id [9660083804b4…(32B), b8654d259638…(32B)]
        换一个新的客户端 SSLContext 再连:握手 20.4 ms;id fbe1951876d8…(32B);服务端缓存里的 id 数 0

关闭会话票据扩展后

实测 · 本机执行输出
  [resume](JVM 参数 -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=false)
    [TLSv1.2] 同一个客户端 SSLContext 连续连接 4 次:
        第 1 次:握手 65.5 ms;session id 678721dad7ce…(32B);创建时间 89596;协议/套件 TLSv1.2/TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;服务端缓存里的 id [678721dad7ce…(32B)];客户端缓存里的 id [678721dad7ce…(32B)]
        第 2 次:握手 2.0 ms;session id 678721dad7ce…(32B);创建时间 89596;协议/套件 TLSv1.2/TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;服务端缓存里的 id [678721dad7ce…(32B)];客户端缓存里的 id [678721dad7ce…(32B)]
        第 3 次:握手 1.6 ms;session id 678721dad7ce…(32B);创建时间 89596;协议/套件 TLSv1.2/TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;服务端缓存里的 id [678721dad7ce…(32B)];客户端缓存里的 id [678721dad7ce…(32B)]
        第 4 次:握手 1.6 ms;session id 678721dad7ce…(32B);创建时间 89596;协议/套件 TLSv1.2/TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;服务端缓存里的 id [678721dad7ce…(32B)];客户端缓存里的 id [678721dad7ce…(32B)]
        换一个新的客户端 SSLContext 再连:握手 33.3 ms;id 709c79a35e5c…(32B);服务端缓存里的 id 数 2
    [TLSv1.3] 同一个客户端 SSLContext 连续连接 4 次:
        第 1 次:握手 51.9 ms;session id 0be7cc4ecd32…(32B);创建时间 89791;协议/套件 TLSv1.3/TLS_AES_256_GCM_SHA384;服务端缓存里的 id [760fe18d257d…(32B)];客户端缓存里的 id [d9bdd7a82781…(32B), 0be7cc4ecd32…(32B)]
        第 2 次:握手 5.8 ms;session id d9bdd7a82781…(32B);创建时间 89791;协议/套件 TLSv1.3/TLS_AES_256_GCM_SHA384;服务端缓存里的 id [3fb47c80e5ec…(32B)];客户端缓存里的 id [d818d80da18a…(32B), 0be7cc4ecd32…(32B)]
        第 3 次:握手 3.4 ms;session id d818d80da18a…(32B);创建时间 89791;协议/套件 TLSv1.3/TLS_AES_256_GCM_SHA384;服务端缓存里的 id [6d17a796cb52…(32B)];客户端缓存里的 id [34598e3436f7…(32B), 0be7cc4ecd32…(32B)]
        第 4 次:握手 3.5 ms;session id 34598e3436f7…(32B);创建时间 89791;协议/套件 TLSv1.3/TLS_AES_256_GCM_SHA384;服务端缓存里的 id [0c984de7475d…(32B)];客户端缓存里的 id [7f51f1b6128b…(32B), 0be7cc4ecd32…(32B)]
        换一个新的客户端 SSLContext 再连:握手 20.5 ms;id a6ed0a46b9dd…(32B);服务端缓存里的 id 数 2

会话超时

实测 · 本机执行输出
  [timeout]
    服务端 sessionTimeout = 2 s(TLS 1.2):第 1 次 id 671938108271…(32B) 创建 90137;立即第 2 次 id 671938108271…(32B)(同一个会话:true)
    睡 3 秒之后服务端缓存里的 id:[](getIds 会过滤掉已过期的?)
    再连:id 671938108271…(32B);与第 1 次相同? true;握手 7.6 ms;服务端缓存里的 id []
    客户端 session 的 isValid = true;getCreationTime 与 getLastAccessedTime 的差 0 ms
  [timeout](JVM 参数 -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=false)
    服务端 sessionTimeout = 2 s(TLS 1.2):第 1 次 id 72629d06e8aa…(32B) 创建 94008;立即第 2 次 id 72629d06e8aa…(32B)(同一个会话:true)
    睡 3 秒之后服务端缓存里的 id:[](getIds 会过滤掉已过期的?)
    再连:id 38933ddb1ec0…(32B);与第 1 次相同? false;握手 48.1 ms;服务端缓存里的 id [38933ddb1ec0…(32B)]
    客户端 session 的 isValid = true;getCreationTime 与 getLastAccessedTime 的差 0 ms

缓存大小

实测 · 本机执行输出
  [cache]
    第 1 个不同的客户端连上后,服务端缓存 id 数 = 0 []
    第 2 个不同的客户端连上后,服务端缓存 id 数 = 0 []
    第 3 个不同的客户端连上后,服务端缓存 id 数 = 0 []
    第 4 个不同的客户端连上后,服务端缓存 id 数 = 0 []
    第 5 个不同的客户端连上后,服务端缓存 id 数 = 0 []
    第 6 个不同的客户端连上后,服务端缓存 id 数 = 0 []
        客户端 1 再连一次:id 相同(恢复);握手 1.8 ms
        客户端 2 再连一次:id 相同(恢复);握手 2.1 ms
        客户端 3 再连一次:id 相同(恢复);握手 1.6 ms
        客户端 4 再连一次:id 相同(恢复);握手 1.5 ms
        客户端 5 再连一次:id 相同(恢复);握手 1.5 ms
        客户端 6 再连一次:id 相同(恢复);握手 1.7 ms
    6 个客户端各自再连一次,恢复成功 6 个
    setSessionCacheSize(0)(不限制):8 个不同客户端之后服务端缓存 id 数 = 0
  [cache](JVM 参数 -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=false)
    第 1 个不同的客户端连上后,服务端缓存 id 数 = 1 [c693e670889b…(32B)]
    第 2 个不同的客户端连上后,服务端缓存 id 数 = 2 [77cf4bff2cac…(32B), c693e670889b…(32B)]
    第 3 个不同的客户端连上后,服务端缓存 id 数 = 3 [074fcf1f3e1e…(32B), 77cf4bff2cac…(32B), c693e670889b…(32B)]
    第 4 个不同的客户端连上后,服务端缓存 id 数 = 3 [074fcf1f3e1e…(32B), 77cf4bff2cac…(32B), 35c15e08399b…(32B)]
    第 5 个不同的客户端连上后,服务端缓存 id 数 = 3 [074fcf1f3e1e…(32B), 35c15e08399b…(32B), fa4629f2e91a…(32B)]
    第 6 个不同的客户端连上后,服务端缓存 id 数 = 3 [ffb7057cef0b…(32B), 35c15e08399b…(32B), fa4629f2e91a…(32B)]
        客户端 1 再连一次:id 不同(完整握手);握手 20.3 ms
        客户端 2 再连一次:id 不同(完整握手);握手 20.1 ms
        客户端 3 再连一次:id 不同(完整握手);握手 19.8 ms
        客户端 4 再连一次:id 不同(完整握手);握手 19.4 ms
        客户端 5 再连一次:id 不同(完整握手);握手 13.1 ms
        客户端 6 再连一次:id 不同(完整握手);握手 9.0 ms
    6 个客户端各自再连一次,恢复成功 0 个
    setSessionCacheSize(0)(不限制):8 个不同客户端之后服务端缓存 id 数 = 8

invalidate()

实测 · 本机执行输出
  [invalidate]
    [TLSv1.2] 第 1、2 次 握手 71.1 / 3.2 ms;客户端缓存 id [9e635c921971…(32B)]
        invalidate() 之后 isValid = false;客户端缓存 id [];服务端缓存 id []
        invalidate 之后再连:握手 26.4 ms;id 44104844bb63…(32B);与 invalidate 前相同? false
    [TLSv1.3] 第 1、2 次 握手 52.8 / 5.4 ms;客户端缓存 id [e466ab6cf22e…(32B), 7ea0ca482a33…(32B)]
        invalidate() 之后 isValid = false;客户端缓存 id [7ea0ca482a33…(32B)];服务端缓存 id []
        invalidate 之后再连:握手 3.7 ms;id ee18657eb659…(32B);与 invalidate 前相同? false

恢复与完整握手的耗时

实测 · 本机执行输出
  [cost]
    [TLSv1.2] RSA-3072 证书,本机回环;60 次同一 SSLContext(恢复)中位数 0.88 ms,P90 1.04 ms;60 次每次新 SSLContext(完整握手,含 SSLContext 初始化之外的握手本身)中位数 6.10 ms,P90 19.14 ms
    [TLSv1.3] RSA-3072 证书,本机回环;60 次同一 SSLContext(恢复)中位数 1.29 ms,P90 1.43 ms;60 次每次新 SSLContext(完整握手,含 SSLContext 初始化之外的握手本身)中位数 4.09 ms,P90 4.31 ms
  [cost](JVM 参数 -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=false)
    [TLSv1.2] RSA-3072 证书,本机回环;60 次同一 SSLContext(恢复)中位数 0.74 ms,P90 0.95 ms;60 次每次新 SSLContext(完整握手,含 SSLContext 初始化之外的握手本身)中位数 6.14 ms,P90 19.34 ms
    [TLSv1.3] RSA-3072 证书,本机回环;60 次同一 SSLContext(恢复)中位数 1.22 ms,P90 1.34 ms;60 次每次新 SSLContext(完整握手,含 SSLContext 初始化之外的握手本身)中位数 4.10 ms,P90 4.31 ms

分步演示

SSLSessionContext
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12,RSA-3072 自签名证书(keytool),本机回环的 SSLServerSocket / SSLSocket;第一次握手含 JIT 预热,耗时数字只用于相对比较
    • 没有验证:真实网络(往返延迟)下的恢复收益、多个服务进程之间共享票据密钥(jdk.tls 的票据密钥在进程内,重启或多实例间不通用,没有测试)、PSK 票据的 jdk.tls.server.sessionTicketTimeout 一类的参数、客户端证书认证(mTLS)的恢复、SSLEngine(NIO)路径、jdk.tls.server.enableSessionTicketExtension 对 TLS 1.3 的作用(这里 TLS 1.3 的行为在两种设置下看起来相同,原因没有查明)、会话缓存淘汰的具体策略(只观察到与淘汰最久未用相符的结果)
    京ICP备2026067502号-1京公网安备11010502063487号