ServerBootstrap绑定和连接过程源码分析
来源:互联网 发布:网络光纤收发器 编辑:程序博客网 时间:2024/06/07 09:18
转载自:http://blog.csdn.net/iter_zc/article/details/39349177
https://segmentfault.com/a/1190000007283053
https://segmentfault.com/a/1190000007403873
典型的Netty服务器端代码如下,首先需要提供两个EventLoopGroup线程池,bossGroup是用来接受客户端TCP连接,workGroup是用来处理IO事件。然后指定采用何种Channel,Netty抽象了一组Channel的结构,和Java本身的Channel概念基本一致,NioServerSocketChannel相当于ServerSocketChannel。最后指定一组ChannelHandler来处理业务功能。(基于4.1.11.Final)
逐层跟踪源码:
回到AbstractBootstrap#initAndRegister方法中,newChannel()完成之后,就对该channel做init工作,实质上调用的是ServerBootstrap中的init:
再回到AbstractBootstrap#initAndRegister方法继续向下看,config().group().register(channel)会将NioServerSocketChannel注册到EventLoop中。config().group()获取的EventLoopGroup是AbstractBootstrap的成员,该值即ServerBootstrap.group(parentGroup, childGroup)调用时的parentGroup,即常说到的bossGroup。
因此group().register()调用的实际上是MultithreadEventLoopGroup.register()方法:
startThread()方法之后接着是addTask(task)方法,addTask是将task,即:
来看看AbstractChannel#register0(promise)做了什么:
回头来看AbstractBootstrap#doBind,判断initAndRegister返回的regFuture是否完成,在AbstractChannel#register0(promise)中,有设置success状态,因此会继续执行下面的doBind0方法:
那先来看channel.bind,即DefaultChannelPipeline#bind:
前面提到了SingleThreadEventExecutor#startThread() 方法中会调用doStartThread()来启动SingleThreadEventExecutor内部关联的Java线程:
看下NioMessageUnsafe#read:
之后对readBuf中的每个NioSocketChannel触发pipeline的ChannelRead事件:
https://segmentfault.com/a/1190000007283053
https://segmentfault.com/a/1190000007403873
典型的Netty服务器端代码如下,首先需要提供两个EventLoopGroup线程池,bossGroup是用来接受客户端TCP连接,workGroup是用来处理IO事件。然后指定采用何种Channel,Netty抽象了一组Channel的结构,和Java本身的Channel概念基本一致,NioServerSocketChannel相当于ServerSocketChannel。最后指定一组ChannelHandler来处理业务功能。(基于4.1.11.Final)
EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 100) // 设置tcp协议的请求等待队列 .option(ChannelOption.SO_BACKLOG, 1024) .childHandler(new ChildChannelHandler()); ChannelFuture f = b.bind(port).sync(); System.out.println("Netty time Server started at port " + port); f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); }NioEventLoopGroup之前已经介绍过了,直接看ServerBootstrap.bind(port),实际上是调用的AbstractBootstrap#bind():
/** * Create a new {@link Channel} and bind it. */ public ChannelFuture bind(int inetPort) { return bind(new InetSocketAddress(inetPort)); }
/** * Create a new {@link Channel} and bind it. */ public ChannelFuture bind(SocketAddress localAddress) { validate(); if (localAddress == null) { throw new NullPointerException("localAddress"); } return doBind(localAddress); }
private ChannelFuture doBind(final SocketAddress localAddress) { final ChannelFuture regFuture = initAndRegister(); final Channel channel = regFuture.channel(); if (regFuture.cause() != null) { return regFuture; } if (regFuture.isDone()) { // At this point we know that the registration was complete and successful. ChannelPromise promise = channel.newPromise(); doBind0(regFuture, channel, localAddress, promise); return promise; } else { // Registration future is almost always fulfilled already, but just in case it's not. final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel); regFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { Throwable cause = future.cause(); if (cause != null) { promise.setFailure(cause); } else { promise.registered(); doBind0(regFuture, channel, localAddress, promise); } } }); return promise; } }
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; }initAndRegister代码着重分析一下,这里的channelFactory就是根据channel(NioServerSocketChannel.class)传入的一个Class对象,实例化ReflectiveChannelFactory对象,channelFactory返回的是NioServerSocketChannel:
public NioServerSocketChannel() { this(newSocket(DEFAULT_SELECTOR_PROVIDER)); } public NioServerSocketChannel(ServerSocketChannel channel) { super(null, channel, SelectionKey.OP_ACCEPT); config = new NioServerSocketChannelConfig(this, javaChannel().socket()); }首先调用无参构造函数,该构造函数会调用下面那个构造函数,可以看到有个SelectionKey.OP_ACCEPT参数。NioServerSocketChannel继承关系链是:NioServerSocketChannel <- AbstractNioMessageChannel <- AbstractNioChannel <- AbstractChannel,右边是它的父类。
逐层跟踪源码:
protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) { super(parent, ch, readInterestOp); }
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) { super(parent); this.ch = ch; this.readInterestOp = readInterestOp; try { ch.configureBlocking(false); } catch (IOException e) { … } }
protected AbstractChannel(Channel parent) { this.parent = parent; id = newId(); unsafe = newUnsafe(); pipeline = newChannelPipeline(); }这里,在AbstractNioChannel中,将readInterestOp设置为了SelectionKey.OP_ACCEPT,同时channel设置为非阻塞模式,在AbstractChannel中实例化一个unsafe和pipeline,具体的实例分别是NioMessageUnsafe和DefaultChannelPipeline。
回到AbstractBootstrap#initAndRegister方法中,newChannel()完成之后,就对该channel做init工作,实质上调用的是ServerBootstrap中的init:
@Override void init(Channel channel) throws Exception { final Map<ChannelOption<?>, Object> options = options0(); synchronized (options) { setChannelOptions(channel, options, logger); } final Map<AttributeKey<?>, Object> attrs = attrs0(); synchronized (attrs) { for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) { @SuppressWarnings("unchecked") AttributeKey<Object> key = (AttributeKey<Object>) e.getKey(); channel.attr(key).set(e.getValue()); } } ChannelPipeline p = channel.pipeline(); final EventLoopGroup currentChildGroup = childGroup; final ChannelHandler currentChildHandler = childHandler; final Entry<ChannelOption<?>, Object>[] currentChildOptions; final Entry<AttributeKey<?>, Object>[] currentChildAttrs; synchronized (childOptions) { currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size())); } synchronized (childAttrs) { currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size())); } p.addLast(new ChannelInitializer<Channel>() { @Override public void initChannel(final Channel ch) throws Exception { final ChannelPipeline pipeline = ch.pipeline(); ChannelHandler handler = config.handler(); if (handler != null) { pipeline.addLast(handler); } ch.eventLoop().execute(new Runnable() { @Override public void run() { pipeline.addLast(new ServerBootstrapAcceptor( ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)); } }); } }); }先给NioServerSocketChannel设置Socket参数和附加属性,然后给NioServerSocketChannel关联的DefaultChannelPipeline中添加了一个ChannelInitializer,而这个 ChannelInitializer中添加了一个关键的ServerBootstrapAcceptor handler,这里会把currentChildGroup即workerGroup作为构造函数参数传入ServerBootstrapAcceptor,还有childHandler、childOptions、childAttrs,childHandler即服务器端代码中bootstrap.childHandler(…)所指的内容。
再回到AbstractBootstrap#initAndRegister方法继续向下看,config().group().register(channel)会将NioServerSocketChannel注册到EventLoop中。config().group()获取的EventLoopGroup是AbstractBootstrap的成员,该值即ServerBootstrap.group(parentGroup, childGroup)调用时的parentGroup,即常说到的bossGroup。
因此group().register()调用的实际上是MultithreadEventLoopGroup.register()方法:
@Override public ChannelFuture register(Channel channel) { return next().register(channel); }next()返回EventExecutor[] children中的一个EventExecutor来完成register。 EventExecutor实际上就是NioEventLoop,即从bossGroup中选一个NioEventLoop出来,最终调用NioEventLoop的父类SingleThreadEventLoop的register方法。
@Override public ChannelFuture register(Channel channel) { return register(new DefaultChannelPromise(channel, this)); } @Override public ChannelFuture register(final ChannelPromise promise) { ObjectUtil.checkNotNull(promise, "promise"); promise.channel().unsafe().register(this, promise); return promise; }在channel().unsafe().register()中会将EventLoop赋值给AbstractChannel内部的 eventLoop字段,到这里就完成了bossGroup 的EventLoop与NioServerSocketChannel的关联过程:
@Override 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); } } }在注册NioServerSocketChannel的过程中,会在AbstractChannel#AbstractUnsafe.register中调用 eventLoop.execute方法, 由于整个代码都是在主线程中运行的, 因此上面的 eventLoop.inEventLoop() 就为false, 于是进入到else分支, 在这个分支中调用了eventLoop.execute。再次强调一下,这里的eventLoop是bossGroup中的一个eventLoop。eventLoop是一个NioEventLoop的实例,最终调用的是 SingleThreadEventExecutor.execute:
@Override public void execute(Runnable task) { if (task == null) { throw new NullPointerException("task"); } boolean inEventLoop = inEventLoop(); if (inEventLoop) { addTask(task); } else { startThread(); addTask(task); if (isShutdown() && removeTask(task)) { reject(); } } if (!addTaskWakesUp && wakesUpForTask(task)) { wakeup(inEventLoop); } }已经分析过inEventLoop == false, 因此执行到else分支, 在这里就调用了startThread() 方法来启动 SingleThreadEventExecutor内部关联的Java线程。
private void startThread() { if (state == ST_NOT_STARTED) { if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) { doStartThread(); } } }STATE_UPDATER是SingleThreadEventExecutor内部维护的一个属性, 它的作用是标识当前的thread的状态. 在初始的时候, STATE_UPDATER == ST_NOT_STARTED, 因此第一次调用 startThread()方法时, 就会进入到if语句内, 进而调用到 doStartThread()。
startThread()方法之后接着是addTask(task)方法,addTask是将task,即:
new Runnable() { @Override public void run() { register0(promise); } }放入到taskQueue队列中去,之后会有线程从taskQueue取出任务执行。register0的主要作用是注册ChannelPipeLine。
来看看AbstractChannel#register0(promise)做了什么:
private void register0(ChannelPromise promise) { try { …… boolean firstRegistration = neverRegistered; doRegister(); neverRegistered = false; registered = true; …… pipeline.invokeHandlerAddedIfNeeded(); safeSetSuccess(promise); pipeline.fireChannelRegistered(); // Only fire a channelActive if the channel has never been registered. This prevents firing // multiple channel actives if the channel is deregistered and re-registered. if (isActive()) { if (firstRegistration) { pipeline.fireChannelActive(); } else if (config().isAutoRead()) { // This channel was registered before and autoRead() is set. This means we need to begin read // again so that we process inbound data. beginRead(); } } } catch (Throwable t) { …… } }doRegister()实际上是一个空方法,然后将promise设置为success状态,表示注册过程完成。pipeline.fireChannelRegistered()触发一个ChannelRegistered事件,会从DefaultChannelPipeline的head开始,最终传递tail即TailContext来处理该事件,TailContext的channelRegistered方法也是个空实现。
回头来看AbstractBootstrap#doBind,判断initAndRegister返回的regFuture是否完成,在AbstractChannel#register0(promise)中,有设置success状态,因此会继续执行下面的doBind0方法:
private static void doBind0( final ChannelFuture regFuture, final Channel channel, final SocketAddress localAddress, final ChannelPromise promise) { // This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up // the pipeline in its channelRegistered() implementation. channel.eventLoop().execute(new Runnable() { @Override public void run() { if (regFuture.isSuccess()) { channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); } else { promise.setFailure(regFuture.cause()); } } }); }这里的channel即NioServerSocketChannel,其关联的eventLoop即bossGroup中的一个,eventLoop的execute方法,之前有说过eventLoop的execute方法,会将task放在taskQueue中等待线程来执行。
那先来看channel.bind,即DefaultChannelPipeline#bind:
public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) { return tail.bind(localAddress, promise); }
@Override public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) { …… final AbstractChannelHandlerContext next = findContextOutbound(); EventExecutor executor = next.executor(); if (executor.inEventLoop()) { next.invokeBind(localAddress, promise); } else { safeExecute(executor, new Runnable() { @Override public void run() { next.invokeBind(localAddress, promise); } }, promise, null); } return promise; }
private AbstractChannelHandlerContext findContextOutbound() { AbstractChannelHandlerContext ctx = this; do { ctx = ctx.prev; } while (!ctx.outbound); return ctx; }
private void invokeBind(SocketAddress localAddress, ChannelPromise promise) { if (invokeHandler()) { try { ((ChannelOutboundHandler) handler()).bind(this, localAddress, promise); } catch (Throwable t) { notifyOutboundHandlerException(t, promise); } } else { bind(localAddress, promise); } }DefaultChannelPipeline中head即HeadContext是最开始的AbstractChannelHandlerContext,因此调用它的bind方法:
public void bind( ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception { unsafe.bind(localAddress, promise); }
@Override public final void bind(final SocketAddress localAddress, final ChannelPromise promise) { … boolean wasActive = isActive(); try { doBind(localAddress); } catch (Throwable t) { safeSetFailure(promise, t); closeIfClosed(); return; } if (!wasActive && isActive()) { invokeLater(new Runnable() { @Override public void run() { pipeline.fireChannelActive(); } }); } safeSetSuccess(promise); }doBind(localAddress)最终调用的是NioServerSocketChannel#bind来完成端口的绑定:
@Override protected void doBind(SocketAddress localAddress) throws Exception { if (PlatformDependent.javaVersion() >= 7) { javaChannel().bind(localAddress, config.getBacklog()); } else { javaChannel().socket().bind(localAddress, config.getBacklog()); } }然后会调用pipeline.fireChannelActive()触发ChannelActive事件:
public final ChannelPipeline fireChannelActive() { AbstractChannelHandlerContext.invokeChannelActive(head); return this; }会调用head即HeadContext的channelActive:
public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.fireChannelActive(); readIfIsAutoRead(); }
private void readIfIsAutoRead() { if (channel.config().isAutoRead()) { channel.read(); } }HeadContext会主动发起一个outbound的读事件,通过Pipeline最后传递到HeadContext的read方法。HeadHandler处理read事件时,实际上调用了unsafe的beginRead方法,最后到了AbstractNioChannel的doBeginRead():
@Override protected void doBeginRead() throws Exception { // Channel.read() or ChannelHandlerContext.read() was called final SelectionKey selectionKey = this.selectionKey; if (!selectionKey.isValid()) { return; } readPending = true; final int interestOps = selectionKey.interestOps(); if ((interestOps & readInterestOp) == 0) { selectionKey.interestOps(interestOps | readInterestOp); } }根据之前的介绍已经知道,此时的readInterestOps是OP_ACCEPT = 16, selectionKey.interestOps=0;因此进入到 selectionKey.interestOps(interestOps|readInterestOp); 使用或操作设置selectionKey的interestOps为OP_ACCEPT,表示开始监听网络连接。
前面提到了SingleThreadEventExecutor#startThread() 方法中会调用doStartThread()来启动SingleThreadEventExecutor内部关联的Java线程:
private void doStartThread() { assert thread == null; executor.execute(new Runnable() { @Override public void run() { thread = Thread.currentThread(); if (interrupted) { thread.interrupt(); } boolean success = false; updateLastExecutionTime(); try { SingleThreadEventExecutor.this.run(); success = true; } catch (Throwable t) { logger.warn("Unexpected exception from an event executor: ", t); } finally { …… } } }); }这里面最重要的是SingleThreadEventExecutor.this.run(),实际上是NioEventLoop#run:
@Override protected void run() { for (;;) { try { …… cancelledKeys = 0; needsToSelectAgain = false; final int ioRatio = this.ioRatio; if (ioRatio == 100) { try { processSelectedKeys(); } finally { // Ensure we always run tasks. runAllTasks(); } } else { final long ioStartTime = System.nanoTime(); try { processSelectedKeys(); } finally { // Ensure we always run tasks. final long ioTime = System.nanoTime() - ioStartTime; runAllTasks(ioTime * (100 - ioRatio) / ioRatio); } } } catch (Throwable t) { handleLoopException(t); } …… } }这里会调用processSelectedKeys()来处理感兴趣的OP_ACCEPT事件,调用链是NioEventLoop#processSelectedKey --- NioMessageUnsafe#read,在NioMessageUnsafe中会触发pipeline的事件;同时,还会有runAllTasks(),来处理taskQueue队列中的任务。可以设置ioRatio,来控制执行IO事件和非IO事件的时间比。
看下NioMessageUnsafe#read:
@Override public void read() { …… final ChannelConfig config = config(); final ChannelPipeline pipeline = pipeline(); final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle(); allocHandle.reset(config); boolean closed = false; Throwable exception = null; try { try { do { int localRead = doReadMessages(readBuf); … } while (allocHandle.continueReading()); } catch (Throwable t) { exception = t; } int size = readBuf.size(); for (int i = 0; i < size; i ++) { readPending = false; pipeline.fireChannelRead(readBuf.get(i)); } readBuf.clear(); allocHandle.readComplete(); pipeline.fireChannelReadComplete(); if (exception != null) { closed = closeOnReadError(exception); pipeline.fireExceptionCaught(exception); } if (closed) { inputShutdown = true; if (isOpen()) { close(voidPromise()); } } } finally { // Check if there is a readPending which was not processed yet. // This could be for two reasons: // * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method // * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method // // See https://github.com/netty/netty/issues/2254 if (!readPending && !config.isAutoRead()) { removeReadOp(); } } }doReadMessages方法实际上接受客户端连接,并创建了NioSocketChannel:
protected int doReadMessages(List<Object> buf) throws Exception { SocketChannel ch = SocketUtils.accept(javaChannel()); try { if (ch != null) { buf.add(new NioSocketChannel(this, ch)); return 1; } } catch (Throwable t) { …… } return 0; }
public NioSocketChannel(Channel parent, SocketChannel socket) { super(parent, socket); config = new NioSocketChannelConfig(this, socket.socket()); }
protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) { super(parent, ch, SelectionKey.OP_READ); }同之前的NioServerSocketChannel的构建类似,构造NioSocketChannel时传入了SelectionKey.OP_READ。
之后对readBuf中的每个NioSocketChannel触发pipeline的ChannelRead事件:
public final ChannelPipeline fireChannelRead(Object msg) { AbstractChannelHandlerContext.invokeChannelRead(head, msg); return this; }ChannelRead会从head开始传递,调用pipeline链中的InboundContext的channelRead方法,之前ServerBootstrapAcceptor是加到了pipeline链中的,而且它继承了ChannelInboundHandlerAdapter,因此会调用ServerBootstrapAcceptor的channelRead方法:
public void channelRead(ChannelHandlerContext ctx, Object msg) { final Channel child = (Channel) msg; child.pipeline().addLast(childHandler); setChannelOptions(child, childOptions, logger); for (Entry<AttributeKey<?>, Object> e: childAttrs) { child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue()); } try { childGroup.register(child).addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (!future.isSuccess()) { forceClose(child, future.cause()); } } }); } catch (Throwable t) { forceClose(child, t); } }这里的msg 即channel,是一个NioSocketChannel的实例, childGroup即workerGroup,于是workerGroup的EventLoop和NioSocketChannel 关联了。注意这里的childGroup.register(child),类似与前面的介绍,最终调用了AbstractNioChannel#doBeginRead方法,将SelectionKey.OP_READ添加到了selectionKey中。
阅读全文
0 0
- ServerBootstrap绑定和连接过程源码分析
- Netty源码分析:ServerBootstrap
- ServerBootStrap启动流程源码分析
- Netty4启动ServerBootStrap源码分析
- netty3.2.3源码分析--ServerBootstrap启动分析
- netty3.2.3源码分析--ServerBootstrap启动分析
- netty源码分析之-ServerBootstrap启动流程分析(3)
- zookeeper源码分析-连接过程
- Netty源码学习-ServerBootstrap启动及事件处理过程
- Netty 源码分析之 一 服务端创建(ServerBootstrap )
- 【Netty源码学习】ServerBootStrap
- Netty3 源码分析 - NIO server绑定过程分析
- Netty5源码分析(一) -- 服务器绑定过程分析
- wifidog源码分析 - 用户连接过程
- Android GATT 连接过程源码分析
- slf4j绑定log4j2日志系统的过程(源码分析)
- netty源码分析(三)Netty服务端ServerBootstrap的初始化与反射在其中的应用分析
- dbcp BasicDataSource 连接池获取连接过程源码分析
- JDK-bin
- 使用Serialize.Linq实现WCF方法参数可传入Linq
- unbutu下搭建samba,实现多用户远程登录,并映射到本地磁盘
- python虚拟环境的搭建
- 今天在学C++的时候遇到的问题
- ServerBootstrap绑定和连接过程源码分析
- Softmax回归模型
- 日常写理解 今日谈对IO的理解~(转载)
- Windows vs环境配置静态库和动态库的方法(/MD、/MT)
- java进行opc连接之一Modbus slave 安装配置
- hadoop 单词个数及所处文件位置统计
- 如何设置自己的jupyter服务器的密码
- 不熟悉的HTML标签整理
- HashTable