关于netty实现的channelHandler线程安全探究以及可能导致不线程安全的例子

首先,netty的处理模型是存在一组IO线程,去处理IO事件,如read,connect,write等等,对于服务端接收到的每个channel,都会将该channel映射到一条IO线程。当一个channel被建立之后,需要将其初始化,包含给他创建pipleline并填充channelhandler;给channel附以channelOptions和channelAttrs等,其中填充channelhandler可能会导致问题

如某个channelHandler是共享的,却被添加到多个channel上,而每个channel对应一个线程,必然会导致线程并发问题,如下列代码:

public final class Server {

    public static void main(String[] args) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            UnSafeChannelHandler handler=new UnSafeChannelHander()
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .childAttr(AttributeKey.newInstance("childAttr"), "childAttrValue")
                    .childHandler(new ChannelInitializer() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(handler);
                        }
                    });

            ChannelFuture f = b.bind(8888).sync();

            f.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

此时你需要自己实现channelHandler的线程安全性,如使用线程安全类或者加锁等,但是这样的操作可能会阻塞IO线程,必须慎用!。(用于全局性的处理,如网络监控,性能检测等)

不仅如此,上述代码都可能不会运行成功,看看netty为channel赋channelHandler的源码实现:

@Override
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
    final AbstractChannelHandlerContext newCtx;
    synchronized (this) {
        checkMultiplicity(handler);

        newCtx = newContext(group, filterName(name, handler), handler);

        addLast0(newCtx);

        // If the registered is false it means that the channel was not registered on an eventloop yet.
        // In this case we add the context to the pipeline and add a task that will call
        // ChannelHandler.handlerAdded(...) once the channel is registered.
        if (!registered) {
            newCtx.setAddPending();
            callHandlerCallbackLater(newCtx, true);
            return this;
        }

        EventExecutor executor = newCtx.executor();
        if (!executor.inEventLoop()) {
            newCtx.setAddPending();
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    callHandlerAdded0(newCtx);
                }
            });
            return this;
        }
    }
    callHandlerAdded0(newCtx);
    return this;
}
private static void checkMultiplicity(ChannelHandler handler) {
    if (handler instanceof ChannelHandlerAdapter) {
        ChannelHandlerAdapter h = (ChannelHandlerAdapter) handler;
        if (!h.isSharable() && h.added) {
            throw new ChannelPipelineException(
                    h.getClass().getName() +
                    " is not a @Sharable handler, so can't be added or removed multiple times.");
        }
        h.added = true;
    }
}

要求是被Sharable注解的共享channelHandler才能被添加至不同的channel中,大家可以看看ChannelInitializer,他就是Shareable的;

另外业务线程也有可能并发调用IO方法如read,write等等,这样会出现线程安全问题吗?

执行IO任务时会转回为IO线程,也就是NioEventLoop,他会将IO方法分装成一个task,串行执行。因此此类方法不会产生并发问题。

 

你可能感兴趣的:(关于netty实现的channelHandler线程安全探究以及可能导致不线程安全的例子)