zoukankan      html  css  js  c++  java
  • netty入门03

    目录(?)[+]

    这篇文章接着上一篇,分析一下Netty4的ServerBootstrp是如何工作的。

    EchoServer

    先看看Netty自带的EchoServer例子:

    1. /** 
    2.  * Echoes back any received data from a client. 
    3.  */  
    4. public class EchoServer {  
    5.   
    6.     private final int port;  
    7.   
    8.     public EchoServer(int port) {  
    9.         this.port = port;  
    10.     }  
    11.   
    12.     public void run() throws Exception {  
    13.         // Configure the server.  
    14.         EventLoopGroup bossGroup = new NioEventLoopGroup();  
    15.         EventLoopGroup workerGroup = new NioEventLoopGroup();  
    16.         try {  
    17.             ServerBootstrap b = new ServerBootstrap();  
    18.             b.group(bossGroup, workerGroup)  
    19.              .channel(NioServerSocketChannel.class)  
    20.              .option(ChannelOption.SO_BACKLOG, 100)  
    21.              .handler(new LoggingHandler(LogLevel.INFO))  
    22.              .childHandler(new ChannelInitializer<SocketChannel>() {  
    23.                  @Override  
    24.                  public void initChannel(SocketChannel ch) throws Exception {  
    25.                      ch.pipeline().addLast(  
    26.                              //new LoggingHandler(LogLevel.INFO),  
    27.                              new EchoServerHandler());  
    28.                  }  
    29.              });  
    30.   
    31.             // Start the server.  
    32.             ChannelFuture f = b.bind(port).sync();  
    33.   
    34.             // Wait until the server socket is closed.  
    35.             f.channel().closeFuture().sync();  
    36.         } finally {  
    37.             // Shut down all event loops to terminate all threads.  
    38.             bossGroup.shutdownGracefully();  
    39.             workerGroup.shutdownGracefully();  
    40.         }  
    41.     }  
    42.   
    43.     public static void main(String[] args) throws Exception {  
    44.         int port;  
    45.         if (args.length > 0) {  
    46.             port = Integer.parseInt(args[0]);  
    47.         } else {  
    48.             port = 8080;  
    49.         }  
    50.         new EchoServer(port).run();  
    51.     }  
    52. }  

    可以看出,用法和Bootstrap差不多。

    作为Builder的ServerBootstrap

    1. public final class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {  
    2.   
    3.     private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>();  
    4.     private final Map<AttributeKey<?>, Object> childAttrs = new LinkedHashMap<AttributeKey<?>, Object>();  
    5.     private volatile EventLoopGroup childGroup;  
    6.     private volatile ChannelHandler childHandler;  
    7.     // ...  
    8. }  

    看代码可以知道,ServerBootstrap比它的超类多了四个Part,如下图所示:

    从bind()方法入手

    bind()方法实际上是在AbstractBootstrap里定义的,bind()先调用validate()方法检查各个Part是否准备就 绪,然后把工作交给了doBind()方法。doBind()方法首先调用initAndRegister()方法,然后把工作交给doBind0()。 initAndRegister()会调用模板方法init()来初始化Channel,initAndRegister()方法的细节上篇文章分析过了,这里不再复述。下面是整个方法调用过程的示意图:


    init()方法

    1. @Override  
    2. void init(Channel channel) throws Exception {  
    3.     final Map<ChannelOption<?>, Object> options = options();  
    4.     synchronized (options) {  
    5.         channel.config().setOptions(options);  
    6.     }  
    7.   
    8.     final Map<AttributeKey<?>, Object> attrs = attrs();  
    9.     synchronized (attrs) {  
    10.         for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {  
    11.             @SuppressWarnings("unchecked")  
    12.             AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();  
    13.             channel.attr(key).set(e.getValue());  
    14.         }  
    15.     }  
    16.   
    17.     ChannelPipeline p = channel.pipeline();  
    18.     if (handler() != null) {  
    19.         p.addLast(handler());  
    20.     }  
    21.   
    22.     final EventLoopGroup currentChildGroup = childGroup;  
    23.     final ChannelHandler currentChildHandler = childHandler;  
    24.     final Entry<ChannelOption<?>, Object>[] currentChildOptions;  
    25.     final Entry<AttributeKey<?>, Object>[] currentChildAttrs;  
    26.     synchronized (childOptions) {  
    27.         currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));  
    28.     }  
    29.     synchronized (childAttrs) {  
    30.         currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));  
    31.     }  
    32.   
    33.     p.addLast(new ChannelInitializer<Channel>() {  
    34.         @Override  
    35.         public void initChannel(Channel ch) throws Exception {  
    36.             ch.pipeline().addLast(new ServerBootstrapAcceptor(  
    37.                     currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));  
    38.         }  
    39.     });  
    40. }  


    ServerBootstrap类的init()方法虽然很长,但是却不难理解。首先是给Channel设置options和attrs,然后把User 提供的针对NioServerSocketChannel的Handler添加到Pipeline的末尾。接下来复制childOptions和 childAttrs,最后实例化一个ChannelInitializer,添加到Pipeline的末尾。init()方法执行完毕之 后,AbstractBootstrap的initAndRegister()方法会将NioServerSocketChannel注册到group 里。到此为止,NioServerSocketChannel的状态如下图所示:

    ChannelInitializer在channelRegistered事件触发后会调用initChannel()方法,随后把自己从Pipeline里删除:

    1. @Sharable  
    2. public abstract class ChannelInitializer<C extends Channel> extends ChannelInboundHandlerAdapter {  
    3.   
    4.     private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChannelInitializer.class);  
    5.   
    6.     /** 
    7.      * This method will be called once the {@link Channel} was registered. After the method returns this instance 
    8.      * will be removed from the {@link ChannelPipeline} of the {@link Channel}. 
    9.      * 
    10.      * @param ch            the {@link Channel} which was registered. 
    11.      * @throws Exception    is thrown if an error occurs. In that case the {@link Channel} will be closed. 
    12.      */  
    13.     protected abstract void initChannel(C ch) throws Exception;  
    14.   
    15.     @SuppressWarnings("unchecked")  
    16.     @Override  
    17.     public final void channelRegistered(ChannelHandlerContext ctx)  
    18.             throws Exception {  
    19.         boolean removed = false;  
    20.         boolean success = false;  
    21.         try {  
    22.             initChannel((C) ctx.channel());  
    23.             ctx.pipeline().remove(this);  
    24.             removed = true;  
    25.             ctx.fireChannelRegistered();  
    26.             success = true;  
    27.         } catch (Throwable t) {  
    28.             logger.warn("Failed to initialize a channel. Closing: " + ctx.channel(), t);  
    29.         } finally {  
    30.             if (!removed) {  
    31.                 ctx.pipeline().remove(this);  
    32.             }  
    33.             if (!success) {  
    34.                 ctx.close();  
    35.             }  
    36.         }  
    37.     }  
    38. }  


    所以注册之后的NioServerSocketChannel状态如下图所示:

    ServerBootstrapAcceptor

    1. private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {  
    2.   
    3.     private final EventLoopGroup childGroup;  
    4.     private final ChannelHandler childHandler;  
    5.     private final Entry<ChannelOption<?>, Object>[] childOptions;  
    6.     private final Entry<AttributeKey<?>, Object>[] childAttrs;  
    7.   
    8.     @SuppressWarnings("unchecked")  
    9.     ServerBootstrapAcceptor(  
    10.             EventLoopGroup childGroup, ChannelHandler childHandler,  
    11.             Entry<ChannelOption<?>, Object>[] childOptions, Entry<AttributeKey<?>, Object>[] childAttrs) {  
    12.         this.childGroup = childGroup;  
    13.         this.childHandler = childHandler;  
    14.         this.childOptions = childOptions;  
    15.         this.childAttrs = childAttrs;  
    16.     }  
    17.   
    18.     @Override  
    19.     @SuppressWarnings("unchecked")  
    20.     public void channelRead(ChannelHandlerContext ctx, Object msg) {  
    21.         Channel child = (Channel) msg;  
    22.   
    23.         child.pipeline().addLast(childHandler);  
    24.   
    25.         for (Entry<ChannelOption<?>, Object> e: childOptions) {  
    26.             try {  
    27.                 if (!child.config().setOption((ChannelOption<Object>) e.getKey(), e.getValue())) {  
    28.                     logger.warn("Unknown channel option: " + e);  
    29.                 }  
    30.             } catch (Throwable t) {  
    31.                 logger.warn("Failed to set a channel option: " + child, t);  
    32.             }  
    33.         }  
    34.   
    35.         for (Entry<AttributeKey<?>, Object> e: childAttrs) {  
    36.             child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());  
    37.         }  
    38.   
    39.         try {  
    40.             childGroup.register(child);  
    41.         } catch (Throwable t) {  
    42.             child.unsafe().closeForcibly();  
    43.             logger.warn("Failed to register an accepted channel: " + child, t);  
    44.         }  
    45.     }  
    46.     // ...  
    47. }  


    ServerBootstrapAcceptor在channelRead事件触发的时候(也就有客户端连接的时候),把childHandler加到 childChannel Pipeline的末尾,设置childHandler的options和attrs,最后把childHandler注册进childGroup,如下 图所示:

  • 相关阅读:
    windows环境配置多个tomcat
    navicat 12 破解
    Eclipse创建Maven报异常:Could not get the value for parameter encoding for plugin......
    tomcat设置日志打印到文件中
    修改mysql数据库的休眠时间
    spring的bean标签的常用属性
    redis 安装与下载(windows版本)
    mysql设置远程可访问
    WPF非轮询方式更新数据库变化SqlDependency(数据库修改前台自动更新)
    WPF实战案例-在线程内同步集合数据到UI线程
  • 原文地址:https://www.cnblogs.com/xdrlczj/p/5846067.html
Copyright © 2011-2022 走看看