备注:本文的分析基于netty 4.0.9final版本
1、ChannelPipeline结构图
2、关键类和接口分析
上一篇关于Channel的文章,在AbstractChannel的介绍中,以及提到了pipeline,这是操作处理的入口,是一个比较重要的概念,这里有必要对pipeline分析一下。
1)ChannelInboundInvoker
1. /**
2. * Interface which is shared by others which need to fire inbound events
3. */
4. interface ChannelInboundInvoker {
5.
6. /**
7. * A {@link Channel} was registered to its {@link EventLoop}.
8. *
9. * This will result in having the {@link ChannelInboundHandler#channelRegistered(ChannelHandlerContext)} method
10. * called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the
11. * {@link Channel}.
12. */
13. ChannelInboundInvoker fireChannelRegistered();
14.
15. /**
16. * A {@link Channel} was unregistered from its {@link EventLoop}.
17. *
18. * This will result in having the {@link ChannelInboundHandler#channelUnregistered(ChannelHandlerContext)} method
19. * called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the
20. * {@link Channel}.
21. */
22. @Deprecated
23. ChannelInboundInvoker fireChannelUnregistered();
24.
25. /**
26. * A {@link Channel} is active now, which means it is connected.
27. *
28. * This will result in having the {@link ChannelInboundHandler#channelActive(ChannelHandlerContext)} method
29. * called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the
30. * {@link Channel}.
31. */
32. ChannelInboundInvoker fireChannelActive();
33.
34. /**
35. * A {@link Channel} is inactive now, which means it is closed.
36. *
37. * This will result in having the {@link ChannelInboundHandler#channelInactive(ChannelHandlerContext)} method
38. * called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the
39. * {@link Channel}.
40. */
41. ChannelInboundInvoker fireChannelInactive();
42.
43. /**
44. * A {@link Channel} received an {@link Throwable} in one of its inbound operations.
45. *
46. * This will result in having the {@link ChannelInboundHandler#exceptionCaught(ChannelHandlerContext, Throwable)}
47. * method called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the
48. * {@link Channel}.
49. */
50. ChannelInboundInvoker fireExceptionCaught(Throwable cause);
51.
52. /**
53. * A {@link Channel} received an user defined event.
54. *
55. * This will result in having the {@link ChannelInboundHandler#userEventTriggered(ChannelHandlerContext, Object)}
56. * method called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the
57. * {@link Channel}.
58. */
59. ChannelInboundInvoker fireUserEventTriggered(Object event);
60.
61. /**
62. * A {@link Channel} received a message.
63. *
64. * This will result in having the {@link ChannelInboundHandler#channelRead(ChannelHandlerContext, Object)}
65. * method called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the
66. * {@link Channel}.
67. */
68. ChannelInboundInvoker fireChannelRead(Object msg);
69.
70. ChannelInboundInvoker fireChannelReadComplete();
71.
72. /**
73. * Triggers an {@link ChannelInboundHandler#channelWritabilityChanged(ChannelHandlerContext)}
74. * event to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.
75. */
76. ChannelInboundInvoker fireChannelWritabilityChanged();
77. }
这接口定义比较简单,即有事件数据从底层到应用时,用它来做相应的处理。
2)ChannelOutboundInvoker
1. /**
2. * Interface which is shared by others which need to execute outbound logic.
3. */
4. interface ChannelOutboundInvoker {
5.
6. /**
7. * Request to bind to the given {@link SocketAddress} and notify the {@link ChannelFuture} once the operation
8. * completes, either because the operation was successful or because of an error.
9. * <p>
10. * This will result in having the
11. * {@link ChannelOutboundHandler#bind(ChannelHandlerContext, SocketAddress, ChannelPromise)} method
12. * called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the
13. * {@link Channel}.
14. */
15. ChannelFuture bind(SocketAddress localAddress);
16.
17. /**
18. * Request to connect to the given {@link SocketAddress} and notify the {@link ChannelFuture} once the operation
19. * completes, either because the operation was successful or because of an error.
20. * <p>
21. * If the connection fails because of a connection timeout, the {@link ChannelFuture} will get failed with
22. * a {@link ConnectTimeoutException}. If it fails because of connection refused a {@link ConnectException}
23. * will be used.
24. * <p>
25. * This will result in having the
26. * {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)}
27. * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the
28. * {@link Channel}.
29. */
30. ChannelFuture connect(SocketAddress remoteAddress);
31.
32. /**
33. * Request to connect to the given {@link SocketAddress} while bind to the localAddress and notify the
34. * {@link ChannelFuture} once the operation completes, either because the operation was successful or because of
35. * an error.
36. * <p>
37. * This will result in having the
38. * {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)}
39. * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the
40. * {@link Channel}.
41. */
42. ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress);
43.
44. /**
45. * Request to disconnect from the remote peer and notify the {@link ChannelFuture} once the operation completes,
46. * either because the operation was successful or because of an error.
47. * <p>
48. * This will result in having the
49. * {@link ChannelOutboundHandler#disconnect(ChannelHandlerContext, ChannelPromise)}
50. * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the
51. * {@link Channel}.
52. */
53. ChannelFuture disconnect();
54.
55. /**
56. * Request to close this ChannelOutboundInvoker and notify the {@link ChannelFuture} once the operation completes,
57. * either because the operation was successful or because of
58. * an error.
59. *
60. * After it is closed it is not possible to reuse it again.
61. * <p>
62. * This will result in having the
63. * {@link ChannelOutboundHandler#close(ChannelHandlerContext, ChannelPromise)}
64. * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the
65. * {@link Channel}.
66. */
67. ChannelFuture close();
68.
69. /**
70. * Request to deregister this ChannelOutboundInvoker from the previous assigned {@link EventExecutor} and notify the
71. * {@link ChannelFuture} once the operation completes, either because the operation was successful or because of
72. * an error.
73. * <p>
74. * This will result in having the
75. * {@link ChannelOutboundHandler#deregister(ChannelHandlerContext, ChannelPromise)}
76. * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the
77. * {@link Channel}.
78. *
79. */
80. @Deprecated
81. ChannelFuture deregister();
82.
83. /**
84. * Request to bind to the given {@link SocketAddress} and notify the {@link ChannelFuture} once the operation
85. * completes, either because the operation was successful or because of an error.
86. *
87. * The given {@link ChannelPromise} will be notified.
88. * <p>
89. * This will result in having the
90. * {@link ChannelOutboundHandler#bind(ChannelHandlerContext, SocketAddress, ChannelPromise)} method
91. * called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the
92. * {@link Channel}.
93. */
94. ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise);
95.
96. /**
97. * Request to connect to the given {@link SocketAddress} and notify the {@link ChannelFuture} once the operation
98. * completes, either because the operation was successful or because of an error.
99. *
100. * The given {@link ChannelFuture} will be notified.
101. *
102. * <p>
103. * If the connection fails because of a connection timeout, the {@link ChannelFuture} will get failed with
104. * a {@link ConnectTimeoutException}. If it fails because of connection refused a {@link ConnectException}
105. * will be used.
106. * <p>
107. * This will result in having the
108. * {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)}
109. * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the
110. * {@link Channel}.
111. */
112. ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise);
113.
114. /**
115. * Request to connect to the given {@link SocketAddress} while bind to the localAddress and notify the
116. * {@link ChannelFuture} once the operation completes, either because the operation was successful or because of
117. * an error.
118. *
119. * The given {@link ChannelPromise} will be notified and also returned.
120. * <p>
121. * This will result in having the
122. * {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)}
123. * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the
124. * {@link Channel}.
125. */
126. ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise);
127.
128. /**
129. * Request to disconnect from the remote peer and notify the {@link ChannelFuture} once the operation completes,
130. * either because the operation was successful or because of an error.
131. *
132. * The given {@link ChannelPromise} will be notified.
133. * <p>
134. * This will result in having the
135. * {@link ChannelOutboundHandler#disconnect(ChannelHandlerContext, ChannelPromise)}
136. * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the
137. * {@link Channel}.
138. */
139. ChannelFuture disconnect(ChannelPromise promise);
140.
141. /**
142. * Request to close this ChannelOutboundInvoker and notify the {@link ChannelFuture} once the operation completes,
143. * either because the operation was successful or because of
144. * an error.
145. *
146. * After it is closed it is not possible to reuse it again.
147. * The given {@link ChannelPromise} will be notified.
148. * <p>
149. * This will result in having the
150. * {@link ChannelOutboundHandler#close(ChannelHandlerContext, ChannelPromise)}
151. * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the
152. * {@link Channel}.
153. */
154. ChannelFuture close(ChannelPromise promise);
155.
156. /**
157. * Request to deregister this ChannelOutboundInvoker from the previous assigned {@link EventExecutor} and notify the
158. * {@link ChannelFuture} once the operation completes, either because the operation was successful or because of
159. * an error.
160. *
161. * The given {@link ChannelPromise} will be notified.
162. * <p>
163. * This will result in having the
164. * {@link ChannelOutboundHandler#deregister(ChannelHandlerContext, ChannelPromise)}
165. * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the
166. * {@link Channel}.
167. */
168. @Deprecated
169. ChannelFuture deregister(ChannelPromise promise);
170.
171. /**
172. * Request to Read data from the {@link Channel} into the first inbound buffer, triggers an
173. * {@link ChannelInboundHandler#channelRead(ChannelHandlerContext, Object)} event if data was
174. * read, and triggers a
175. * {@link ChannelInboundHandler#channelReadComplete(ChannelHandlerContext) channelReadComplete} event so the
176. * handler can decide to continue reading. If there's a pending read operation already, this method does nothing.
177. * <p>
178. * This will result in having the
179. * {@link ChannelOutboundHandler#read(ChannelHandlerContext)}
180. * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the
181. * {@link Channel}.
182. */
183. ChannelOutboundInvoker read();
184.
185. /**
186. * Request to write a message via this ChannelOutboundInvoker through the {@link ChannelPipeline}.
187. * This method will not request to actual flush, so be sure to call {@link #flush()}
188. * once you want to request to flush all pending data to the actual transport.
189. */
190. ChannelFuture write(Object msg);
191.
192. /**
193. * Request to write a message via this ChannelOutboundInvoker through the {@link ChannelPipeline}.
194. * This method will not request to actual flush, so be sure to call {@link #flush()}
195. * once you want to request to flush all pending data to the actual transport.
196. */
197. ChannelFuture write(Object msg, ChannelPromise promise);
198.
199. /**
200. * Request to flush all pending messages via this ChannelOutboundInvoker.
201. */
202. ChannelOutboundInvoker flush();
203.
204. /**
205. * Shortcut for call {@link #write(Object, ChannelPromise)} and {@link #flush()}.
206. */
207. ChannelFuture writeAndFlush(Object msg, ChannelPromise promise);
208.
209. /**
210. * Shortcut for call {@link #write(Object)} and {@link #flush()}.
211. */
212. ChannelFuture writeAndFlush(Object msg);
213. }
这个接口定义用于处理从应用到底层的事件数据。
3)ChannelPipeline
ChannelPipeline继承ChannelInboundInvoker和ChannelOutboundInvoker,它既是一个inboundinvoke,又是一个outboundinvoke,同时它也是ChannelHandler的管理者,提供了很多方法对handler进行操作。
4)DefaultChannelPipeline
在分析DefaultChannelPipeline之前,不得不先分析DefaultChannelHandlerContext(实现ChannelHandlerContext),因为ChannelPipeline的所有handler执行都是间接由ChannelhandlerContext执行的,在后面DefaultChannelPipeline的定义中,我们可以看到这点。
ChannelHandlerContext接口定义了很多方法,关键代码如下:
1. /**
2. * Return the {@link Channel} which is bound to the {@link ChannelHandlerContext}.
3. */
4. Channel channel();
5.
6. /**
7. * The {@link EventExecutor} that is used to dispatch the events. This can also be used to directly
8. * submit tasks that get executed in the event loop. For more information please refer to the
9. * {@link EventExecutor} javadoc.
10. */
11. EventExecutor executor();
返回当前HandlerContext的Channel,以及返回当前EventExcutor。
DefaultChannelHandlerContext具体的final类,关键代码如下:
1. volatile DefaultChannelHandlerContext next;
2. volatile DefaultChannelHandlerContext prev;
3.
4. private final boolean inbound;
5. private final boolean outbound;
6. private final AbstractChannel channel;
7. private final DefaultChannelPipeline pipeline;
8. private final String name;
9. private final ChannelHandler handler;
10. private boolean removed;
11.
12. // Will be set to null if no child executor should be used, otherwise it will be set to the
13. // child executor.
14. final EventExecutor executor;
15. private ChannelFuture succeededFuture;
从上述代码中可以看出,DefaultChannelHandlerContext其实是一个上下文环境,它里面包括了当前Channel,当前的pipeline,以及当前的handler。另外它还提供了很多方法的实现,列如下面的函数。
1. @Override
2. public ChannelHandlerContext fireChannelActive() {
3. final DefaultChannelHandlerContext next = findContextInbound();
4. EventExecutor executor = next.executor();
5. if (executor.inEventLoop()) {
6. next.invokeChannelActive();
7. else {
8. new Runnable() {
9. @Override
10. public void run() {
11. next.invokeChannelActive();
12. }
13. });
14. }
15. return this;
16. }
从这个函数中,可以看出Handler的执行是由executor以线程的方式执行的。
分析完DefaultChannelHandlerContext,下面再来看DefaultChannelPipeline(实现Channelpipeline)。它是一个具体的handler管理者,handler的类型可以是inboundhandler,也可以是outboundhandler。关键代码如下:
1. final AbstractChannel channel;
2.
3. final DefaultChannelHandlerContext head;
4. final DefaultChannelHandlerContext tail;
5.
6. private final Map<String, DefaultChannelHandlerContext> name2ctx =
7. new HashMap<String, DefaultChannelHandlerContext>(4);
8.
9. final Map<EventExecutorGroup, EventExecutor> childExecutors =
10. new IdentityHashMap<EventExecutorGroup, EventExecutor>();
11.
12. public DefaultChannelPipeline(AbstractChannel channel) {
13. if (channel == null) {
14. throw new NullPointerException("channel");
15. }
16. this.channel = channel;
17.
18. new TailHandler();
19. new DefaultChannelHandlerContext(this, null, generateName(tailHandler), tailHandler);
20.
21. new HeadHandler(channel.unsafe());
22. new DefaultChannelHandlerContext(this, null, generateName(headHandler), headHandler);
23.
24. head.next = tail;
25. tail.prev = head;
26. }
从上述代码中可以看出,每个Channel都有一个对应的pipeline,每个pipeline有两个ChannelHandlerContext,分别包含默认的TailHandler和HeadHandler,并且它们构成一个双向链表结构。TailHandler处理inbound类型的数据;HeadHandler处理outbound类型的数据。
TailHandler是一个内部类,它实现ChannelInboundHandler接口,定义如下:
1. // A special catch-all handler that handles both bytes and messages.
2. static final class TailHandler implements ChannelInboundHandler {
3.
4. @Override
5. public void channelRegistered(ChannelHandlerContext ctx) throws Exception { }
6.
7. @Override
8. public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { }
9.
10. @Override
11. public void channelActive(ChannelHandlerContext ctx) throws Exception { }
12.
13. @Override
14. public void channelInactive(ChannelHandlerContext ctx) throws Exception { }
15.
16. @Override
17. public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { }
18.
19. @Override
20. public void handlerAdded(ChannelHandlerContext ctx) throws Exception { }
21.
22. @Override
23. public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { }
24.
25. @Override
26. public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { }
27.
28. @Override
29. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
30. logger.warn(
31. "An exceptionCaught() event was fired, and it reached at the tail of the pipeline. " +
32. "It usually means the last handler in the pipeline did not handle the exception.", cause);
33. }
34.
35. @Override
36. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
37. try {
38. logger.debug(
39. "Discarded inbound message {} that reached at the tail of the pipeline. " +
40. "Please check your pipeline configuration.", msg);
41. finally {
42. ReferenceCountUtil.release(msg);
43. }
44. }
45.
46. @Override
47. public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { }
48. }
TailHandler的实现函数都是空的,这说明对于底层上来应用的数据,用户必须定义Handler来处理,不能使用默认的Handler进行处理。
HeadHandler也是一个内部类,它实现ChannelOutboundHandler接口,定义如下:
1. static final class HeadHandler implements ChannelOutboundHandler {
2.
3. protected final Unsafe unsafe;
4.
5. protected HeadHandler(Unsafe unsafe) {
6. this.unsafe = unsafe;
7. }
8.
9. @Override
10. public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
11. // NOOP
12. }
13.
14. @Override
15. public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
16. // NOOP
17. }
18.
19. @Override
20. public void bind(
21. ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
22. throws Exception {
23. unsafe.bind(localAddress, promise);
24. }
25.
26. @Override
27. public void connect(
28. ChannelHandlerContext ctx,
29. SocketAddress remoteAddress, SocketAddress localAddress,
30. throws Exception {
31. unsafe.connect(remoteAddress, localAddress, promise);
32. }
33.
34. @Override
35. public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
36. unsafe.disconnect(promise);
37. }
38.
39. @Override
40. public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
41. unsafe.close(promise);
42. }
43.
44. @Override
45. public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
46. unsafe.deregister(promise);
47. }
48.
49. @Override
50. public void read(ChannelHandlerContext ctx) {
51. unsafe.beginRead();
52. }
53.
54. @Override
55. public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
56. unsafe.write(msg, promise);
57. }
58.
59. @Override
60. public void flush(ChannelHandlerContext ctx) throws Exception {
61. unsafe.flush();
62. }
63.
64. @Override
65. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
66. ctx.fireExceptionCaught(cause);
67. }
68. }
HeaderHandler的实现函数都是基于unsafe对象的函数实现的,所以对于OutBound类型的数据,即应用往底层的数据,可以使用默认的Handler进行处理。
3、基于DefaultChannelPipeline的fireChannelActive函数分析handler的调用过程
1)fireChannelActive定义
1. @Override
2. public ChannelPipeline fireChannelActive() {
3. head.fireChannelActive();
4.
5. if (channel.config().isAutoRead()) {
6. channel.read();
7. }
8.
9. return this;
10. }
2) fireChannelActive实现分析
1. @Override
2. public ChannelHandlerContext fireChannelActive() {
3. final DefaultChannelHandlerContext next = findContextInbound();
4. EventExecutor executor = next.executor();
5. if (executor.inEventLoop()) {
6. next.invokeChannelActive();
7. else {
8. new Runnable() {
9. @Override
10. public void run() {
11. next.invokeChannelActive();
12. }
13. });
14. }
15. return this;
16. }<pre name="code" class="java"> private DefaultChannelHandlerContext findContextInbound() {
17. this;
18. do {
19. ctx = ctx.next;
20. while (!ctx.inbound);
21. return ctx;
22. }
1. private void invokeChannelActive() {
2. try {
3. this);
4. catch (Throwable t) {
5. notifyHandlerException(t);
6. }
7. }
上面的代码逻辑还是比较简单的,就是从Channelhandlercontext的Head链表中获取上下文,然后调用相应的Handler执行函数。作为开发者,这时必须定义自己的Handler,并且实现ChannelActive函数,因为这是从底层到应用的数据,这就是平时我们在编写服务器端的代码时,都会定义一个继承InboundHandler的handler,并且覆盖channelActive函数的原因。
4、总结
本文分析了ChannelPipeline对Handler的管理过程,它的主要思路是利用ChannelHandlerContext上下文获取Handler,然后间接调用Handler的函数实现处理逻辑。