2012-04-18 54 views
0

我组合了netty的大部分例子HexdumpProxy(http://netty.io/docs/stable/xref/org/jboss/netty/example/proxy/)和SecureChat(http://netty.io/docs/ stable/xref/org/jboss/netty/example/securechat)组成一个支持SSL(和非SSL)的代理(到非SSL后端)。这似乎是一个好主意,被证明是很简单的,而且正是我目前所需要的。如何解决代理示例trafficlock和ssl handshakelock的死锁?

代理示例代码使用trafficlock锁作为解决发生围绕饱和频道和设定输入的可写和可读状态在2010年报道的竞争条件(http://markmail.org/message/x7jc6mqx6ripynqf)和传出频道。

现在,在我的例子中,在更高的负载下,这会导致死锁,因为SSL代码中的另一个锁“handshakelock”是交织在一起的。请参阅下面的分析器诊断输出。

我担心即使在阅读了原始讨论之后,我也不明白交通锁的问题足以找到解决这个僵局的简单方法。

(这是与网状3.2.6)

Java-level deadlock has been detected 

This means that some threads are blocked waiting to enter a synchronization block or 
waiting to reenter a synchronization block after an Object.wait() call, where each thread 
owns one monitor while trying to obtain another monitor already held by another thread. 

Deadlock: 


New I/O client worker #1-2 is waiting to lock [email protected] which is held by New I/O server worker #1-2 
New I/O server worker #1-2 is waiting to lock [email protected] which is held by New I/O client worker #1-2 




Thread stacks 


New I/O client worker #1-2 [BLOCKED; waiting to lock [email protected]] 
org.jboss.netty.handler.ssl.SslHandler.wrap(SslHandler.java:665) <== sync handshakelock 
org.jboss.netty.handler.ssl.SslHandler.handleDownstream(SslHandler.java:461) 
org.jboss.netty.channel.DefaultChannelPipeline.sendDownstream(DefaultChannelPipeline.java:591) 
org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendDownstream(DefaultChannelPipeline.java:776) 
org.jboss.netty.channel.Channels.write(Channels.java:632) 
org.jboss.netty.handler.codec.oneone.OneToOneEncoder.handleDownstream(OneToOneEncoder.java:70) 
org.jboss.netty.channel.DefaultChannelPipeline.sendDownstream(DefaultChannelPipeline.java:591) 
org.jboss.netty.channel.DefaultChannelPipeline.sendDownstream(DefaultChannelPipeline.java:582) 
org.jboss.netty.channel.Channels.write(Channels.java:611) 
org.jboss.netty.channel.Channels.write(Channels.java:578) 
org.jboss.netty.channel.AbstractChannel.write(AbstractChannel.java:251) 
com.activevideo.frontend.ProxyInboundHandler$OutboundHandler.messageReceived(ProxyInboundHandler.java:162) <== sync trafficlock 
org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:80) 
org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) 
org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:783) 
org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:302) 
org.jboss.netty.handler.codec.frame.FrameDecoder.unfoldAndFireMessageReceived(FrameDecoder.java:317) 
org.jboss.netty.handler.codec.frame.FrameDecoder.callDecode(FrameDecoder.java:299) 
org.jboss.netty.handler.codec.frame.FrameDecoder.messageReceived(FrameDecoder.java:216) 
org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:80) 
org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) 
org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:559) 
org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:274) 
org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:261) 
org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:351) 
org.jboss.netty.channel.socket.nio.NioWorker.processSelectedKeys(NioWorker.java:282) 
org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:202) 
org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108) 
org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:44) 
java.util.concurrent.ThreadPoolExecutor$Worker.runTask(unknown source) 
java.util.concurrent.ThreadPoolExecutor$Worker.run(unknown source) 
java.lang.Thread.run(unknown source) 


New I/O server worker #1-2 [BLOCKED; waiting to lock [email protected]] 
com.activevideo.frontend.ProxyInboundHandler.channelInterestChanged(ProxyInboundHandler.java:138) <== sync trafficlock 
org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:116) 
org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) 
org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:783) 
org.jboss.netty.channel.SimpleChannelUpstreamHandler.channelInterestChanged(SimpleChannelUpstreamHandler.java:192) 
org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:116) 
org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) 
org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:783) 
org.jboss.netty.channel.SimpleChannelUpstreamHandler.channelInterestChanged(SimpleChannelUpstreamHandler.java:192) 
org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:116) 
org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) 
org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:783) 
org.jboss.netty.channel.SimpleChannelUpstreamHandler.channelInterestChanged(SimpleChannelUpstreamHandler.java:192) 
org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:116) 
org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) 
org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:559) 
org.jboss.netty.channel.Channels.fireChannelInterestChanged(Channels.java:335) 
org.jboss.netty.channel.socket.nio.NioWorker.setInterestOps(NioWorker.java:728) 
org.jboss.netty.channel.socket.nio.NioServerSocketPipelineSink.handleAcceptedSocket(NioServerSocketPipelineSink.java:129) 
org.jboss.netty.channel.socket.nio.NioServerSocketPipelineSink.eventSunk(NioServerSocketPipelineSink.java:76) 
org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendDownstream(DefaultChannelPipeline.java:771) 
org.jboss.netty.handler.ssl.SslHandler.handleDownstream(SslHandler.java:430) 
org.jboss.netty.channel.DefaultChannelPipeline.sendDownstream(DefaultChannelPipeline.java:591) 
org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendDownstream(DefaultChannelPipeline.java:776) 
org.jboss.netty.handler.codec.oneone.OneToOneEncoder.handleDownstream(OneToOneEncoder.java:60) 
org.jboss.netty.channel.DefaultChannelPipeline.sendDownstream(DefaultChannelPipeline.java:591) 
org.jboss.netty.channel.DefaultChannelPipeline.sendDownstream(DefaultChannelPipeline.java:582) 
org.jboss.netty.channel.Channels.setInterestOps(Channels.java:652) 
org.jboss.netty.channel.AbstractChannel.setInterestOps(AbstractChannel.java:222) 
org.jboss.netty.channel.AbstractChannel.setReadable(AbstractChannel.java:244) 
com.activevideo.frontend.SSLProxyInboundHandler$BackendConnector$1.operationComplete(SSLProxyInboundHandler.java:92) 
org.jboss.netty.channel.DefaultChannelFuture.notifyListener(DefaultChannelFuture.java:381) 
org.jboss.netty.channel.DefaultChannelFuture.addListener(DefaultChannelFuture.java:148) 
com.activevideo.frontend.SSLProxyInboundHandler$BackendConnector.operationComplete(SSLProxyInboundHandler.java:87) 
org.jboss.netty.channel.DefaultChannelFuture.notifyListener(DefaultChannelFuture.java:381) 
org.jboss.netty.channel.DefaultChannelFuture.notifyListeners(DefaultChannelFuture.java:367) 
org.jboss.netty.channel.DefaultChannelFuture.setSuccess(DefaultChannelFuture.java:316) 
org.jboss.netty.handler.ssl.SslHandler.setHandshakeSuccess(SslHandler.java:1040) 
org.jboss.netty.handler.ssl.SslHandler.wrapNonAppData(SslHandler.java:838) 
org.jboss.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:907) <=== sync handshakelock 
org.jboss.netty.handler.ssl.SslHandler.decode(SslHandler.java:620) 
org.jboss.netty.handler.codec.frame.FrameDecoder.callDecode(FrameDecoder.java:282) 
org.jboss.netty.handler.codec.frame.FrameDecoder.messageReceived(FrameDecoder.java:214) 
org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:80) 
org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) 
org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:559) 
org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:274) 
org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:261) 
org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:351) 
org.jboss.netty.channel.socket.nio.NioWorker.processSelectedKeys(NioWorker.java:282) 
org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:202) 
org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108) 
org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:44) 
java.util.concurrent.ThreadPoolExecutor$Worker.runTask(unknown source) 
java.util.concurrent.ThreadPoolExecutor$Worker.run(unknown source) 
java.lang.Thread.run(unknown source) 
+0

向我们展示您的自定义处理程序 – 2012-04-18 05:56:36

+0

顺便说一句,3.2.6是相当古老的..你应该升级到3.4.0.Final – 2012-04-18 09:56:44

回答

1

好吧,我想我固定它。

代理示例中较早争用条件的解决方案不必要地包含对synchronized块中的write()调用。

原来的问题有这种(罕见)比赛即将离任的线程(TO)和即将到来的线程(TI)之间的条件:)inboundChannel.write((在的messageReceived)

  • TO:

    1. TO :inboundChannel.isWritable()返回(在的messageReceived)
    2. 在(1)被刷新发出接着的未决写假
    3. TI:inboundChannel.isWritable()返回真(在channelInterestChanged)
    4. TI:欧tboundChannel.setReadable(真)(在channelInterestChanged)
    5. TO:outboundChannel.setReadable(假)(在的messageReceived)

    在2010年的解决方案是围绕设置可读标志引入(用 'trafficlock')同步同时在inboundChannel和outboundChannel(以及在InterestChanged处理程序)的的messageReceived()处理,像这样:

    synchronized (trafficLock) { 
        outboundChannel.write(msg); 
        // If outboundChannel is saturated, do not read until notified in 
        // OutboundHandler.channelInterestChanged(). 
        if (!outboundChannel.isWritable()) { 
        e.getChannel().setReadable(false); 
        } 
    } 
    

    这确实解决了竞争条件,如可以防止步骤3,4和5干涉步骤2和步骤6.但是,将步骤1(写入())离开是安全的e同步块。

    对write()的调用导致了死锁,因为在SSLHandler中调用该函数时,它使用另一个锁handshakelock。

    所以..我把这个调用移到了两个位置的同步块之外的write()。僵局现在消失了。我建议“官方”代理示例相应地更改。