Netty系列-服务器端启动源码分析
来源:互联网 发布:js怎么遍历对象数组 编辑:程序博客网 时间:2024/05/21 02:49
1.先看下服务器端启动的流程,如之前显示服务器时间的程序
package com.netty.server;import io.netty.bootstrap.ServerBootstrap;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.*;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioServerSocketChannel;import io.netty.handler.codec.LineBasedFrameDecoder;import io.netty.handler.codec.string.StringDecoder;import io.netty.handler.logging.LogLevel;import io.netty.handler.logging.LoggingHandler;import java.util.Date;/** * Created by lcq on 12/5/2016. */public class NettyTimeServer { public static void main(String[] args) { int port = 8080; try { new NettyTimeServer().bind(port); } catch (InterruptedException e) { e.printStackTrace(); } } private void bind(int port) throws InterruptedException { EventLoopGroup bossGroup = new NioEventLoopGroup();//创建boss线程 EventLoopGroup workerGroup = new NioEventLoopGroup();//创建工作线程 try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class)//设置channel类型,服务端用的是NioServerSocketChannel.handler(new LoggingHandler(LogLevel.INFO))//设置NioServerSocketChannel的Handler.option(ChannelOption.SO_BACKLOG,1024).childHandler(new ChildHandler()); ChannelFuture f = null; f = b.bind(port).sync(); f.channel().closeFuture().sync(); }finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } /** * ChildHandler 用于处理IO事件 */ private class ChildHandler extends ChannelInitializer<SocketChannel> {//设置childHandler,作为新建的NioSocketChannel的初始化Handler当新建的与客户端通信的NioSocketChannel被注册到EventLoop成功时,该方法会被调用,添加业务Handler @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024)); socketChannel.pipeline().addLast(new StringDecoder()); socketChannel.pipeline().addLast(new TimeServerHandler2()); } private class TimeServerHandler2 extends ChannelHandlerAdapter { private int counter; @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { String body = (String)msg; System.out.println("time server receive order : " + body + " ;ther counter is :" + ++counter); String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() + System.getProperty("line.separator") : "BAD ORDER"; ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes()); ctx.writeAndFlush(resp); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } } }}2.ServerBootstrap类
public final class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> { private static final InternalLogger logger = InternalLoggerFactory.getInstance(ServerBootstrap.class); private volatile ServerChannelFactory<? extends ServerChannel> channelFactory; //NioSocketChannel创建时使用 private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>(); private final Map<AttributeKey<?>, Object> childAttrs = new LinkedHashMap<AttributeKey<?>, Object>(); private volatile EventLoopGroup childGroup; private volatile ChannelHandler childHandler;AbstractBootstrap中public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable { //服务端NioServerSocketChannel创建时使用 private volatile EventLoopGroup group; private volatile SocketAddress localAddress; private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>(); private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>(); private volatile ChannelHandler handler;
AbstractBootstrap的成员变量用于设置服务端NioServerSocketChannel,ServerBootstrap的变量用于设置创建用户连接时NioSocketChannel时使用。
3.配置
1)b.group(bossGroup,workerGroup)
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) { super.group(parentGroup); if (childGroup == null) { throw new NullPointerException("childGroup"); } if (this.childGroup != null) { throw new IllegalStateException("childGroup set already"); } this.childGroup = childGroup; return this; }分别配置父类中的boss线程组和子类中的工作线程组
2).channel(NioServerSocketChannel.class)
public ServerBootstrap channel(Class<? extends ServerChannel> channelClass) { if (channelClass == null) { throw new NullPointerException("channelClass"); } return channelFactory(new ServerBootstrapChannelFactory<ServerChannel>(channelClass)); }根据传递的类型创建channel工厂
public ServerBootstrap channelFactory(ServerChannelFactory<? extends ServerChannel> channelFactory) { if (channelFactory == null) { throw new NullPointerException("channelFactory"); } if (this.channelFactory != null) { throw new IllegalStateException("channelFactory set already"); } this.channelFactory = channelFactory; return this; }
下面就是channel工厂类的实现
private static final class ServerBootstrapChannelFactory<T extends ServerChannel> implements ServerChannelFactory<T> { private final Class<? extends T> clazz; ServerBootstrapChannelFactory(Class<? extends T> clazz) { this.clazz = clazz; } @Override public T newChannel(EventLoop eventLoop, EventLoopGroup childGroup) {//在注册创建channel的时候该方法会被执行! try { Constructor<? extends T> constructor = clazz.getConstructor(EventLoop.class, EventLoopGroup.class); return constructor.newInstance(eventLoop, childGroup); } catch (Throwable t) { throw new ChannelException("Unable to create Channel from class " + clazz, t); } } @Override public String toString() { return StringUtil.simpleClassName(clazz) + ".class"; } }
3).handler(new LoggingHandler(LogLevel.INFO))
设置服务端NioServerSocketChannel的Handler
public B handler(ChannelHandler handler) { if (handler == null) { throw new NullPointerException("handler"); } this.handler = handler;//父类AbstractBootstrap中的handler,该handler是被NioServerSocketChannel使用 return (B) this; }4).option(ChannelOption.SO_BACKLOG, 1024)
设置channel的选项,设置BackLog的大小,表示排队中连接数最大值
5).childHandler(new ChildHandler())
分清.handler和.childHandler的区别!首先,两者都是设置一个Handler,但是,前者设置的是属于服务端NioServerSocketChannel的,而后者设置的Handler是属于给客户端连接的NioSocketChannel用的。
好了,现在配置完成。如何启动服务端进行监听呢,从以下的绑定方法开始。
4.绑定流程b.bind(port)
public ChannelFuture bind(int inetPort) { return bind(new InetSocketAddress(inetPort)); } 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; } final ChannelPromise promise; if (regFuture.isDone()) { promise = channel.newPromise(); doBind0(regFuture, channel, localAddress, promise); } else { // Registration future is almost always fulfilled already, but just in case it's not. promise = new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE); regFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { doBind0(regFuture, channel, localAddress, promise); } }); } return promise; }
一步步跟进去看到了关键方法initAndRegister
final ChannelFuture initAndRegister() { Channel channel; try { channel = createChannel(); } catch (Throwable t) { return VoidChannel.INSTANCE.newFailedFuture(t); } try { init(channel); } catch (Throwable t) { channel.unsafe().closeForcibly(); return channel.newFailedFuture(t); } ChannelPromise regFuture = channel.newPromise(); channel.unsafe().register(regFuture); if (regFuture.cause() != null) { if (channel.isRegistered()) { channel.close(); } else { channel.unsafe().closeForcibly(); } } // If we are here and the promise is not failed, it's one of the following cases: // 1) If we attempted registration from the event loop, the registration has been completed at this point. // i.e. It's safe to attempt bind() or connect() now beause the channel has been registered. // 2) If we attempted registration from the other thread, the registration request has been successfully // added to the event loop's task queue for later execution. // i.e. It's safe to attempt bind() or connect() now: // because bind() or connect() will be executed *after* the scheduled registration task is executed // because register(), bind(), and connect() are all bound to the same thread. return regFuture; }
看到了createChannel方法
@Override Channel createChannel() { EventLoop eventLoop = group().next(); return channelFactory().newChannel(eventLoop, childGroup); }
这里就是之前设置的channel工厂创建channel了
init(channel);方法,初始化channel(就是NioServerSocketChannel)
void init(Channel channel) throws Exception { final Map<ChannelOption<?>, Object> options = options(); synchronized (options) { channel.config().setOptions(options); } final Map<AttributeKey<?>, Object> attrs = attrs(); 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(); if (handler() != null) { p.addLast(handler());//为NioServerSocketChannel绑定的pipeline添加Handler,即添加LoggingHandler } 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>() {//为NioServerSocketChannel的pipeline添加一个初始化Handler,当NioServerSocketChannel在EventLoop注册成功时,该handler的init方法将被调用 @Override public void initChannel(Channel ch) throws Exception {//为NioServerSocketChannel的pipeline添加ServerBootstrapAcceptor处理器, ch.pipeline().addLast(new ServerBootstrapAcceptor(currentChildHandler, currentChildOptions, currentChildAttrs)); } }); }
到上面此时pipeline中又多了一个handler:内部类ServerBootstrap$1(继承了ChannelInitializer),此时数组的链表情况如下:HeadHandler,ServerBootstrap$1和TailHandler。p.addLast方法并不是把ServerBootstrap$1放到tail上,而是放到tail的前一个节点上。
之后是
ChannelPromise regFuture = channel.newPromise();
channel.unsafe().register(regFuture);
看unsafe register方法
public final void register(final ChannelPromise promise) { 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(); promise.setFailure(t); } } } private void register0(ChannelPromise promise) { try { // check if the channel is still open as it could be closed in the mean time when the register // call was outside of the eventLoop if (!ensureOpen(promise)) { return; } doRegister();//最底层的注册调用 registered = true; promise.setSuccess(); pipeline.fireChannelRegistered();//发起pipeline调用fireChannelRegistered(其中调用head.fireChannelRegistered) if (isActive()) {//channel已经处于Active状态(如果是服务端,表示listen成功,如果是客户端,便是connect成功) pipeline.fireChannelActive();//发起pipeline的fireChannelActive } } catch (Throwable t) { // Close the channel directly to avoid FD leak. closeForcibly(); closeFuture.setClosed(); if (!promise.tryFailure(t)) { logger.warn( "Tried to fail the registration promise, but it is complete already. " + "Swallowing the cause of the registration failure:", t); } } } protected void doRegister() throws Exception { boolean selected = false; for (;;) { try { selectionKey = javaChannel().register(eventLoop().selector, 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; } } } }详细看下
@Override public ChannelPipeline fireChannelRegistered() { head.fireChannelRegistered(); return this; }
@Override public ChannelHandlerContext fireChannelRegistered() { DefaultChannelHandlerContext next = findContextInbound(MASK_CHANNEL_REGISTERED); next.invoker.invokeChannelRegistered(next); return this; }
private DefaultChannelHandlerContext findContextInbound(int mask) { DefaultChannelHandlerContext ctx = this; do { ctx = ctx.next; } while ((ctx.skipFlags & mask) != 0); return ctx; }
findContextInbound方法,里面ServerBootstrap$1是继承自ChannelInitializer,而ChannelInitializer.channelRegistered是没有@Skip注解的。@Skip注解什么用?这个要结合DefaultChannelHandlerContext.skipFlags0(Class<? extends ChannelHandler> handlerType)。这个skipFlags0方法返回一个整数,如果该方法上标记了@Skip注解,那么表示该方法在Handler被执行时,需要被忽略。所以,此时do {ctx = ctx.next;} while ((ctx.skipFlags & mask) != 0);片段的执行结果返回的是ServerBootstrap$1这个Handler。
执行ServerBootstrap$1这个Handler的channelRegistered方法
public final void channelRegistered(ChannelHandlerContext ctx) throws Exception { ChannelPipeline pipeline = ctx.pipeline(); boolean success = false; try { initChannel((C) ctx.channel());//调用了initChannel方法 pipeline.remove(this); ctx.fireChannelRegistered();//继续触发channelRegistered success = true; } catch (Throwable t) { logger.warn("Failed to initialize a channel. Closing: " + ctx.channel(), t); } finally { if (pipeline.context(this) != null) { pipeline.remove(this); } if (!success) { ctx.close(); } } }这里又回调了initChannel方法。该方法把ServerBootstrapAcceptor这个Handler加入到Pipeline中;此时handler链情况如下:HeadHandler,ServerBootstrap$1,ServerBootstrap$ServerBootstrapAcceptor和TailHandler
ctx.fireChannelRegistered()中 findContextInbound内部执行时,会跳过ServerBootstrapAcceptor这个handler,最终找到找到tailHandler,并执行channelRegistered()这个方法。就这样,最终完成了整个 pipeline.fireChannelRegistered();执行。为什么会跳过ServerBootstrapAcceptor?因为他的channelRegistered方法是有@Skip标记的,而ServerBootstrap$1没有跳过是由于ChannelInitializer继承ChannelHandlerAdapter重写了channelRegistered方法,而这个方法没有@Skip标记。
5.客户端连接
当一个客户端连接进来时,都发生了什么。
1)首先看NioEventLoop中事件循环
protected void run() { for (;;) { oldWakenUp = wakenUp.getAndSet(false); try { if (hasTasks()) { selectNow(); } else { select(); // 'wakenUp.compareAndSet(false, true)' is always evaluated // before calling 'selector.wakeup()' to reduce the wake-up // overhead. (Selector.wakeup() is an expensive operation.) // // However, there is a race condition in this approach. // The race condition is triggered when 'wakenUp' is set to // true too early. // // 'wakenUp' is set to true too early if: // 1) Selector is waken up between 'wakenUp.set(false)' and // 'selector.select(...)'. (BAD) // 2) Selector is waken up between 'selector.select(...)' and // 'if (wakenUp.get()) { ... }'. (OK) // // In the first case, 'wakenUp' is set to true and the // following 'selector.select(...)' will wake up immediately. // Until 'wakenUp' is set to false again in the next round, // 'wakenUp.compareAndSet(false, true)' will fail, and therefore // any attempt to wake up the Selector will fail, too, causing // the following 'selector.select(...)' call to block // unnecessarily. // // To fix this problem, we wake up the selector again if wakenUp // is true immediately after selector.select(...). // It is inefficient in that it wakes up the selector for both // the first case (BAD - wake-up required) and the second case // (OK - no wake-up required). if (wakenUp.get()) { selector.wakeup(); } } cancelledKeys = 0; final long ioStartTime = System.nanoTime(); needsToSelectAgain = false; if (selectedKeys != null) { processSelectedKeysOptimized(selectedKeys.flip()); } else { processSelectedKeysPlain(selector.selectedKeys()); } final long ioTime = System.nanoTime() - ioStartTime; final int ioRatio = this.ioRatio; runAllTasks(ioTime * (100 - ioRatio) / ioRatio); if (isShuttingDown()) { closeAll(); if (confirmShutdown()) { break; } } } catch (Throwable t) { logger.warn("Unexpected exception in the selector loop.", t); // Prevent possible consecutive immediate failures that lead to // excessive CPU consumption. try { Thread.sleep(1000); } catch (InterruptedException e) { // Ignore. } } } }
private void processSelectedKeysOptimized(SelectionKey[] selectedKeys) { for (int i = 0;; i ++) { final SelectionKey k = selectedKeys[i]; if (k == null) { break; } final Object a = k.attachment(); if (a instanceof AbstractNioChannel) {//NioServerSocketChannel执行这里 processSelectedKey(k, (AbstractNioChannel) a); } else { @SuppressWarnings("unchecked") NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a; processSelectedKey(k, task); } if (needsToSelectAgain) { selectAgain(); // Need to flip the optimized selectedKeys to get the right reference to the array // and reset the index to -1 which will then set to 0 on the for loop // to start over again. // // See https://github.com/netty/netty/issues/1523 selectedKeys = this.selectedKeys.flip(); i = -1; } } }
private static void processSelectedKey(SelectionKey k, AbstractNioChannel ch) { final NioUnsafe unsafe = ch.unsafe(); if (!k.isValid()) { // close the channel if the key is not valid anymore unsafe.close(unsafe.voidPromise()); return; } try { int readyOps = k.readyOps(); // Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead // to a spin loop if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {//因为是ACCEPT事件,所以执行这里(这里的read会因为NioServerSocketChannel和NioSocketChannel不同) unsafe.read(); if (!ch.isOpen()) { // Connection already closed - no need to handle write. return; } } if ((readyOps & SelectionKey.OP_WRITE) != 0) { // Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write ch.unsafe().forceFlush(); } if ((readyOps & SelectionKey.OP_CONNECT) != 0) { // remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking // See https://github.com/netty/netty/issues/924 int ops = k.interestOps(); ops &= ~SelectionKey.OP_CONNECT; k.interestOps(ops); unsafe.finishConnect(); } } catch (CancelledKeyException e) { unsafe.close(unsafe.voidPromise()); } }NioServerSocketChannel继承了AbstractNioMessageChannel,所以执行的是AbstractNioMessageChannel的版本
public void read() { assert eventLoop().inEventLoop(); if (!config().isAutoRead()) { removeReadOp(); } final ChannelConfig config = config(); final int maxMessagesPerRead = config.getMaxMessagesPerRead(); final boolean autoRead = config.isAutoRead(); final ChannelPipeline pipeline = pipeline();//获取服务端NioServerSocketChannel的pipeline boolean closed = false; Throwable exception = null; try { for (;;) { int localRead = doReadMessages(readBuf); if (localRead == 0) { break; } if (localRead < 0) { closed = true; break; } if (readBuf.size() >= maxMessagesPerRead | !autoRead) { break; } } } catch (Throwable t) { exception = t; } int size = readBuf.size(); for (int i = 0; i < size; i ++) { pipeline.fireChannelRead(readBuf.get(i)); } readBuf.clear(); pipeline.fireChannelReadComplete(); if (exception != null) { if (exception instanceof IOException) { // ServerChannel should not be closed even on IOException because it can often continue // accepting incoming connections. (e.g. too many open files) closed = !(AbstractNioMessageChannel.this instanceof ServerChannel); } pipeline.fireExceptionCaught(exception); } if (closed) { if (isOpen()) { close(voidPromise()); } } } }
doReadMessages方法
protected int doReadMessages(List<Object> buf) throws Exception { SocketChannel ch = javaChannel().accept(); try { if (ch != null) { buf.add(new NioSocketChannel(this, childEventLoopGroup().next(), ch)); return 1; } } catch (Throwable t) { logger.warn("Failed to create a new channel from an accepted socket.", t); try { ch.close(); } catch (Throwable t2) { logger.warn("Failed to close a socket.", t2); } } return 0; }
执行完doReadMessages之后,针对客户端的SocketChannel已经创建了,由于之后还会引发channelRead和channelReadComplete事件,而这些都会导致pipeline中的ServerBootstrapAcceptor的相应方法被调用。
看ServerBootstrapAcceptor中的方法
public void channelRead(ChannelHandlerContext ctx, Object msg) { Channel child = (Channel) msg; child.pipeline().addLast(childHandler);//将最开始配置的childHandler添加到SocketChannel的pipeline中,这个Handler也是一个初始化Handler,原理和服务端的一致 for (Entry<ChannelOption<?>, Object> e: childOptions) { try { if (!child.config().setOption((ChannelOption<Object>) e.getKey(), e.getValue())) { logger.warn("Unknown channel option: " + e); } } catch (Throwable t) { logger.warn("Failed to set a channel option: " + child, t); } } for (Entry<AttributeKey<?>, Object> e: childAttrs) { child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue()); } child.unsafe().register(child.newPromise());//将SocketChannel注册到WORK EventLoopGroup中,注册过程与服务端类似 }现在服务端事件监听已经启动,针对客户端连接初始化业务handler也会相应启动。
Netty5和Netty4对pipeline的处理机制还是有许多差别的。
参考博客:
http://www.cnblogs.com/chenyangyao/p/5795100.html 点击打开链接
https://my.oschina.net/geecoodeer/blog/193237 点击打开链接
- Netty系列-服务器端启动源码分析
- Netty系列-客户端启动源码分析
- netty源码分析系列文章
- netty源码分析(一)-启动
- Netty源码分析1 -- 服务器启动
- netty源码分析之服务端启动全
- netty源码分析之-服务端启动核心源码分析(5)
- [netty源码分析]--服务端启动的工作流程分析
- netty源码分析之-ServerBootstrap启动流程分析(3)
- Netty源码分析系列1——NIOEventLoopGroup的创建
- Netty源码分析:服务端启动全过程(篇幅很长)
- netty源码分析之服务端启动全解析
- Netty启动分析
- netty 启动分析 NioEventLoopGroup
- Netty启动过程分析
- Netty 源码分析之 一 揭开 Bootstrap 神秘的红盖头 (服务器端)
- Netty 源码分析
- netty源码分析小结
- leetcode-31-NextPermutation
- Kubernetes部署的最佳安全实践
- MySQL--多表连接查询
- 使用微信时提示网络连接错误
- awk命令常见用法
- Netty系列-服务器端启动源码分析
- html的canvas使用,时钟绘图总结
- PVANET: Deep but Lightweight Neural Networks for Real-time Object Detection
- 用eclipse编译,结果出现“错误:找不到或无法加载主类”
- java 设计模式 —— 浅析状态模式
- iOS面试题
- 2路归并排序算法(C语言)
- C# 程序关闭托盘图标不会自动消失
- java文件操作