同事劉陽使用dubbo服務器中配置mina做爲網絡傳輸層,發現大併發狀況下,解碼發生以下異常java
014-12-01 18:00:44,652 [DubboServerHandler-10.1.19.13:20880-thread-164] WARN alibaba.dubbo.remoting.exchange.codec.ExchangeCodec (ExchangeCodec.java:596) - [DUBBO] Fail to encode response: Response [id=8119, version=2.0.0, status=40, event=false, error=Fail to decode request due to: RpcInvocation [methodName=null, parameterTypes=null, arguments=null, attachments={input=242}, headers=null], result=null], send bad_response info instead, cause: null, dubbo version: 2.5.5, current host: 127.0.0.1
java.lang.NullPointerException
at com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec.encodeResponseData(DubboCodec.java:301)
at com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec.encodeResponse(ExchangeCodec.java:560)
at com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec.encode(ExchangeCodec.java:104)
at com.alibaba.dubbo.rpc.protocol.dubbo.DubboCountCodec.encode(DubboCountCodec.java:39)
at com.alibaba.dubbo.remoting.transport.mina.MinaCodecAdapter$InternalEncoder.encode(MinaCodecAdapter.java:79)
at org.apache.mina.filter.codec.ProtocolCodecFilter.filterWrite(ProtocolCodecFilter.java:214)
at org.apache.mina.common.support.AbstractIoFilterChain.callPreviousFilterWrite(AbstractIoFilterChain.java:361)
at org.apache.mina.common.support.AbstractIoFilterChain.access$1300(AbstractIoFilterChain.java:53)
at org.apache.mina.common.support.AbstractIoFilterChain$EntryImpl$1.filterWrite(AbstractIoFilterChain.java:659)
at org.apache.mina.common.support.AbstractIoFilterChain$TailFilter.filterWrite(AbstractIoFilterChain.java:587)
at org.apache.mina.common.support.AbstractIoFilterChain.callPreviousFilterWrite(AbstractIoFilterChain.java:361)
at org.apache.mina.common.support.AbstractIoFilterChain.fireFilterWrite(AbstractIoFilterChain.java:355)
at org.apache.mina.transport.socket.nio.SocketSessionImpl.write0(SocketSessionImpl.java:166)
at org.apache.mina.common.support.BaseIoSession.write(BaseIoSession.java:177)
at org.apache.mina.common.support.BaseIoSession.write(BaseIoSession.java:168)
at com.alibaba.dubbo.remoting.transport.mina.MinaChannel.send(MinaChannel.java:95)
at com.alibaba.dubbo.remoting.transport.AbstractPeer.send(AbstractPeer.java:51)
at
web
通過對比netty3和netty4做爲傳輸層,卻都沒有發現相似的問題。算法
首先排除不是mina自己的問題,mina也沒有爆出有這個問題,初步判斷dubbo在使用mina時機制有問題apache
通過對比發現緩存
1.netty是爲每個channel分配了一個NettyCodecAdapter, mina確實在服務器監聽前配置了MinaCodecAdapter安全
2.也就是說,netty的每個獨立的通道的Codec(encoder/decoder)是通道安全的服務器
3.mina的全部通道是共享相同的codec(encoder/decoder)的,所以,解碼器中的實例數據時非channel安全的
網絡
所以解碼器中與netty相同的解碼器的緩衝數據算法在併發狀況下將會產生數據覆蓋問題。session
4.解決方案
併發
1.配置acceptor的監聽器
codecAdapter = new MinaCodecAdapter(getCodec(), getUrl(), this);
acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(codecAdapter));
acceptor.addListener(new IoServiceListener(){
@Override
public void serviceActivated(IoService service,
SocketAddress serviceAddress, IoHandler handler,
IoServiceConfig config) {
}
@Override
public void serviceDeactivated(IoService service,
SocketAddress serviceAddress, IoHandler handler,
IoServiceConfig config) {
}
@Override
public void sessionCreated(IoSession session) {
codecAdapter.sessionCreated(session);
}
@Override
public void sessionDestroyed(IoSession session) {
codecAdapter.sessionDestroyed(session);
}
});
2.監聽session的create和destroy事件,傳遞到decoder中,decoder中,經過session和buffer的鍵值對保存對不一樣通道的數據的緩存,
private Map<IoSession, ChannelBuffer> buffers = new ConcurrentHashMap<IoSession, ChannelBuffer>();
// ChannelBuffers.EMPTY_BUFFER;
public void sessionCreated(IoSession session) {
buffers.put(session, ChannelBuffers.EMPTY_BUFFER);
}
public void sessionDestroyed(IoSession session) {
buffers.remove(session);
}
3.解碼時經過session得到當前channel的數據
ChannelBuffer buffer = buffers.get(session);
if(buffer == null) return;
通過測試,問題得以解決