netty源码分析之-Channel注册流程详解(8)
来源:互联网 发布:外卖软件下载 编辑:程序博客网 时间:2024/06/05 16:48
前面分析来Channel初始化的流程,在这里继续分析Channel注册的相关流程,在netty中关于register底层实际上就是java nio中将Channel注册到Selector上的过程
对于register相关代码分析:
final ChannelFuture initAndRegister() { Channel channel = null; try { channel = channelFactory.newChannel(); init(channel); } catch (Throwable t) { if (channel != null) { // channel can be null if newChannel crashed (eg SocketException("too many open files")) channel.unsafe().closeForcibly(); } // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t); } ChannelFuture regFuture = config().group().register(channel); ① if (regFuture.cause() != null) { if (channel.isRegistered()) { channel.close(); } else { channel.unsafe().closeForcibly(); } } return regFuture; }
对于①关于register的核心代码分析,config()返回的是和bootstrap相关的配置文件,group返回的是对应的NioEventLoopGroup,register方法实现:
//MultithreadEventLoopGroup中的: @Override public ChannelFuture register(Channel channel) { return next().register(channel); }
对于next方法实现:
//MultithreadEventLoopGroup中的: @Override public EventLoop next() { return (EventLoop) super.next(); }//父类EventExecutorChooserFactory中实现为 @UnstableApi interface EventExecutorChooser { /** * Returns the new {@link EventExecutor} to use. */ EventExecutor next(); }
也就是返回下一个EventExecutor
继续分析register方法实现:
//在SingleThreadEventLoop中实现: @Override public ChannelFuture register(Channel channel) { return register(new DefaultChannelPromise(channel, this)); }
DefaultChannelPromise为future相关的对象。继续追溯到底层实现:
////在SingleThreadEventLoop中实现: @Override public ChannelFuture register(final ChannelPromise promise) { ObjectUtil.checkNotNull(promise, "promise"); promise.channel().unsafe().register(this, promise); return promise; }
ChannelPromise为可写的ChannelFuture对象,并且可以获得相关的Channel对象
//AbstractNioChannel中实现: @Override public NioUnsafe unsafe() { return (NioUnsafe) super.unsafe(); }
Channel中Unsafe接口下定义的都是底层相关的方法定义
//AbstractUnsafe类实现了上述的Unsafe定义的register方法:public final void register(EventLoop eventLoop, final ChannelPromise promise) { ... AbstractChannel.this.eventLoop = eventLoop; if (eventLoop.inEventLoop()) { register0(promise); } else { try { eventLoop.execute(new Runnable() { @Override public void run() { register0(promise); } }); } catch (Throwable t) { logger.warn( "Force-closing a channel whose registration task was not accepted by an event loop: {}", AbstractChannel.this, t); closeForcibly(); closeFuture.setClosed(); safeSetFailure(promise, t); } } }
对于eventLoop.inEventLoop()的实现:
//判断当前调用的线程是否是EventLoop中维护的那一个线程 public boolean inEventLoop(Thread thread) { return thread == this.thread; }
如果调用该方法的线程是EventLoop的那一个线程,那么直接执行,否则提交该任务至EventLoop中的线程来执行。因此可以避免产生多线程并发问题
对于最底层register相关的实现为:
//在AbstractNioChannel中的:protected void doRegister() throws Exception { boolean selected = false; for (;;) { try { selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this); return; } catch (CancelledKeyException e) { if (!selected) { // Force the Selector to select now as the "canceled" SelectionKey may still be // cached and not removed because no Select.select(..) operation was called yet. eventLoop().selectNow(); selected = true; } else { // We forced a select operation on the selector before but the SelectionKey is still cached // for whatever reason. JDK bug ? throw e; } } } }
其中:
private final SelectableChannel ch;...protected SelectableChannel javaChannel() { return ch; }...private Selector unwrappedSelector;
底层也就是java nio中的将Channel注册到Selector中
需要注意:
- 一个EventLoopGroup当中会包含一个或多个EventLoop
- 一个EventLoop在它的整个生命周期当中都只会与唯一一个Thread进行绑定
- 所有的由EventLoop所处理的各种I/O事件都将在它所相关联的那个Thread上进行处理(因此在实现Handle时候无需关心线程安全性问题)
- 一个Channel在它的整个生命周期中只会注册在一个EventLoop
- 一个EventLoop在运行过程当中,会被分配给一个或者多个Channel
重要结论:在netty中,Channel的实现一定是县城安全的,基于此,我们可以存储一个Channel的引用,并且在需要向远程端点发送数据时,通过这个引用来调用Channel相应的方法,即便当时有很多线程都在使用它也不会出现多线程问题,而且消息一定会按照顺序发送出去(底层是用队列来维护提交的任务)。
所以我们在业务开发中,不要将长时间执行的耗时任务放入EventLoop的执行队列中(执行的Channelhandle那些回调方法),因为它将会一直阻塞该线程所对应的所有Channel上的其他执行任务,如果我们需要进行阻塞调用或是耗时的操作(实际开发中很常见),那么我们就需要使用一个专门的EventExecutor(业务线程池)。
这里的业务线程池有两种方式能够实现:- 第一种是在ChannelHandler的的回调方法中,直接使用线程池,将需要执行的业务任务提交到线程池中。
- 第二种是借助于Netty提供的向ChannelPipeline添加ChannelHandler时调用的类似于addLast方法来传递EventExecutor
默认情况下,调用pipeline.addLast(new MyServerHandler()),ChannelHandler中的回调方法都是由I/O线程所执行;如果调用ChannelPipeline addLast(EventExecutorGroup group, ChannelHandler… handlers),那么ChannelHandler中的回调方法就可以由参数中的group线程组来执行
- netty源码分析之-Channel注册流程详解(8)
- netty源码分析(十五)Channel注册流程深度解读
- netty源码分析之-Channel与ChannelPipeline详解(6)
- netty源码分析 之三 transport(channel)
- netty(十三)源码分析之Channel
- 源码分析-netty-channel-channelFuture
- netty源码分析(十四)Netty初始化流程总结及Channel与ChannelHandlerContext作用域分析
- Netty之Channel源代码分析
- netty源码分析(十六)Channel选择器工厂与轮询算法及注册底层实现
- netty源码分析之-ServerBootstrap启动流程分析(3)
- netty源码分析之-ChannelHandler与ChannelContext详解(7)
- netty源码分析之-SimpleChannelInboundHandler与ChannelInboundHandlerAdapter详解(9)
- netty源码分析之-ReferenceCounted详解(12)
- netty源码分析之-处理器详解(13)
- netty源码分析之ChannelFuture
- netty源码分析之ChannelPipeline
- netty源码分析之ChannelHandler
- netty源码分析之ChannelPipeline
- Linux系统进程控制编程(三)——exec函数族的使用
- linux中安装hive的步骤以及关于jline报错的问题
- Java ClassLoader初探
- Spring学习(三)之依赖注入实现
- Android之间互相的录屏直播 --点对点传输(tcp长连接发送h264)
- netty源码分析之-Channel注册流程详解(8)
- 小白学Git(3)——添加远程库(github)
- C语言字符数组赋初值
- AI技术与伦理
- excel之列联表分析
- c语言的自增自减练习
- maven的相关配置
- yum 安装报错 File "/usr/bin/yum", line 30
- JAVA学习--集合