编码器的encode方法会并发执行吗?我观察到编码方法可能是由不同线程并发的。管道定义为:
Channels.pipeline(
idleHandler,
new AmfDecoder<GameEvent>(GameEvent.class),
new AmfEncoder<GameEvent>(),
concurrencyHandler,
new WebHandler());
编码器:
public class AmfEncoder<T extends IAmfEvent> extends OneToOneEncoder{
private final SerializationContext serializationContext = new SerializationContext();
private final Amf3Output amfout = new Amf3Output(serializationContext);
@Override
protected Object encode(ChannelHandlerContext arg0, Channel arg1,
Object arg2) throws Exception {
T e = (T)arg2;
ByteArrayOutputStream byteoutStreamSize = new ByteArrayOutputStream();
amfout.setOutputStream(byteoutStreamSize);
amfout.writeObject(e.getBody());
// byteoutStreamSize has small probability become empty at here, in debug mode I can sure e.getBody() has data
// I thought byteoutStreamSize might be empty by another thread call "amfout.flush()" or "amfout.reset()"
amfout.flush();
//...
amfout.reset();
}
}
Channel.write的调用不仅是属于netty的工作线程的线程或者Exeutionhandler中的线程。我自己创建的线程池会调用Channel.write()。 当我将amfout和serializationContext的2个变量移入encode()函数作为局部变量后,问题就消失了。
Doc说ChannelPipeline是线程安全的,我读netty 3.4.5发现“add”,“remove”...操作被锁定,但sendDownstream和sendUpstream没有锁定。因此,如果存在不属于工作线程池或ExecutionHandler线程池的线程,并且所有这些线程都调用Channel.write(),则解码器和编码器中将出现并发问题
最佳答案
Channel 管道是线程安全的,但这里的问题是下游事件和上游事件的事件执行模型不同:
默认情况下使用(多个)用户线程执行下游处理程序。
默认情况下,下游处理程序不是线程安全的,因为它们可以由多个用户线程以任意顺序执行(通常
DownstreamEvents
是轻量级的,因此它们的处理程序不会在实例变量中维护状态)。查看 Netty 代码库中的OneToOneEncoder
实现。它们都没有维护状态。上游处理程序默认使用单个线程执行,或者使用多个线程一一执行(如果使用执行处理程序)。
由于单线程事件执行,上游处理程序是线程安全的(即使它们可以保持可变状态)。
因此,有人可能会错误地认为下游处理程序与上游处理程序一样线程安全。
正如您所说,解决方案是将实例变量移动到本地范围 如果不需要状态。否则,使下游处理方法线程安全。
关于java - Netty的并发编码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10699688/