Netty-In-Depth.pdf

本实例可以直接从Netty Example出找到源代码. 代码地址 TelnetServer.java

Telnet协议实现

  1. public class NettyServerExample {
  2. private static final StringDecoder DECODER = new StringDecoder();//解码器
  3. private static final StringEncoder ENCODER = new StringEncoder();//编码器
  4. //biz handler
  5. private static final TelnetServerHandler SERVER_HANDLER = new TelnetServerHandler();
  6. public static void main(String[] args) throws Exception {
  7. EventLoopGroup bossGroup = new NioEventLoopGroup(1);//boss线程
  8. EventLoopGroup workerGroup = new NioEventLoopGroup();//worker线程
  9. try {
  10. ServerBootstrap b = new ServerBootstrap();
  11. b.group(bossGroup, workerGroup)
  12. .channel(NioServerSocketChannel.class)
  13. .handler(new LoggingHandler(LogLevel.INFO))
  14. .childHandler(new ChannelInitializer<SocketChannel>() {
  15. protected void initChannel(SocketChannel socketChannel) {
  16. //todo 这里是重点 Channel初始化的时候添加handler,每一个channel都会注册这些
  17. ChannelPipeline pipeline = socketChannel.pipeline();
  18. // Add the text line codec combination first,
  19. pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
  20. // the encoder and decoder are static as these are sharable
  21. pipeline.addLast(DECODER);
  22. pipeline.addLast(ENCODER);
  23. // and then business logic.
  24. pipeline.addLast(SERVER_HANDLER);
  25. }
  26. });
  27. b.bind(8023).sync().channel().closeFuture().sync();
  28. } finally {
  29. bossGroup.shutdownGracefully();
  30. workerGroup.shutdownGracefully();
  31. }
  32. }
  33. }

biz handler

  1. @ChannelHandler.Sharable
  2. public class TelnetServerHandler extends SimpleChannelInboundHandler<String> {
  3. @Override
  4. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  5. // Send greeting for a new connection.
  6. ctx.write("Welcome to " + InetAddress.getLocalHost().getHostName() + "!\r\n");
  7. ctx.write("It is " + new Date() + " now.\r\n");
  8. ctx.flush();
  9. }
  10. @Override
  11. public void channelRead0(ChannelHandlerContext ctx, String request) throws Exception {
  12. // Generate and write a response.
  13. String response;
  14. boolean close = false;
  15. System.out.println("request:"+request);
  16. if (request == null) {
  17. response = "Please type something.\r\n";
  18. } else if ("bye".equals(request.toLowerCase())) {
  19. response = "Have a good day!\r\n";
  20. close = true;
  21. } else {
  22. response = "Did you say '" + request + "'?\r\n";
  23. }
  24. // We do not need to write a ChannelBuffer here.
  25. // We know the encoder inserted at TelnetPipelineFactory will do the conversion.
  26. ChannelFuture future = ctx.write(response);
  27. // Close the connection after sending 'Have a good day!'
  28. // if the client has sent 'bye'.
  29. if (close) {
  30. future.addListener(ChannelFutureListener.CLOSE);
  31. }
  32. }
  33. @Override
  34. public void channelReadComplete(ChannelHandlerContext ctx) {
  35. ctx.flush();
  36. }
  37. }

看着 Netty 一个简单的官方实例,对于 Netty 的使用会有一个大概的了解。基本结构就是使用Decoder和Encoder对TCP的字节流做反序列化,然后传递到后边的handler事件(双向链表) 触发完handler之后再返回(理论上是可有可无)对应数据.

这里暂不讨论 EventLoopGroup 和 EventLoop

基础知识 - 图1

常用Class

总结如下:

  • 每一个 ChannelHandler 会和一个 AbstractChannelHandlerContext 进行绑定,他们是 1对1 的关系.
  • 每一个 Channel 都会有多个 ChannelHandler ,这些ChannelHandlerChannel初始化的时候设置上去 ,详情请看 TelnetServerInitializer.java
  • 每一个 ChannelPipeline 都会将 AbstractChannelHandlerContext串联成链表,head和tail由netty分配, 通过 AbstractChannelHandlerContext 就可以将 ChannelHandler 串联起来,详情看下图
  • 每一个 ChannelPipeline 都会分配到一个 NioEventLoop 上,这个 EventLoop 可以自己传递进去,也可以是 worker 线程 (每一个NioEventloop都是单线程的),一般都是自己传递进去.

Note: channel 不和 NioEventLoop 进行绑定(但是实际上是有这个关系的),这个关系由 pipeline 维系

EventLoop

EventLoopGroup中的一个子线程(worker)会去处理绑定在自身的所有Channel发生的所有IO事件,避免了线程切换. 说起来比较绕,看下英文.

Will handle all the I/O operations for a Channel once registered,One EventLoop instance will usually handle more than one Channel but this may depend onimplementation details and internals

理解起来就是Channel(A)初始化的时候,会把这个A分配给一个EventLoop(B)进行绑定,后续这个A发生的所有IO事件都会交由B去完成(异步)

ChannelPipeline

使用 PipelineHandler 串连起来,形成一组处理器,将Netty串联是一个整体,非常核心的一个Class了

  1. I/O Request
  2. via {@link Channel} or
  3. {@link ChannelHandlerContext}
  4. |
  5. +---------------------------------------------------+---------------+
  6. | ChannelPipeline | |
  7. | \|/ |
  8. | +---------------------+ +-----------+----------+ |
  9. | | Inbound Handler N | | Outbound Handler 1 | |
  10. | +----------+----------+ +-----------+----------+ |
  11. | /|\ | |
  12. | | \|/ |
  13. | +----------+----------+ +-----------+----------+ |
  14. | | Inbound Handler N-1 | | Outbound Handler 2 | |
  15. | +----------+----------+ +-----------+----------+ |
  16. | /|\ . |
  17. | . . |
  18. | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|
  19. | [ method call] [method call] |
  20. | . . |
  21. | . \|/ |
  22. | +----------+----------+ +-----------+----------+ |
  23. | | Inbound Handler 2 | | Outbound Handler M-1 | |
  24. | +----------+----------+ +-----------+----------+ |
  25. | /|\ | |
  26. | | \|/ |
  27. | +----------+----------+ +-----------+----------+ |
  28. | | Inbound Handler 1 | | Outbound Handler M | |
  29. | +----------+----------+ +-----------+----------+ |
  30. | /|\ | |
  31. +---------------+-----------------------------------+---------------+
  32. | \|/
  33. +---------------+-----------------------------------+---------------+
  34. | | | |
  35. | [ Socket.read() ] [ Socket.write() ] |
  36. | |
  37. | Netty Internal I/O Threads (Transport Implementation) |
  38. +-------------------------------------------------------------------+

Channel、ChannelHandlerContext、Handler

Channel 类比于一个TCP连接,所有的IO操作都是异步的,结果 ChannelFuture 会立即返回,但是不保证成功.
Handler 是所有 Inbound HandlerOutBound Handler 的一个统称,使用 ChannelPipeline 将其串联成 双向链表 的数据结构
ChannelHandlerContext 是交互的上下文,它的核心就是鉴定当前 ChannelHandlerInbound 还是 Outbound ,已经将 prevnext 链接成串. 看下参数即明白

  1. volatile AbstractChannelHandlerContext next; 上一个AbstractChannelHandlerContext
  2. volatile AbstractChannelHandlerContext prev; 下一个AbstractChannelHandlerContext
  3. private final boolean inbound; 进站
  4. private final boolean outbound; 出战
  5. private final DefaultChannelPipeline pipeline; 流水线pipeline
  6. private final String name; handler 名字

总结

理解Filter有助于理解pipeline,理解了pipeline基本上就了解了它的整个流程。Intercepting Filter