Netty系列-服务器端启动源码分析

来源:互联网 发布:js怎么遍历对象数组 编辑:程序博客网 时间:2024/05/21 02:49
之前写了几个Netty实现服务端和客户端交互的例子,现在通过分析源码看一下服务器端和客户端启动的整个流程。

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 点击打开链接



0 0
原创粉丝点击