官方文档:
Interface ChannelPipeline ------------------ Netty API Reference (4.0.54.Final)
【简称ChannelHandlerContext------------->ctx】
【简称ChannelHandler------------------------>handler】
【简称ChannelPipeline------------------------>pipeline】
A list of ChannelHandlers which handles or intercepts inbound events and outbound operations of a Channel. ChannelPipeline implements an advanced form of the Intercepting Filter pattern to give a user full control over how an event is handled and how the ChannelHandlers in a pipeline interact with each other.
handler负责处理或拦截channel中inbound事件,以及outbound操作。pipeline是一种高大上的拦截过滤器,让用户随心所欲地处理事件,还能操作pipeline中的handler相互通信。
Creation of a pipeline
Each channel has its own pipeline and it is created automatically when a new channel is created.
每个channel都有自己的pipeline。且每当创建一个channel,pipeline就随之自动创建。
How an event flows in a pipeline
The following diagram describes how I/O events are processed by ChannelHandlers in a ChannelPipeline typically. An I/O event is handled by either a ChannelInboundHandler or a ChannelOutboundHandler and be forwarded to its closest handler by calling the event propagation methods defined in ChannelHandlerContext, such as ChannelHandlerContext.fireChannelRead(Object) and ChannelHandlerContext.write(Object).
下图描述了一般情况下pipeline中的handler是怎样处理I/O事件的。一个I/O事件会被inbound handler或outbound handler处理,然后被它们通过特定的方式转发到最近的(或者说“下一个”)handler。特定的转发方式有ctx.fireChannelRead(Object)和ctx.write(Object)。
An inbound event is handled by the inbound handlers in the bottom-up direction as shown on the left side of the diagram. An inbound handler usually handles the inbound data generated by the I/O thread on the bottom of the diagram. The inbound data is often read from a remote peer via the actual input operation such as SocketChannel.read(ByteBuffer). If an inbound event goes beyond the top inbound handler, it is discarded silently, or logged if it needs your attention.
很好理解,inbound handler负责处理inbound事件,inbound事件来自socket.read()(见上图左半部分最下面)。通常inbound handler就是处理从I/O线程来的inbound数据(见图底部)。inbound数据
往往来自远程设备,我们通过SocketChannel.read(ByteBuffer)读取。如果inbound事件走出最顶部的inbound handler(即图中Inbound Handler N),它会消失在茫茫人海中。当然你也可以在它逃离最顶部handler前获取它的信息。
An outbound event is handled by the outbound handler in the top-down direction as shown on the right side of the diagram. An outbound handler usually generates or transforms the outbound traffic such as write requests. If an outbound event goes beyond the bottom outbound handler, it is handled by an I/O thread associated with the Channel. The I/O thread often performs the actual output operation such as SocketChannel.write(ByteBuffer).
outbound handler自然就是处理outbound事件,outbound事件最终以socket.write()完成(见上图右半部分最下面)。outbound handler通常通过写入请求产生或改变outbound通信量。如果outbound事件走出最底部的outbound handler,那么它会被与当前channel有关的I/O线程处理。此I/O线程会调用SocketChannel.write(ByteBuffer)完成写操作。
For example, let us assume that we created the following pipeline:
ChannelPipeline p = ...;
p.addLast("1", new InboundHandlerA());
p.addLast("2", new InboundHandlerB());
p.addLast("3", new OutboundHandlerA());
p.addLast("4", new OutboundHandlerB());
p.addLast("5", new InboundOutboundHandlerX());
In the example above, the class whose name starts with Inbound means it is an inbound handler. The class whose name starts with Outbound means it is a outbound handler.
In the given example configuration, the handler evaluation order is 1, 2, 3, 4, 5 when an event goes inbound. When an event goes outbound, the order is 5, 4, 3, 2, 1. On top of this principle, ChannelPipeline skips the evaluation of certain handlers to shorten the stack depth:
● 3 and 4 don't implement ChannelInboundHandler, and therefore the actual evaluation order of an inbound event will be: 1, 2, and 5.
● 1 and 2 don't implement ChannelOutboundHandler, and therefore the actual evaluation order of a outbound event will be: 5, 4, and 3.
● If 5 implements both ChannelInboundHandler and ChannelOutboundHandler, the evaluation order of an inbound and a outbound event could be 125 and 543 respectively.
上例中,以Inbound开头的类名是inbound handler,以outbound开头的类名是outbound handler。
根据上面的设置,当一个inbound事件到来,handler的执行顺序是1,2,3,4,5。而一个outbound事件到来,handler的执行顺序是5,4,3,2,1。
根据这个规则,pipeline会一些handler来缩短“堆深度”。
- 因为3号和4号handler没有实现ChannelInboundHandler,所以当一个inbound事件到来,实际上执行顺序是1,2,5
- 因为1号和2号handler没有实现ChannelOutboundHandler,所以当一个outbound事件到来,实际上执行顺序是5,4,3
- 如果5号handler实现了ChannelInboundHandler和ChannelOutboundHandler那么,当inbound或outbound事件到来时,执行顺序会是1,2,5或5,4,3(其实就如上两条所述)
Forwarding an event to the next handler
As you might noticed in the diagram shows, a handler has to invoke the event propagation methods in ChannelHandlerContext to forward an event to its next handler. Those methods include:
你可能在上图已经看出来,一个handler必须靠ctx调用事件传递方法把事件传递到下一个handler,这些方法包括:
● Inbound event propagation methods:
○ ChannelHandlerContext.fireChannelRegistered()
○ ChannelHandlerContext.fireChannelActive()
○ ChannelHandlerContext.fireChannelRead(Object)
○ ChannelHandlerContext.fireChannelReadComplete()
○ ChannelHandlerContext.fireExceptionCaught(Throwable)
○ ChannelHandlerContext.fireUserEventTriggered(Object)
○ ChannelHandlerContext.fireChannelWritabilityChanged()
○ ChannelHandlerContext.fireChannelInactive()
○ ChannelHandlerContext.fireChannelUnregistered()
● Outbound event propagation methods:
○ ChannelHandlerContext.bind(SocketAddress, ChannelPromise)
○ ChannelHandlerContext.connect(SocketAddress, SocketAddress, ChannelPromise)
○ ChannelHandlerContext.write(Object, ChannelPromise)
○ ChannelHandlerContext.flush()
○ ChannelHandlerContext.read()
○ ChannelHandlerContext.disconnect(ChannelPromise)
○ ChannelHandlerContext.close(ChannelPromise)
○ ChannelHandlerContext.deregister(ChannelPromise)
and the following example shows how the event propagation is usually done:
下面的例子说明handler之间是如何传递事件的
public class MyInboundHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("Connected!");
ctx.fireChannelActive();
}
}
public class MyOutboundHandler extends ChannelOutboundHandlerAdapter {
@Override
public void close(ChannelHandlerContext ctx, ChannelPromise promise) {
System.out.println("Closing ..");
ctx.close(promise);
}
}
Building a pipeline
A user is supposed to have one or more ChannelHandlers in a pipeline to receive I/O events (e.g. read) and to request I/O operations (e.g. write and close). For example, a typical server will have the following handlers in each channel's pipeline, but your mileage may vary depending on the complexity and characteristics of the protocol and business logic:
- Protocol Decoder - translates binary data (e.g. ByteBuf) into a Java object.
- Protocol Encoder - translates a Java object into binary data.
- Business Logic Handler - performs the actual business logic (e.g. database access).
and it could be represented as shown in the following example:
用户在pipeline上应该有多条handler来接收I/O事件(如,读)和请求I/O操作(如,写和关闭操作)。比如,一个服务器在每个pipeline中会有如下handler,但这还是要根据你的情况而定,比如复杂度和协议的特性,以及业务逻辑。
- 协议解码器 - 将二进制数据(如ByteBuf)转化为Java对象
- 协议编码器 - 将Java对象转换为二进制数据
- 业务逻辑handler - 实现真正的业务逻辑(如数据库访问)
一个典型的例子如下:
static final EventExecutorGroup group = new DefaultEventExecutorGroup(16);
...
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("decoder", new MyProtocolDecoder());
pipeline.addLast("encoder", new MyProtocolEncoder());
// Tell the pipeline to run MyBusinessLogicHandler's event handler methods
// in a different thread than an I/O thread so that the I/O thread is not blocked by
// a time-consuming task.
// If your business logic is fully asynchronous or finished very quickly, you don't
// need to specify a group.
//告知pipline在另一个线程(非I/O线程)运行MyBusinessLogicHandler的事件处理方法,
//这样可以防止I/O线程被耗时任务阻塞。如果你的业务逻辑是完全异步的,
//或者说完成得非常快,那么你不必指定一个组执行handler。
pipeline.addLast(group, "handler", new MyBusinessLogicHandler());
Thread safety
A ChannelHandler can be added or removed at any time because a ChannelPipeline is thread safe. For example, you can insert an encryption handler when sensitive information is about to be exchanged, and remove it after the exchange.
handler可以随时添加或移除,因为pipeline是线程安全的。举个例子:当你正准备传递一些敏感数据时,可以临时添加一个加密handler,在数据传递完成后把它删除掉。