Netty源码分析之客户端启动过程

一、先来看一下客户端示例代码。

 1 public class NettyClientTest {
 2     public void connect(int port, String host) throws Exception {
 3         EventLoopGroup group = new NioEventLoopGroup();//与服务端不同,客户端只需要一个IO线程组
 4 
 5         try {
 6             Bootstrap b = new Bootstrap();
 7             b.group(group)
 8                     .option(ChannelOption.TCP_NODELAY, true)//禁用nagel算法
 9                     .channel(NioSocketChannel.class)//设置channel类型为NioSocketChannel
10                     .handler(new ChannelInitializer<SocketChannel>() {//为channel设置初始化Handler
11                         @Override
12                         protected void initChannel(SocketChannel ch) throws Exception {
13                             ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes());
14                             ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
15                             ch.pipeline().addLast(new StringDecoder());
16                             ch.pipeline().addLast(new EchoClientHandler());
17                         }
18                     });
19             ChannelFuture f = b.connect(host, port).sync();//等不等待连接结束
20             f.channel().closeFuture().sync();//同步等待关闭
21         }finally {
22              group.shutdownGracefully();
23         }
24     }
25    public static void main(String[] args) throws Exception{
26        int port = 8082;
27        new NettyClientTest().connect(port,"127.0.0.1");
28    }
29 }
30 
31 class EchoClientHandler extends ChannelInboundHandlerAdapter{
32     private int count = 0;
33     static final String ECHO_REQ = "HI , MY NAME IS CHENYANG.$_";
34 
35     @Override
36     public void channelActive(ChannelHandlerContext ctx) throws Exception {
37         for(int i = 0;i < 10;i++){
38             ctx.writeAndFlush(Unpooled.copiedBuffer(ECHO_REQ.getBytes()));
39         }
40     }
41 
42     @Override
43     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
44         System.out.println("This is"+ ++count + "times receive server:[" + msg + "]");
45         ctx.writeAndFlush(Unpooled.copiedBuffer("hehe.$_".getBytes()));
46         ctx.fireChannelRead(msg);
47     }
48 
49     @Override
50     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
51         ctx.flush();
52     }
53 
54     @Override
55     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
56         cause.printStackTrace();
57         ctx.close();
58     }
59 }

二、启动过程分析

由于客户端Bootstrap的配置过程和服务端ServerBootstrap配置过程原理相类似,此处不再单独讲解客户端的配置过程。接下来直接看客户端的connect过程。

三、connect过程分析

       ChannelFuture f = b.connect(host, port).sync();

       connect代码如下:

1    /**
2      * Connect a {@link Channel} to the remote peer.
3      */
4     public ChannelFuture connect(String inetHost, int inetPort) {
5         return connect(new InetSocketAddress(inetHost, inetPort));
6     }

       继续深入

 1   /**
 2      * Connect a {@link Channel} to the remote peer.
 3      */
 4     public ChannelFuture connect(SocketAddress remoteAddress) {
 5         if (remoteAddress == null) {
 6             throw new NullPointerException("remoteAddress");
 7         }
 8 
 9         validate();
10         return doConnect(remoteAddress, localAddress());
11     }

       继续查看doConnect源码

 1  /**
 2      * @see {@link #connect()}
 3      */
 4     private ChannelFuture doConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
 5         final ChannelFuture regFuture = initAndRegister();//与服务端的类似,负责初始化和注册这个channel
 6         final Channel channel = regFuture.channel();//获得创建的channel
 7         if (regFuture.cause() != null) {
 8             return regFuture;
 9         }
10 
11         final ChannelPromise promise = channel.newPromise();
12         if (regFuture.isDone()) {
13             doConnect0(regFuture, channel, remoteAddress, localAddress, promise);//连接
14         } else {
15             regFuture.addListener(new ChannelFutureListener() {
16                 @Override
17                 public void operationComplete(ChannelFuture future) throws Exception {
18                     doConnect0(regFuture, channel, remoteAddress, localAddress, promise);
19                 }
20             });
21         }
22 
23         return promise;
24     }

        看一下initAndRegister代码

 1  final ChannelFuture initAndRegister() {
 2         final Channel channel = channelFactory().newChannel();//调用之前设置的channel工厂,创建channel,此处就是NioSocketChannel
 3         try {
 4             init(channel);//初始化这个channel,这个针对客户端和服务端是不同的
 5         } catch (Throwable t) {
 6             channel.unsafe().closeForcibly();
 7             // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
 8             return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
 9         }
10 
11         ChannelFuture regFuture = group().register(channel);//向NioEventLoopGroup中注册这个channel
12         if (regFuture.cause() != null) {
13             if (channel.isRegistered()) {
14                 channel.close();
15             } else {
16                 channel.unsafe().closeForcibly();
17             }
18         }
19 
20         // If we are here and the promise is not failed, it's one of the following cases:
21         // 1) If we attempted registration from the event loop, the registration has been completed at this point.
22         //    i.e. It's safe to attempt bind() or connect() now because the channel has been registered.
23         // 2) If we attempted registration from the other thread, the registration request has been successfully
24         //    added to the event loop's task queue for later execution.
25         //    i.e. It's safe to attempt bind() or connect() now:
26         //         because bind() or connect() will be executed *after* the scheduled registration task is executed
27         //         because register(), bind(), and connect() are all bound to the same thread.
28 
29         return regFuture;
30     }

      首先看一下针对客户端的init代码。

 1     @Override
 2     @SuppressWarnings("unchecked")
 3     void init(Channel channel) throws Exception {
 4         ChannelPipeline p = channel.pipeline();
 5         p.addLast(handler());//设置用户添加的handler,也就是初始化的handler
 6 
 7         final Map<ChannelOption<?>, Object> options = options();
 8         synchronized (options) {
 9             for (Entry<ChannelOption<?>, Object> e: options.entrySet()) {
10                 try {
11                     if (!channel.config().setOption((ChannelOption<Object>) e.getKey(), e.getValue())) {//设置channel的配置选项
12                         logger.warn("Unknown channel option: " + e);
13                     }
14                 } catch (Throwable t) {
15                     logger.warn("Failed to set a channel option: " + channel, t);
16                 }
17             }
18         }
19 
20         final Map<AttributeKey<?>, Object> attrs = attrs();
21         synchronized (attrs) {
22             for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
23                 channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());//设置channel的属性
24             }
25         }
26     }

       接下来看register过程,这个和服务端是一样的。(ChannelFuture regFuture = group().register(channel);)

1   @Override
2     public ChannelFuture register(Channel channel) {
3         return next().register(channel);//next()会在Group中选出下一个NioEventLoop
4     }
1     @Override
2     public ChannelFuture register(Channel channel) {
3         return register(channel, new DefaultChannelPromise(channel, this));
4     }
 1    @Override
 2     public ChannelFuture register(final Channel channel, final ChannelPromise promise) {
 3         if (channel == null) {
 4             throw new NullPointerException("channel");
 5         }
 6         if (promise == null) {
 7             throw new NullPointerException("promise");
 8         }
 9 
10         channel.unsafe().register(this, promise);//unsafe中执行真正的注册操作
11         return promise;
12     }
 1 @Override
 2         public final void register(EventLoop eventLoop, final ChannelPromise promise) {
 3             if (eventLoop == null) {
 4                 throw new NullPointerException("eventLoop");
 5             }
 6             if (isRegistered()) {
 7                 promise.setFailure(new IllegalStateException("registered to an event loop already"));
 8                 return;
 9             }
10             if (!isCompatible(eventLoop)) {
11                 promise.setFailure(
12                         new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
13                 return;
14             }
15 
16             AbstractChannel.this.eventLoop = eventLoop;//设置该channel绑定的eventloop
17 
18             if (eventLoop.inEventLoop()) {//必须保证在eventloop线程中执行
19                 register0(promise);//注册
20             } else {
21                 try {
22                     eventLoop.execute(new OneTimeTask() {
23                         @Override
24                         public void run() {
25                             register0(promise);
26                         }
27                     });
28                 } catch (Throwable t) {
29                     logger.warn(
30                             "Force-closing a channel whose registration task was not accepted by an event loop: {}",
31                             AbstractChannel.this, t);
32                     closeForcibly();
33                     closeFuture.setClosed();
34                     safeSetFailure(promise, t);
35                 }
36             }
37         }

     继续看register0代码

 1 private void register0(ChannelPromise promise) {
 2             try {
 3                 // check if the channel is still open as it could be closed in the mean time when the register
 4                 // call was outside of the eventLoop
 5                 if (!promise.setUncancellable() || !ensureOpen(promise)) {
 6                     return;
 7                 }
 8                 boolean firstRegistration = neverRegistered;
 9                 doRegister();//在selector上注册
10                 neverRegistered = false;
11                 registered = true;//设置已经注册标识
12                 safeSetSuccess(promise);//设置注册成功
13                 pipeline.fireChannelRegistered();//引发channelRegistered事件,这会导致初始化Handler的channelRegistered被调用
14                 // Only fire a channelActive if the channel has never been registered. This prevents firing
15                 // multiple channel actives if the channel is deregistered and re-registered.
16                 if (firstRegistration && isActive()) {//如果channel可用,针对客户端,也就是connect成功
17                     pipeline.fireChannelActive();//引发channelActive事件,最终注册read事件
18                 }
19             } catch (Throwable t) {
20                 // Close the channel directly to avoid FD leak.
21                 closeForcibly();
22                 closeFuture.setClosed();
23                 safeSetFailure(promise, t);
24             }
25         }

       看doRegister代码

 1 @Override
 2     protected void doRegister() throws Exception {
 3         boolean selected = false;
 4         for (;;) {
 5             try {
 6                 selectionKey = javaChannel().register(eventLoop().selector, 0, this);//注意,这里注册的op为0,不会监听任何事件
 7                 return;
 8             } catch (CancelledKeyException e) {
 9                 if (!selected) {
10                     // Force the Selector to select now as the "canceled" SelectionKey may still be
11                     // cached and not removed because no Select.select(..) operation was called yet.
12                     eventLoop().selectNow();
13                     selected = true;
14                 } else {
15                     // We forced a select operation on the selector before but the SelectionKey is still cached
16                     // for whatever reason. JDK bug ?
17                     throw e;
18                 }
19             }
20         }
21     }

       initAndRegister执行完成之后,继续看doConnect0代码

 1  private static void doConnect0(
 2             final ChannelFuture regFuture, final Channel channel,
 3             final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
 4 
 5         // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
 6         // the pipeline in its channelRegistered() implementation.
 7         channel.eventLoop().execute(new Runnable() {//接下来的代码实在eventloop中执行,而不是用户线程
 8             @Override
 9             public void run() {
10                 if (regFuture.isSuccess()) {
11                     if (localAddress == null) {
12                         channel.connect(remoteAddress, promise);//执行connect
13                     } else {
14                         channel.connect(remoteAddress, localAddress, promise);
15                     }
16                     promise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
17                 } else {
18                     promise.setFailure(regFuture.cause());
19                 }
20             }
21         });
22     }

      继续看connect代码,简单的调用了pipeline.connect

1    @Override
2     public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
3         return pipeline.connect(remoteAddress, promise);
4     }

      从tail开始     

1    @Override
2     public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
3         return tail.connect(remoteAddress, promise);
4     }

      最终会调用到head.connect()

1   @Override
2         public void connect(
3                 ChannelHandlerContext ctx,
4                 SocketAddress remoteAddress, SocketAddress localAddress,
5                 ChannelPromise promise) throws Exception {
6             unsafe.connect(remoteAddress, localAddress, promise);
7         }
 1    @Override
 2         public final void connect(
 3                 final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
 4             if (!promise.setUncancellable() || !ensureOpen(promise)) {
 5                 return;
 6             }
 7 
 8             try {
 9                 if (connectPromise != null) {
10                     throw new IllegalStateException("connection attempt already made");
11                 }
12 
13                 boolean wasActive = isActive();
14                 if (doConnect(remoteAddress, localAddress)) {
15                     fulfillConnectPromise(promise, wasActive);//设置promise
16                 } else {
17                     connectPromise = promise;
18                     requestedRemoteAddress = remoteAddress;
19 
20                     // Schedule connect timeout.
21                     int connectTimeoutMillis = config().getConnectTimeoutMillis();//支持连接超时机制
22                     if (connectTimeoutMillis > 0) {
23                         connectTimeoutFuture = eventLoop().schedule(new OneTimeTask() {
24                             @Override
25                             public void run() {
26                                 ChannelPromise connectPromise = AbstractNioChannel.this.connectPromise;
27                                 ConnectTimeoutException cause =
28                                         new ConnectTimeoutException("connection timed out: " + remoteAddress);
29                                 if (connectPromise != null && connectPromise.tryFailure(cause)) {
30                                     close(voidPromise());
31                                 }
32                             }
33                         }, connectTimeoutMillis, TimeUnit.MILLISECONDS);
34                     }
35 
36                     promise.addListener(new ChannelFutureListener() {
37                         @Override
38                         public void operationComplete(ChannelFuture future) throws Exception {
39                             if (future.isCancelled()) {
40                                 if (connectTimeoutFuture != null) {
41                                     connectTimeoutFuture.cancel(false);
42                                 }
43                                 connectPromise = null;
44                                 close(voidPromise());
45                             }
46                         }
47                     });
48                 }
49             } catch (Throwable t) {
50                 promise.tryFailure(annotateConnectException(t, remoteAddress));
51                 closeIfClosed();
52             }
53         }

  客户端的isActive()

1     @Override
2     public boolean isActive() {
3         SocketChannel ch = javaChannel();
4         return ch.isOpen() && ch.isConnected();
5     }

  服务端的isActive()

1   @Override
2     public boolean isActive() {
3         return javaChannel().socket().isBound();
4     }

   看一下doConnect代码

 1 @Override
 2     protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
 3         if (localAddress != null) {
 4             javaChannel().socket().bind(localAddress);
 5         }
 6 
 7         boolean success = false;
 8         try {
 9             boolean connected = javaChannel().connect(remoteAddress);//执行真正的异步connect
10             if (!connected) {
11                 selectionKey().interestOps(SelectionKey.OP_CONNECT);//如果没有注册成功,就注册OP_CONNECT事件
12             }
13             success = true;
14             return connected;
15         } finally {
16             if (!success) {
17                 doClose();
18             }
19         }
20     }
 1 private void fulfillConnectPromise(ChannelPromise promise, boolean wasActive) {
 2             if (promise == null) {
 3                 // Closed via cancellation and the promise has been notified already.
 4                 return;
 5             }
 6 
 7             // trySuccess() will return false if a user cancelled the connection attempt.
 8             boolean promiseSet = promise.trySuccess();
 9 
10             // Regardless if the connection attempt was cancelled, channelActive() event should be triggered,
11             // because what happened is what happened.
12             if (!wasActive && isActive()) {//如果connect成功
13                 pipeline().fireChannelActive();//最终会注册read事件,细节如下
14             }
15 
16             // If a user cancelled the connection attempt, close the channel, which is followed by channelInactive().
17             if (!promiseSet) {
18                 close(voidPromise());
19             }
20         }
 1   @Override
 2     public ChannelPipeline fireChannelActive() {
 3         head.fireChannelActive();
 4 
 5         if (channel.config().isAutoRead()) {
 6             channel.read();//pipeline.read()-->tail.read()-->****-->head.read()-->unsafe.beginRead()-->doBeginRead()-->real操作
 7         }
 8 
 9         return this;
10     }

四、看一下如何获取异步连接结果的

  在NioEventLoop的循环中,可以看到如下代码:

1  if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
2                 // remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking
3                 // See https://github.com/netty/netty/issues/924
4                 int ops = k.interestOps();
5                 ops &= ~SelectionKey.OP_CONNECT;
6                 k.interestOps(ops);
7 
8                 unsafe.finishConnect();
9             }

       当发生OP_CONNECT事件时,最终会调用unsafe.finishConnect,代码如下

 1 @Override
 2         public final void finishConnect() {
 3             // Note this method is invoked by the event loop only if the connection attempt was
 4             // neither cancelled nor timed out.
 5 
 6             assert eventLoop().inEventLoop();//确保该操作是在eventLoop线程中的
 7 
 8             try {
 9                 boolean wasActive = isActive();
10                 doFinishConnect();
11                 fulfillConnectPromise(connectPromise, wasActive);
12             } catch (Throwable t) {
13                 fulfillConnectPromise(connectPromise, annotateConnectException(t, requestedRemoteAddress));
14             } finally {
15                 // Check for null as the connectTimeoutFuture is only created if a connectTimeoutMillis > 0 is used
16                 // See https://github.com/netty/netty/issues/1770
17                 if (connectTimeoutFuture != null) {
18                     connectTimeoutFuture.cancel(false);
19                 }
20                 connectPromise = null;
21             }
22         }
1   @Override
2     protected void doFinishConnect() throws Exception {
3         if (!javaChannel().finishConnect()) {//判断JDK的SocketChannel连接结果,返回true表示连接成功
4             throw new Error();
5         }
6     }

 判断JDK的SocketChannel连接结果,返回true表示连接成功

  1  public boolean finishConnect() throws IOException {
  2         Object var1 = this.readLock;
  3         synchronized(this.readLock) {
  4             Object var2 = this.writeLock;
  5             synchronized(this.writeLock) {
  6                 Object var3 = this.stateLock;
  7                 boolean var10000;
  8                 synchronized(this.stateLock) {
  9                     if(!this.isOpen()) {
 10                         throw new ClosedChannelException();
 11                     }
 12 
 13                     if(this.state == 2) {
 14                         var10000 = true;
 15                         return var10000;
 16                     }
 17 
 18                     if(this.state != 1) {
 19                         throw new NoConnectionPendingException();
 20                     }
 21                 }
 22 
 23                 int var41 = 0;
 24 
 25                 Object var4;
 26                 try {
 27                     label525: {
 28                         boolean var29 = false;
 29 
 30                         boolean var6;
 31                         label506: {
 32                             try {
 33                                 var29 = true;
 34                                 this.begin();
 35                                 synchronized(this.blockingLock()) {
 36                                     label480: {
 37                                         label494: {
 38                                             Object var5 = this.stateLock;
 39                                             synchronized(this.stateLock) {
 40                                                 if(!this.isOpen()) {
 41                                                     var6 = false;
 42                                                     break label494;
 43                                                 }
 44 
 45                                                 this.readerThread = NativeThread.current();
 46                                             }
 47 
 48                                             if(!this.isBlocking()) {
 49                                                 do {
 50                                                     var41 = checkConnect(this.fd, false, this.readyToConnect);
 51                                                 } while(var41 == -3 && this.isOpen());
 52                                             } else {
 53                                                 do {
 54                                                     while(true) {
 55                                                         var41 = checkConnect(this.fd, true, this.readyToConnect);
 56                                                         if(var41 == 0) {
 57                                                             continue;
 58                                                         }
 59                                                         break;
 60                                                     }
 61                                                 } while(var41 == -3 && this.isOpen());
 62                                             }
 63 
 64                                             var29 = false;
 65                                             break label480;
 66                                         }
 67 
 68                                         var29 = false;
 69                                         break label506;
 70                                     }
 71                                 }
 72                             } finally {
 73                                 if(var29) {
 74                                     Object var13 = this.stateLock;
 75                                     synchronized(this.stateLock) {
 76                                         this.readerThread = 0L;
 77                                         if(this.state == 3) {
 78                                             this.kill();
 79                                             var41 = 0;
 80                                         }
 81                                     }
 82 
 83                                     this.end(var41 > 0 || var41 == -2);
 84 
 85                                     assert IOStatus.check(var41);
 86 
 87                                 }
 88                             }
 89 
 90                             var4 = this.stateLock;
 91                             synchronized(this.stateLock) {
 92                                 this.readerThread = 0L;
 93                                 if(this.state == 3) {
 94                                     this.kill();
 95                                     var41 = 0;
 96                                 }
 97                             }
 98 
 99                             this.end(var41 > 0 || var41 == -2);
100 
101                             assert IOStatus.check(var41);
102                             break label525;
103                         }
104 
105                         Object var7 = this.stateLock;
106                         synchronized(this.stateLock) {
107                             this.readerThread = 0L;
108                             if(this.state == 3) {
109                                 this.kill();
110                                 var41 = 0;
111                             }
112                         }
113 
114                         this.end(var41 > 0 || var41 == -2);
115 
116                         assert IOStatus.check(var41);
117 
118                         return var6;
119                     }
120                 } catch (IOException var38) {
121                     this.close();
122                     throw var38;
123                 }
124 
125                 if(var41 > 0) {
126                     var4 = this.stateLock;
127                     synchronized(this.stateLock) {
128                         this.state = 2;
129                         if(this.isOpen()) {
130                             this.localAddress = Net.localAddress(this.fd);
131                         }
132                     }
133 
134                     var10000 = true;
135                     return var10000;
136                 } else {
137                     var10000 = false;
138                     return var10000;
139                 }
140             }
141         }
142     }

    fulfillConnectPromise会出发链接激活事件

 1    private void fulfillConnectPromise(ChannelPromise promise, boolean wasActive) {
 2             if (promise == null) {
 3                 // Closed via cancellation and the promise has been notified already.
 4                 return;
 5             }
 6 
 7             // trySuccess() will return false if a user cancelled the connection attempt.
 8             boolean promiseSet = promise.trySuccess();
 9 
10             // Regardless if the connection attempt was cancelled, channelActive() event should be triggered,
11             // because what happened is what happened.
12             if (!wasActive && isActive()) {
13                 pipeline().fireChannelActive();//参照前面的说明
14             }
15 
16             // If a user cancelled the connection attempt, close the channel, which is followed by channelInactive().
17             if (!promiseSet) {
18                 close(voidPromise());
19             }
20         }

 五、write过程

  由于在服务端启动过程中已经多次分析了channel的read执行过程,因此在这里单独分析一下channel的write过程。首先看一下channe接口中关于write方法的定义:

1 /**
2      * Request to write a message via this {@link Channel} through the {@link ChannelPipeline}.
3      * This method will not request to actual flush, so be sure to call {@link #flush()}
4      * once you want to request to flush all pending data to the actual transport.
5      */
6     ChannelFuture write(Object msg);

    其在AbstractChannel中的实现为:

1  @Override
2     public ChannelFuture write(Object msg) {
3         return pipeline.write(msg);
4     }

    继续深入

1    @Override
2     public ChannelFuture write(Object msg) {
3         return tail.write(msg);
4     }

    事件进入pipeline之后,会从tail  context开始向前传播(因为write是个outbound事件)

1    @Override
2     public ChannelFuture write(Object msg) {
3         return write(msg, newPromise());
4     }

   继续

 1  @Override
 2     public ChannelFuture write(final Object msg, final ChannelPromise promise) {
 3         if (msg == null) {
 4             throw new NullPointerException("msg");
 5         }
 6 
 7         if (!validatePromise(promise, true)) {
 8             ReferenceCountUtil.release(msg);
 9             // cancelled
10             return promise;
11         }
12         write(msg, false, promise);//false表示不flush缓冲区的意思
13 
14         return promise;
15     }

  继续

 1  private void write(Object msg, boolean flush, ChannelPromise promise) {
 2 
 3         AbstractChannelHandlerContext next = findContextOutbound();
 4         EventExecutor executor = next.executor();
 5         if (executor.inEventLoop()) {
 6             next.invokeWrite(msg, promise);
 7             if (flush) {
 8                 next.invokeFlush();
 9             }
10         } else {
11             int size = channel.estimatorHandle().size(msg);
12             if (size > 0) {
13                 ChannelOutboundBuffer buffer = channel.unsafe().outboundBuffer();
14                 // Check for null as it may be set to null if the channel is closed already
15                 if (buffer != null) {
16                     buffer.incrementPendingOutboundBytes(size);
17                 }
18             }
19             Runnable task;
20             if (flush) {
21                 task = WriteAndFlushTask.newInstance(next, msg, size, promise);
22             }  else {
23                 task = WriteTask.newInstance(next, msg, size, promise);
24             }
25             safeExecute(executor, task, promise, msg);
26         }
27     }

    看一下findContextOutbound的实现

1   private AbstractChannelHandlerContext findContextOutbound() {
2         AbstractChannelHandlerContext ctx = this;
3         do {
4             ctx = ctx.prev;
5         } while (!ctx.outbound);
6         return ctx;
7     }

    找到下一个OutBound类型的Context之后,会调用Context中的Handler

1    private void invokeWrite(Object msg, ChannelPromise promise) {
2         try {
3             ((ChannelOutboundHandler) handler()).write(this, msg, promise);
4         } catch (Throwable t) {
5             notifyOutboundHandlerException(t, promise);
6         }
7     }

     继续看handler的write实现

 1  /**
 2      * Calls {@link ChannelHandlerContext#write(Object)} to forward
 3      * to the next {@link ChannelOutboundHandler} in the {@link ChannelPipeline}.
 4      *
 5      * Sub-classes may override this method to change behavior.
 6      */
 7     @Override
 8     public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
 9         ctx.write(msg, promise);
10     }

     可以看到,默认的实现是将事件继续沿pipeline向前传播,最终会传到head Context

1   @Override
2         public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
3             unsafe.write(msg, promise);
4         }

     unsafe会执行真正的IO操作

 1  @Override
 2         public final void write(Object msg, ChannelPromise promise) {
 3             ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
 4             if (outboundBuffer == null) {
 5                 // If the outboundBuffer is null we know the channel was closed and so
 6                 // need to fail the future right away. If it is not null the handling of the rest
 7                 // will be done in flush0()
 8                 // See https://github.com/netty/netty/issues/2362
 9                 safeSetFailure(promise, CLOSED_CHANNEL_EXCEPTION);
10                 // release message now to prevent resource-leak
11                 ReferenceCountUtil.release(msg);
12                 return;
13             }
14 
15             int size;
16             try {
17                 msg = filterOutboundMessage(msg);
18                 size = estimatorHandle().size(msg);
19                 if (size < 0) {
20                     size = 0;
21                 }
22             } catch (Throwable t) {
23                 safeSetFailure(promise, t);
24                 ReferenceCountUtil.release(msg);
25                 return;
26             }
27 
28             outboundBuffer.addMessage(msg, size, promise);
29         }

    可以看到,unsafe的write操作并不是真正的将数据发送出去,而是在环形缓冲区中进行缓存。当channel调用flush时,最终会执行unsafe的flush

 1  @Override
 2         public final void flush() {
 3             ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
 4             if (outboundBuffer == null) {
 5                 return;
 6             }
 7 
 8             outboundBuffer.addFlush();
 9             flush0();
10         }

     addFlush仅仅是对之前缓存的Message进行标记

 1   /**
 2      * Add a flush to this {@link ChannelOutboundBuffer}. This means all previous added messages are marked as flushed
 3      * and so you will be able to handle them.
 4      */
 5     public void addFlush() {
 6         // There is no need to process all entries if there was already a flush before and no new messages
 7         // where added in the meantime.
 8         //
 9         // See https://github.com/netty/netty/issues/2577
10         Entry entry = unflushedEntry;
11         if (entry != null) {
12             if (flushedEntry == null) {
13                 // there is no flushedEntry yet, so start with the entry
14                 flushedEntry = entry;
15             }
16             do {
17                 flushed ++;
18                 if (!entry.promise.setUncancellable()) {
19                     // Was cancelled so make sure we free up memory and notify about the freed bytes
20                     int pending = entry.cancel();
21                     decrementPendingOutboundBytes(pending, false, true);
22                 }
23                 entry = entry.next;
24             } while (entry != null);
25 
26             // All flushed so reset unflushedEntry
27             unflushedEntry = null;
28         }
29     }

    接下来看一下真正的flush操作

 1  protected void flush0() {
 2             if (inFlush0) {
 3                 // Avoid re-entrance
 4                 return;
 5             }
 6 
 7             final ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
 8             if (outboundBuffer == null || outboundBuffer.isEmpty()) {
 9                 return;
10             }
11 
12             inFlush0 = true;
13 
14             // Mark all pending write requests as failure if the channel is inactive.
15             if (!isActive()) {
16                 try {
17                     if (isOpen()) {
18                         outboundBuffer.failFlushed(NOT_YET_CONNECTED_EXCEPTION, true);
19                     } else {
20                         // Do not trigger channelWritabilityChanged because the channel is closed already.
21                         outboundBuffer.failFlushed(CLOSED_CHANNEL_EXCEPTION, false);
22                     }
23                 } finally {
24                     inFlush0 = false;
25                 }
26                 return;
27             }
28 
29             try {
30                 doWrite(outboundBuffer);
31             } catch (Throwable t) {
32                 boolean close = t instanceof IOException && config().isAutoClose();
33                 // We do not want to trigger channelWritabilityChanged event if the channel is going to be closed.
34                 outboundBuffer.failFlushed(t, !close);
35                 if (close) {
36                     close(voidPromise());
37                 }
38             } finally {
39                 inFlush0 = false;
40             }
41         }

     doWrite执行真正的写操作

 1  @Override
 2     protected void doWrite(ChannelOutboundBuffer in) throws Exception {
 3         for (;;) {
 4             int size = in.size();
 5             if (size == 0) {
 6                 // All written so clear OP_WRITE
 7                 clearOpWrite();
 8                 break;
 9             }
10             long writtenBytes = 0;
11             boolean done = false;
12             boolean setOpWrite = false;
13 
14             // Ensure the pending writes are made of ByteBufs only.
15             ByteBuffer[] nioBuffers = in.nioBuffers();
16             int nioBufferCnt = in.nioBufferCount();
17             long expectedWrittenBytes = in.nioBufferSize();
18             SocketChannel ch = javaChannel();
19 
20             // Always us nioBuffers() to workaround data-corruption.
21             // See https://github.com/netty/netty/issues/2761
22             switch (nioBufferCnt) {
23                 case 0:
24                     // We have something else beside ByteBuffers to write so fallback to normal writes.
25                     super.doWrite(in);
26                     return;
27                 case 1:
28                     // Only one ByteBuf so use non-gathering write
29                     ByteBuffer nioBuffer = nioBuffers[0];
30                     for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) {
31                         final int localWrittenBytes = ch.write(nioBuffer);
32                         if (localWrittenBytes == 0) {
33                             setOpWrite = true;
34                             break;
35                         }
36                         expectedWrittenBytes -= localWrittenBytes;
37                         writtenBytes += localWrittenBytes;
38                         if (expectedWrittenBytes == 0) {
39                             done = true;
40                             break;
41                         }
42                     }
43                     break;
44                 default:
45                     for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) {
46                         final long localWrittenBytes = ch.write(nioBuffers, 0, nioBufferCnt);
47                         if (localWrittenBytes == 0) {
48                             setOpWrite = true;
49                             break;
50                         }
51                         expectedWrittenBytes -= localWrittenBytes;
52                         writtenBytes += localWrittenBytes;
53                         if (expectedWrittenBytes == 0) {
54                             done = true;
55                             break;
56                         }
57                     }
58                     break;
59             }
60 
61             // Release the fully written buffers, and update the indexes of the partially written buffer.
62             in.removeBytes(writtenBytes);
63 
64             if (!done) {
65                 // Did not write all buffers completely.
66                 incompleteWrite(setOpWrite);
67                 break;
68             }
69         }
70     }

 

 1    protected final void clearOpWrite() {
 2         final SelectionKey key = selectionKey();
 3         // Check first if the key is still valid as it may be canceled as part of the deregistration
 4         // from the EventLoop
 5         // See https://github.com/netty/netty/issues/2104
 6         if (!key.isValid()) {
 7             return;
 8         }
 9         final int interestOps = key.interestOps();
10         if ((interestOps & SelectionKey.OP_WRITE) != 0) {
11             key.interestOps(interestOps & ~SelectionKey.OP_WRITE);
12         }
13     }

 

  

 1 @Override
 2     protected void doWrite(ChannelOutboundBuffer in) throws Exception {
 3         int writeSpinCount = -1;
 4 
 5         for (;;) {
 6             Object msg = in.current();
 7             if (msg == null) {
 8                 // Wrote all messages.
 9                 clearOpWrite();
10                 break;
11             }
12 
13             if (msg instanceof ByteBuf) {
14                 ByteBuf buf = (ByteBuf) msg;
15                 int readableBytes = buf.readableBytes();
16                 if (readableBytes == 0) {
17                     in.remove();
18                     continue;
19                 }
20 
21                 boolean setOpWrite = false;
22                 boolean done = false;
23                 long flushedAmount = 0;
24                 if (writeSpinCount == -1) {
25                     writeSpinCount = config().getWriteSpinCount();
26                 }
27                 for (int i = writeSpinCount - 1; i >= 0; i --) {
28                     int localFlushedAmount = doWriteBytes(buf);
29                     if (localFlushedAmount == 0) {
30                         setOpWrite = true;
31                         break;
32                     }
33 
34                     flushedAmount += localFlushedAmount;
35                     if (!buf.isReadable()) {
36                         done = true;
37                         break;
38                     }
39                 }
40 
41                 in.progress(flushedAmount);
42 
43                 if (done) {
44                     in.remove();
45                 } else {
46                     incompleteWrite(setOpWrite);
47                     break;
48                 }
49             } else if (msg instanceof FileRegion) {
50                 FileRegion region = (FileRegion) msg;
51                 boolean done = region.transfered() >= region.count();
52                 boolean setOpWrite = false;
53 
54                 if (!done) {
55                     long flushedAmount = 0;
56                     if (writeSpinCount == -1) {
57                         writeSpinCount = config().getWriteSpinCount();
58                     }
59 
60                     for (int i = writeSpinCount - 1; i >= 0; i--) {
61                         long localFlushedAmount = doWriteFileRegion(region);
62                         if (localFlushedAmount == 0) {
63                             setOpWrite = true;
64                             break;
65                         }
66 
67                         flushedAmount += localFlushedAmount;
68                         if (region.transfered() >= region.count()) {
69                             done = true;
70                             break;
71                         }
72                     }
73 
74                     in.progress(flushedAmount);
75                 }
76 
77                 if (done) {
78                     in.remove();
79                 } else {
80                     incompleteWrite(setOpWrite);
81                     break;
82                 }
83             } else {
84                 // Should not reach here.
85                 throw new Error();
86             }
87         }
88     }

 

1    @Override
2     protected int doWriteBytes(ByteBuf buf) throws Exception {
3         final int expectedWrittenBytes = buf.readableBytes();
4         return buf.readBytes(javaChannel(), expectedWrittenBytes);
5     }

 

 1    /**
 2      * Notify the {@link ChannelPromise} of the current message about writing progress.
 3      */
 4     public void progress(long amount) {
 5         Entry e = flushedEntry;
 6         assert e != null;
 7         ChannelPromise p = e.promise;
 8         if (p instanceof ChannelProgressivePromise) {
 9             long progress = e.progress + amount;
10             e.progress = progress;
11             ((ChannelProgressivePromise) p).tryProgress(progress, e.total);
12         }
13     }

 

 1 @Override
 2     public boolean tryProgress(long progress, long total) {
 3         if (total < 0) {
 4             total = -1;
 5             if (progress < 0 || isDone()) {
 6                 return false;
 7             }
 8         } else if (progress < 0 || progress > total || isDone()) {
 9             return false;
10         }
11 
12         notifyProgressiveListeners(progress, total);
13         return true;
14     }

 

posted @ 2016-08-22 20:28  陈洋Cy  阅读(2102)  评论(0编辑  收藏  举报