You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Quarkus:SSL握手时执行长IO任务如何避免阻塞事件循环?

解决Quarkus SSL握手阶段调用外部服务阻塞Vert.x事件循环的问题

你的核心问题是JDK X509TrustManager的同步API限制,导致握手阶段调用外部服务时阻塞Vert.x事件循环。以下是三种可行的解决方案,覆盖不同场景需求:

方案一:将外部服务调用移至Quarkus工作线程池

Quarkus提供了专门的工作线程池处理阻塞任务,我们可以把外部服务调用逻辑转移到工作线程中执行,避免占用Vert.x事件循环线程。

实现代码

class CustomTrustManager(
    private val manager: X509TrustManager,
    private val externalService: ExternalService,
    private val vertx: Vertx
) : X509TrustManager {

    override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
        // 先执行JDK默认的证书信任链验证
        manager.checkClientTrusted(chain, authType)
        
        val certificate = chain[0]
        // 将阻塞调用提交到工作线程池,同步等待结果
        val isValid = vertx.executeBlocking<Boolean>({ promise ->
            try {
                promise.complete(externalService.verifyCertificate(certificate))
            } catch (e: Exception) {
                promise.fail(e)
            }
        }, false) // false表示使用工作线程池而非事件循环
            .await()

        if (!isValid) {
            throw CertificateException("Certificate is unknown in external service")
        }
    }

    // 实现X509TrustManager的其他必填方法
    override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
        manager.checkServerTrusted(chain, authType)
    }

    override fun getAcceptedIssuers(): Array<X509Certificate> {
        return manager.getAcceptedIssuers()
    }
}

配置调整

在application.properties中调整工作线程池参数,适配并发握手需求:

quarkus.vertx.worker-pool-size=16
quarkus.vertx.max-worker-execution-time=5s

方案二:预缓存合法证书+异步刷新

如果外部服务的合法证书列表变更频率低,可以通过预缓存+后台异步刷新的方式,彻底消除握手阶段的阻塞调用。

1. 证书缓存服务

@ApplicationScoped
class CertificateCache(private val externalService: ExternalService, private val vertx: Vertx) {
    private val validCertFingerprints = ConcurrentHashMap<String, Unit>()

    @PostConstruct
    fun initCache() {
        refreshCache()
        // 每30分钟刷新一次缓存,可根据业务调整周期
        vertx.setPeriodic(30 * 60 * 1000) { refreshCache() }
    }

    private fun refreshCache() {
        vertx.executeBlocking<Void>({ promise ->
            try {
                val validCertificates = externalService.fetchAllValidCertificates()
                validCertFingerprints.clear()
                validCertificates.forEach { cert ->
                    // 用SHA-256指纹作为唯一标识
                    val fingerprint = cert.digest("SHA-256")
                        .joinToString(":") { "%02x".format(it) }
                    validCertFingerprints[fingerprint] = Unit
                }
                promise.complete()
            } catch (e: Exception) {
                promise.fail(e)
                // 刷新失败时保留旧缓存,避免服务中断
            }
        }, false)
    }

    fun isCertificateValid(cert: X509Certificate): Boolean {
        val fingerprint = cert.digest("SHA-256")
            .joinToString(":") { "%02x".format(it) }
        return validCertFingerprints.containsKey(fingerprint)
    }
}

2. 修改后的CustomTrustManager

class CustomTrustManager(
    private val manager: X509TrustManager,
    private val certificateCache: CertificateCache
) : X509TrustManager {

    override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
        manager.checkClientTrusted(chain, authType)
        val certificate = chain[0]
        
        if (!certificateCache.isCertificateValid(certificate)) {
            throw CertificateException("Certificate is unknown in external service")
        }
    }

    // 实现其他X509TrustManager方法...
}

优势

  • 握手阶段完全无阻塞,性能最优
  • 降低对外部服务的依赖风险,避免网络波动影响服务可用性

方案三:Vert.x SSL握手拦截器(进阶)

绕过X509TrustManager的同步限制,直接通过Vert.x的SslHandler钩子插入异步验证逻辑,在握手完成前完成证书校验。

实现代码

@ApplicationScoped
class VertxCustomizer(
    private val externalService: ExternalService,
    private val vertx: Vertx
) : HttpServerOptionsCustomizer {

    override fun customizeHttpsServer(options: HttpServerOptions) {
        val originalTrustOptions = options.trustOptions
        // 自定义SslHandler生成逻辑
        options.sslHandlerProvider = { sslContext ->
            val sslHandler = sslContext.newHandler(vertx)
            // 监听握手完成事件,插入异步验证
            sslHandler.handshakeFuture().onSuccess { sslSession ->
                val peerCerts = sslSession.peerCertificates
                if (peerCerts.isNotEmpty() && peerCerts[0] is X509Certificate) {
                    val cert = peerCerts[0] as X509Certificate
                    vertx.executeBlocking<Boolean>({ promise ->
                        try {
                            promise.complete(externalService.verifyCertificate(cert))
                        } catch (e: Exception) {
                            promise.fail(e)
                        }
                    }, false)
                        .onFailure {
                            // 验证失败直接关闭连接
                            sslHandler.close()
                        }
                }
            }
            sslHandler
        }
    }
}

注意事项

  • 需确保验证失败时及时关闭连接,防止非法客户端继续发送请求
  • 要处理握手后验证的时序问题,避免客户端在验证完成前发起业务请求

内容的提问来源于stack exchange,提问作者Jan Thomä

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 16:06:03