Processing business: how to spread the event is in the pipeline
Netty series catalog ( https://www.cnblogs.com/binarylei/p/10117436.html )
On the one receiving the data: Adaptive read buffer and connection to solve any problem , we know that NioEventLoop constantly poll, receiving OP_READ event; then read the data spread out through pipeline.fireChannelRead (byteBuf). So the business process is in fact channelRead pipeline processing on (), this section also focuses on events in the pipeline is in communication behavior.
In the pipeline in the event spread, we need to focus on communication behavior and threads of execution events:
- Propagation behavior: to perform a post-event Handler, if not manually trigger ctx.fireChannelRead, then spread interrupted.
- Thread of execution: Business thread is executed in NioEventLoop in default. If there is obstruction of business process needs to be considered from another thread.
1. Analysis of the main line
1.1 mainline
In fact, on a main line has been talked about, when reading data triggers pipeline.fireChannelRead (byteBuf) to read data spread out. We now focuses on how events propagate in the pipeline.
Handler execution qualifications:
- 实现 ChannelInboundHandler
- Implementation channelRead not annotated @Skip
- Handler execution chain can be interrupted. If you do not take the initiative to trigger ctx.fireChannelRead method will not continue down the implementation.
1.2 knowledge points
(1) the nature of the business process
ChannelInboundHandler channelRead all data in the pipeline () execution. And execution can be interrupted.
(Two) business processing thread
Channel default processing threads are bound NioEventLoop thread, other threads can also be set:
pipeline.addLast(new UnorderedThreadPoolEventExecutor(10), serverHandler);
2. Source analysis
2.1 fireChannelRead spread in the pipeline
@Override
public final ChannelPipeline fireChannelRead(Object msg) {
AbstractChannelHandlerContext.invokeChannelRead(head, msg);
return this;
}
Note: you can see fireChannelRead from the head -> tail has been behind the spread.
2.1 propagation behavior
Once the pipeline is executed in the middle of a Handler, the propagation behavior is interrupted. If you need to continue execution, we need to take the initiative to call ctx.fireChannelRead.
// AbstractChannelHandlerContext
@Override
public ChannelHandlerContext fireChannelRead(final Object msg) {
invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);
return this;
}
private void invokeChannelRead(Object msg) {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelRead(this, msg);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelRead(msg);
}
}
Note: you can see either perform channelRead method, or until you find a method to perform fireChannelRead corresponding Handler so far. If you do not call the principal ctx.fireChannelRead, the propagation behavior will be interrupted.
May wonder, corresponding Handler by findContextInbound () is not found, why do you need through invokeHandler () judgment once again? In fact, findContextInbound have channelRead method is to find the Handler, and invokeHandler method is to determine whether the Handler has been deleted.
2.3 thread of execution
Each Handler are executed in their corresponding executor, the default is NioEventLoop thread. Of course, you can also specify other threads yourself.
// AbstractChannelHandlerContext
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRead(m);
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRead(m);
}
});
}
}
Description: We need to focus on business execution threads, as if the business occupancy throughput time is too long, it will affect Netty IO's.
2.4 ChannelPipeline vs ChannelHandlerContext
Netty each Handler are packaged into ChannelHandlerContext added to ChannelPipeline in. Although it ChannelPipeline method ChannelHandlerContext the call, but the pipeline will begin traversing the head or tail, and will only begin to traverse from the current ctx hander.
We still take the following four read comparison: channel.read (), pipeline.read (), ctx.read (), unsafe.read ()
- channel.read (): Direct call pipeline.read ().
- pipeline.read (): call tail.read (), all experienced Handler from head or tail. In fact, the last head.read () call unsafe.beginRead (), this method registers OP_ACCEPT or OP_READ event to activate Channel.
- ctx.read (): all current ctx after Handler from the beginning. If the transmission data is required instead ctx.write ctx.channel (). Write.
- unsafe.read (): the bottom of the API. And unsafe.beginRead () different, unsafe # read will actually read data from the socket revbuf.
2.5 HeadContext vs TailContext
- HeadContext:
- inbound triggered by events, from the head -> tail, so it is necessary to call ctx.firexxx events spread it.
- outbound triggered by the user, from the tail -> head, usually called directly to the head. If the middle of a re-Handler custom implementation of the method, it will not cut again, see AbstractChannelHandlerContext # invokeWrite0.
- TailContext: function basically nothing has been downward propagation of an event can be.
method | Execution order | Function Description |
---|---|---|
bind | outbound | unsafe.bind |
connect | outbound | unsafe.connect |
disconnect | outbound | unsafe.disconnect |
close | outbound | unsafe.close |
deregister | outbound | unsafe.deregister |
read | outbound | unsafe.beginRead: Registration events of interest |
write | outbound | unsafe.write |
flush | outbound | unsafe.flush |
exceptionCaught | inbound | ctx.fireExceptionCaught |
channelRegistered | inbound | callHandlerAddedForAllHandlers ctx.fireChannelRegistered |
channelUnregistered | inbound | ctx.fireChannelUnregistered destroy |
channelActive | inbound | ctx.fireChannelActive readIfIsAutoRead:调用unsafe.beginRead |
channelInactive | inbound | ctx.fireChannelInactive() |
channelRead | inbound | ctx.fireChannelRead |
channelReadComplete | inbound | ctx.fireChannelReadComplete readIfIsAutoRead |
userEventTriggered | inbound | ctx.fireUserEventTriggered |
channelWritabilityChanged | inbound | ctx.fireChannelWritabilityChanged |
method | Execution order | Function Description |
---|---|---|
channelRead | inbound | ReferenceCountUtil.release(msg) |
The intentions of recording a little bit every day. Perhaps the content is not important, but the habit is very important!