如何在 Netty 中的 ssl 握手之前优雅地关闭频道?
Posted
技术标签:
【中文标题】如何在 Netty 中的 ssl 握手之前优雅地关闭频道?【英文标题】:How to close a channel gracefully before ssl handshake in Netty? 【发布时间】:2018-03-08 05:42:38 【问题描述】:我在我的应用程序中添加了一个限制功能,当传入的请求率超过阈值时,它需要在 SSL 握手之前关闭一个通道以减少 CPU 使用率。现在我使用带有服务器模式的 Netty SslHandler
进行握手。我目前的实现是在SslHandler
之前添加ChannelInboundHandlerAdapter
并重写channelActive
方法:
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception
if (!limiter.tryAcquire())
ctx.close();
return;
ctx.pipeline().remove(this);
ctx.fireChannelActive();
通过这种方式,通道可以在变为活动状态时关闭。但是,我会收到握手失败的SslHandshakeCompletionEvent
。我阅读了 Netty SslHandler
的源代码。当 channelInactive 事件被触发时,它将在channelInactive
方法中设置握手失败。所以我想知道是否有更好的方法来关闭通道而不触发握手失败事件,因为当我限制关闭通道时握手过程还没有开始。
【问题讨论】:
【参考方案1】:你在错误的地方解决问题。这个想法不是关闭被接受的通道太快,而是不要太快地接受它们,这将在接受处理程序中完成,而不是在通道处理程序中完成。
【讨论】:
你的意思是修改ServerSocketChannel
?我有一个将 InboundHandler 添加到NioserverSocketChannel
的解决方案,因此我可以在传入通道注册和激活之前关闭它。但是,这意味着我必须使用channel.unsafe().closeForcibly()
。如果我使用普通的channel.close()
方法,Netty 会抛出通道未注册异常。但是根据 Netty 文档Unsafe,这似乎是内部 API,我们不应该使用它吗?
我的意思是,当您拥有最大连接数时,要么停止接受连接,要么取消注册对 OP_ACCEPT 的兴趣,但是 Netty 会这样做。你不想关闭连接:已经太晚了;你也不需要关闭ServerSocketChannel
。基本上就是不要打电话给accept()
。
我一开始也考虑过这个方案。但是,我正在添加一个速率限制功能,这意味着我需要每秒接受一些连接请求并拒绝其余的连接请求。如果只是让Netty
不接受新连接,那不是速率限制的目标。当然,我可以每秒修改OP_ACCEPT
来控制Netty
每秒接受的连接数,但是这样是不是一个好的解决方案?
所以你需要在每一秒内接受这么多的连接,然后在那一秒的剩余时间内停止接受它们。这完全符合您的目标。不清楚你为什么不这么认为,
我只是想知道以高频率模式修改OP_ACCEPT
是否是一个好习惯(每秒修改两次),它会导致Netty
的高开销吗?【参考方案2】:
您的方法几乎是正确的,但是,覆盖 channelRegistered
方法而不是 channelActive
是正确的。
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception
if (isGlobalLimitReached())
ctx.close();
else
ctx.pipeline().remove(this);
ctx.pipeline().addFirst(sslCtx.newHandler(ctx.alloc()));
ctx.fireChannelRegistered();
此外,您需要在流水线的开头动态添加SslHandler
,并且仅在满足您的条件时(未达到限制)。
在这种情况下,您不会收到SslHandshakeCompletionEvent
。当SslHandler
添加到管道和
channel.isActive()
return true(你的情况在上面的代码中)。
channelRegistered
是您可以拒绝/关闭连接的最近点。
【讨论】:
这让我很困惑。我使用调试模式来尝试找出SslHandler
何时开始握手。似乎具有客户端模式的处理程序在通道处于活动状态时开始握手。如果SslHandler
处于服务器模式,它会在通道读取时开始握手(SslHandler
源代码中有一个readDuringHandshake
标志)。最让我困惑的是,即使它在通道处于活动状态时开始握手,我的处理程序也是在SslHandler
之前添加的,而我的channelInactive
方法应该在握手之前运行并关闭通道。为什么它仍然给SslHandshakeCompletionEvent
?
或者就像你说的,当SslHandler
被添加到管道时,握手就开始了,但它似乎立即开始,并且在通道变得活跃之前。
更新:修改方法为channelRegistered
并测试,Netty
还是给SslHandshakeCompletionEvent
。
@JianZhu 我的错,我没有查看 channel.isActive() 方法。请检查我上面编辑的答案。
非常感谢。我尝试根据条件动态地将SslHandler
添加到管道中。如果条件匹配,我加SslHandler
,否则不加。该解决方案适用于我的用例。只是仍然想知道为什么第一个解决方案不起作用。正如我在更新评论中所说,我尝试添加一个处理程序,该处理程序像您的答案中的代码一样重写channelRegistered
方法,并将其添加到 SslHandler 之前。我仍然会收到SslHandshakeCompletionEvent
。这是否意味着握手已经开始,甚至在channelResigtered
事件被触发之前?以上是关于如何在 Netty 中的 ssl 握手之前优雅地关闭频道?的主要内容,如果未能解决你的问题,请参考以下文章
使用 2-way SSL Handskake(客户端和服务器证书)设置 Netty