netty 中的解码器、编码器、ServerHandler 管道
Decoder, Encoder, ServerHandler pipeline in netty
查看文档,它是这样说的:
https://netty.io/4.0/api/io/netty/channel/ChannelPipeline.html
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: 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.
pipeline.addLast(group, "handler",
new MyBusinessLogicHandler());
在 Github 上的许多示例中,我看到了相同的模式。我想知道是否有人可以解释为什么 businessHandler 不在解码器和编码器之间。我认为您会得到您的 POJO,然后在业务处理程序中对其进行处理,然后对其进行编码。
当然,业务处理程序在解码器和 encoder.Take Factorial 示例的示例之间。
public void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
if (sslCtx != null) {
pipeline.addLast(sslCtx.newHandler(ch.alloc()));
}
// Enable stream compression (you can remove these two if unnecessary)
pipeline.addLast(ZlibCodecFactory.newZlibEncoder(ZlibWrapper.GZIP));
pipeline.addLast(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP));
// Add the number codec first,
pipeline.addLast(new BigIntegerDecoder());
pipeline.addLast(new NumberEncoder());
// and then business logic.
// Please note we create a handler for every new channel
// because it has stateful properties.
pipeline.addLast(new FactorialServerHandler());
}`
在initChannel
pipeline的函数中想到先添加encoder和decoder,最后添加handler。执行流程实际上按解码器、处理程序和编码器排序。
decoder,handler 和 encoder 等处理程序实际上存储在 AbstractChannelHandlerContext
class 中。 Netty中有一个AbstractChannelHandlerContext
的链表。列表排列成decoder context
-->handler context
-->encoder context
,执行也是一样!
由于调用处理程序的顺序,解码器和编码器通常位于管道的开头。对于传入数据,它是自下而上的,而对于传出数据,它是自上而下的。
例如
pipeline.addLast(new MyEncoder());
pipeline.addLast(new MyDecoder());
pipeline.addLast(new MyBusiness());
在这种情况下,对于传入数据调用顺序是:MyDecoder(将数据转换为 POJO)-> MyBusiness(传入流不调用编码器)和传出数据:MyBusiness->MyEncoder(解码器不是要求传出流)。
如果您在业务处理程序(实际上是解码器之后的 POJO)中接收到一个传入流,对其进行处理并将其写回,看起来 MyBusiness 位于编码器和解码器之间,因为数据正在返回到编码器。
事实上,如果你在你的服务器中添加1.decoder,2.businessHandler,3.encoder,然后写成ctx.channel().writeAndFlush()
或者ctx.pipeline().writeAndFlush()
,encoder就会被调用。在这种情况下是 bc,它将从尾部开始寻找上一个 outboundChannel。但是,如果您写 ctx.writeAndFlush()
,它会从 businessHandler 的位置寻找上一个 outboundChannel。
在AbstractChannelHandlerContext的findContextOutbound()
第一行加个断点,就搞定了
private AbstractChannelHandlerContext findContextOutbound(int mask) {
AbstractChannelHandlerContext ctx = this;
EventExecutor currentExecutor = executor();
do {
ctx = ctx.prev;
} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_OUTBOUND));
return ctx;
}
查看文档,它是这样说的:
https://netty.io/4.0/api/io/netty/channel/ChannelPipeline.html
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: 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.
pipeline.addLast(group, "handler", new MyBusinessLogicHandler());
在 Github 上的许多示例中,我看到了相同的模式。我想知道是否有人可以解释为什么 businessHandler 不在解码器和编码器之间。我认为您会得到您的 POJO,然后在业务处理程序中对其进行处理,然后对其进行编码。
当然,业务处理程序在解码器和 encoder.Take Factorial 示例的示例之间。
public void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
if (sslCtx != null) {
pipeline.addLast(sslCtx.newHandler(ch.alloc()));
}
// Enable stream compression (you can remove these two if unnecessary)
pipeline.addLast(ZlibCodecFactory.newZlibEncoder(ZlibWrapper.GZIP));
pipeline.addLast(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP));
// Add the number codec first,
pipeline.addLast(new BigIntegerDecoder());
pipeline.addLast(new NumberEncoder());
// and then business logic.
// Please note we create a handler for every new channel
// because it has stateful properties.
pipeline.addLast(new FactorialServerHandler());
}`
在initChannel
pipeline的函数中想到先添加encoder和decoder,最后添加handler。执行流程实际上按解码器、处理程序和编码器排序。
decoder,handler 和 encoder 等处理程序实际上存储在 AbstractChannelHandlerContext
class 中。 Netty中有一个AbstractChannelHandlerContext
的链表。列表排列成decoder context
-->handler context
-->encoder context
,执行也是一样!
由于调用处理程序的顺序,解码器和编码器通常位于管道的开头。对于传入数据,它是自下而上的,而对于传出数据,它是自上而下的。
例如
pipeline.addLast(new MyEncoder());
pipeline.addLast(new MyDecoder());
pipeline.addLast(new MyBusiness());
在这种情况下,对于传入数据调用顺序是:MyDecoder(将数据转换为 POJO)-> MyBusiness(传入流不调用编码器)和传出数据:MyBusiness->MyEncoder(解码器不是要求传出流)。
如果您在业务处理程序(实际上是解码器之后的 POJO)中接收到一个传入流,对其进行处理并将其写回,看起来 MyBusiness 位于编码器和解码器之间,因为数据正在返回到编码器。
事实上,如果你在你的服务器中添加1.decoder,2.businessHandler,3.encoder,然后写成ctx.channel().writeAndFlush()
或者ctx.pipeline().writeAndFlush()
,encoder就会被调用。在这种情况下是 bc,它将从尾部开始寻找上一个 outboundChannel。但是,如果您写 ctx.writeAndFlush()
,它会从 businessHandler 的位置寻找上一个 outboundChannel。
在AbstractChannelHandlerContext的findContextOutbound()
第一行加个断点,就搞定了
private AbstractChannelHandlerContext findContextOutbound(int mask) {
AbstractChannelHandlerContext ctx = this;
EventExecutor currentExecutor = executor();
do {
ctx = ctx.prev;
} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_OUTBOUND));
return ctx;
}