Netty4.0学习笔记系列之二:Handler的执行顺序

Handler在netty中,无疑占据着非常重要的地位。Handler与Servlet中的filter很像,通过Handler可以完成通讯报文的解码编码、拦截指定的报文、统一对日志错误进行处理、统一对请求进行计数、控制Handler执行与否。一句话,没有它做不到的只有你想不到的。

Netty中的所有handler都实现自ChannelHandler接口。按照输出输出来分,分为ChannelInboundHandler、ChannelOutboundHandler两大类。ChannelInboundHandler对从客户端发往服务器的报文进行处理,一般用来执行解码、读取客户端数据、进行业务处理等;ChannelOutboundHandler对从服务器发往客户端的报文进行处理,一般用来进行编码、发送报文到客户端。

Netty中,可以注册多个handler。ChannelInboundHandler按照注册的先后顺序执行;ChannelOutboundHandler按照注册的先后顺序逆序执行,如下图所示,按照注册的先后顺序对Handler进行排序,request进入Netty后的执行顺序为:

基本的概念就说到这,下面用一个例子来进行验证。该例子模拟Client与Server间的通讯,Server端注册了2个ChannelInboundHandler、2个ChannelOutboundHandler。当Client连接到Server后,会向Server发送一条消息。Server端通过ChannelInboundHandler 对Client发送的消息进行读取,通过ChannelOutboundHandler向client发送消息。最后Client把接收到的信息打印出来。

Server端一共有5个类:HelloServer InboundHandler1 InboundHandler2 OutboundHandler1 OutboundHandler2

1、HelloServer 代码如下

[java] view plain copy
  1. package com.guowl.testmultihandler;  
  2.   
  3. import io.netty.bootstrap.ServerBootstrap;  
  4. import io.netty.channel.ChannelFuture;  
  5. import io.netty.channel.ChannelInitializer;  
  6. import io.netty.channel.ChannelOption;  
  7. import io.netty.channel.EventLoopGroup;  
  8. import io.netty.channel.nio.NioEventLoopGroup;  
  9. import io.netty.channel.socket.SocketChannel;  
  10. import io.netty.channel.socket.nio.NioServerSocketChannel;  
  11.   
  12. public class HelloServer {  
  13.     public void start(int port) throws Exception {  
  14.         EventLoopGroup bossGroup = new NioEventLoopGroup();   
  15.         EventLoopGroup workerGroup = new NioEventLoopGroup();  
  16.         try {  
  17.             ServerBootstrap b = new ServerBootstrap();   
  18.             b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)   
  19.                     .childHandler(new ChannelInitializer<SocketChannel>() {   
  20.                                 @Override  
  21.                                 public void initChannel(SocketChannel ch) throws Exception {  
  22.                                     // 注册两个OutboundHandler,执行顺序为注册顺序的逆序,所以应该是OutboundHandler2 OutboundHandler1  
  23.                                     ch.pipeline().addLast(new OutboundHandler1());  
  24.                                     ch.pipeline().addLast(new OutboundHandler2());  
  25.                                     // 注册两个InboundHandler,执行顺序为注册顺序,所以应该是InboundHandler1 InboundHandler2  
  26.                                     ch.pipeline().addLast(new InboundHandler1());  
  27.                                     ch.pipeline().addLast(new InboundHandler2());  
  28.                                 }  
  29.                             }).option(ChannelOption.SO_BACKLOG, 128)   
  30.                     .childOption(ChannelOption.SO_KEEPALIVE, true);   
  31.   
  32.             ChannelFuture f = b.bind(port).sync();   
  33.   
  34.             f.channel().closeFuture().sync();  
  35.         } finally {  
  36.             workerGroup.shutdownGracefully();  
  37.             bossGroup.shutdownGracefully();  
  38.         }  
  39.     }  
  40.   
  41.     public static void main(String[] args) throws Exception {  
  42.         HelloServer server = new HelloServer();  
  43.         server.start(8000);  
  44.     }  
  45. }  


2、InboundHandler1

[java] view plain copy
  1. package com.guowl.testmultihandler;  
  2.   
  3. import io.netty.channel.ChannelHandlerContext;  
  4. import io.netty.channel.ChannelInboundHandlerAdapter;  
  5.   
  6. import org.slf4j.Logger;  
  7. import org.slf4j.LoggerFactory;  
  8.   
  9. public class InboundHandler1 extends ChannelInboundHandlerAdapter {  
  10.     private static Logger   logger  = LoggerFactory.getLogger(InboundHandler1.class);  
  11.   
  12.     @Override  
  13.     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {  
  14.         logger.info("InboundHandler1.channelRead: ctx :" + ctx);  
  15.         // 通知执行下一个InboundHandler  
  16.         ctx.fireChannelRead(msg);  
  17.     }  
  18.   
  19.     @Override  
  20.     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {  
  21.         logger.info("InboundHandler1.channelReadComplete");  
  22.         ctx.flush();  
  23.     }  
  24. }  

3、InboundHandler2

[java] view plain copy
  1. package com.guowl.testmultihandler;  
  2.   
  3. import io.netty.buffer.ByteBuf;  
  4. import io.netty.channel.ChannelHandlerContext;  
  5. import io.netty.channel.ChannelInboundHandlerAdapter;  
  6.   
  7. import org.slf4j.Logger;  
  8. import org.slf4j.LoggerFactory;  
  9.   
  10. public class InboundHandler2 extends ChannelInboundHandlerAdapter {  
  11.     private static Logger   logger  = LoggerFactory.getLogger(InboundHandler2.class);  
  12.   
  13.     @Override  
  14.     // 读取Client发送的信息,并打印出来  
  15.     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {  
  16.         logger.info("InboundHandler2.channelRead: ctx :" + ctx);  
  17.         ByteBuf result = (ByteBuf) msg;  
  18.         byte[] result1 = new byte[result.readableBytes()];  
  19.         result.readBytes(result1);  
  20.         String resultStr = new String(result1);  
  21.         System.out.println("Client said:" + resultStr);  
  22.         result.release();  
  23.   
  24.         ctx.write(msg);  
  25.     }  
  26.   
  27.     @Override  
  28.     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {  
  29.         logger.info("InboundHandler2.channelReadComplete");  
  30.         ctx.flush();  
  31.     }  
  32.   
  33. }  

 

4、OutboundHandler1

[java] view plain copy
  1. package com.guowl.testmultihandler;  
  2.   
  3. import io.netty.buffer.ByteBuf;  
  4. import io.netty.channel.ChannelHandlerContext;  
  5. import io.netty.channel.ChannelOutboundHandlerAdapter;  
  6. import io.netty.channel.ChannelPromise;  
  7.   
  8. import org.slf4j.Logger;  
  9. import org.slf4j.LoggerFactory;  
  10.   
  11. public class OutboundHandler1 extends ChannelOutboundHandlerAdapter {  
  12.     private static Logger   logger  = LoggerFactory.getLogger(OutboundHandler1.class);  
  13.     @Override  
  14.     // 向client发送消息  
  15.     public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {  
  16.         logger.info("OutboundHandler1.write");  
  17.         String response = "I am ok!";  
  18.         ByteBuf encoded = ctx.alloc().buffer(4 * response.length());  
  19.         encoded.writeBytes(response.getBytes());  
  20.         ctx.write(encoded);  
  21.         ctx.flush();  
  22.     }  
  23.       
  24.       
  25. }  

5、OutboundHandler2

[java] view plain copy
  1. package com.guowl.testmultihandler;  
  2.   
  3. import io.netty.channel.ChannelHandlerContext;  
  4. import io.netty.channel.ChannelOutboundHandlerAdapter;  
  5. import io.netty.channel.ChannelPromise;  
  6.   
  7. import org.slf4j.Logger;  
  8. import org.slf4j.LoggerFactory;  
  9.   
  10. public class OutboundHandler2 extends ChannelOutboundHandlerAdapter {  
  11.     private static Logger   logger  = LoggerFactory.getLogger(OutboundHandler2.class);  
  12.       
  13.     @Override  
  14.     public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {  
  15.         logger.info("OutboundHandler2.write");  
  16.         // 执行下一个OutboundHandler  
  17.         super.write(ctx, msg, promise);  
  18.     }  
  19. }  


Client端有两个类:HelloClient  HelloClientIntHandler

1、HelloClient  

[java] view plain copy
  1. package com.guowl.testmultihandler;  
  2.   
  3. import io.netty.bootstrap.Bootstrap;  
  4. import io.netty.channel.ChannelFuture;  
  5. import io.netty.channel.ChannelInitializer;  
  6. import io.netty.channel.ChannelOption;  
  7. import io.netty.channel.EventLoopGroup;  
  8. import io.netty.channel.nio.NioEventLoopGroup;  
  9. import io.netty.channel.socket.SocketChannel;  
  10. import io.netty.channel.socket.nio.NioSocketChannel;  
  11.   
  12. public class HelloClient {  
  13.     public void connect(String host, int port) throws Exception {  
  14.         EventLoopGroup workerGroup = new NioEventLoopGroup();  
  15.   
  16.         try {  
  17.             Bootstrap b = new Bootstrap();  
  18.             b.group(workerGroup);  
  19.             b.channel(NioSocketChannel.class);  
  20.             b.option(ChannelOption.SO_KEEPALIVE, true);  
  21.             b.handler(new ChannelInitializer<SocketChannel>() {  
  22.                 @Override  
  23.                 public void initChannel(SocketChannel ch) throws Exception {  
  24.                     ch.pipeline().addLast(new HelloClientIntHandler());  
  25.                 }  
  26.             });  
  27.   
  28.             // Start the client.  
  29.             ChannelFuture f = b.connect(host, port).sync();  
  30.             f.channel().closeFuture().sync();  
  31.         } finally {  
  32.             workerGroup.shutdownGracefully();  
  33.         }  
  34.     }  
  35.   
  36.     public static void main(String[] args) throws Exception {  
  37.         HelloClient client = new HelloClient();  
  38.         client.connect("127.0.0.1", 8000);  
  39.     }  
  40. }  

 

2、HelloClientIntHandler

[java] view plain copy
  1. package com.guowl.testmultihandler;  
  2.   
  3. import io.netty.buffer.ByteBuf;  
  4. import io.netty.channel.ChannelHandlerContext;  
  5. import io.netty.channel.ChannelInboundHandlerAdapter;  
  6.   
  7. import org.slf4j.Logger;  
  8. import org.slf4j.LoggerFactory;  
  9.   
  10. public class HelloClientIntHandler extends ChannelInboundHandlerAdapter {  
  11.     private static Logger   logger  = LoggerFactory.getLogger(HelloClientIntHandler.class);  
  12.     @Override  
  13.     // 读取服务端的信息  
  14.     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {  
  15.         logger.info("HelloClientIntHandler.channelRead");  
  16.         ByteBuf result = (ByteBuf) msg;  
  17.         byte[] result1 = new byte[result.readableBytes()];  
  18.         result.readBytes(result1);  
  19.         result.release();  
  20.         ctx.close();  
  21.         System.out.println("Server said:" + new String(result1));  
  22.     }  
  23.     @Override  
  24.     // 当连接建立的时候向服务端发送消息 ,channelActive 事件当连接建立的时候会触发  
  25.     public void channelActive(ChannelHandlerContext ctx) throws Exception {  
  26.         logger.info("HelloClientIntHandler.channelActive");  
  27.         String msg = "Are you ok?";  
  28.         ByteBuf encoded = ctx.alloc().buffer(4 * msg.length());  
  29.         encoded.writeBytes(msg.getBytes());  
  30.         ctx.write(encoded);  
  31.         ctx.flush();  
  32.     }  
  33. }  

 

server端执行结果为:

 

在使用Handler的过程中,需要注意:

1、ChannelInboundHandler之间的传递,通过调用 ctx.fireChannelRead(msg) 实现;调用ctx.write(msg) 将传递到ChannelOutboundHandler。

2、ctx.write()方法执行后,需要调用flush()方法才能令它立即执行。

3、ChannelOutboundHandler 在注册的时候需要放在最后一个ChannelInboundHandler之前,否则将无法传递到ChannelOutboundHandler。

 

  • 如果调用的是ctx.channel().write()是从尾开始执行,不会有博主说的问题,如果是直接用ctx.write()时则会有博主说的问题
posted on 2018-03-01 22:44  武胜-阿伟  阅读(5410)  评论(0编辑  收藏  举报