zoukankan      html  css  js  c++  java
  • 【转】Netty源码的阅读学习

    Netty 源码阅读学习

    博客分类:
     

    背景 

    最忌工作中接触到Netty相关应用场景,之前看过mima的部分源码,所以最近看了Netty的部分源码和学习其设计思想,做个简单的分享(学习代码为:Netty:3.6.3.FINALE)。

    Netty概述

    官方:Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.

    特点:和Mina,Grizzly一样都是基于nio的通信框架,事件驱动,异步非阻塞,高性能,高可靠性。

    Mina和Neety的主导作者都是Trustin Lee. Mina是apache社区提供的一个基于NIO的高性能网络应用程序框架,Trustin Lee离开apache后加入redhat开启了Netty,所以两者的设计思路基本一致,目前Netty的更新更加频繁,文档也比较全。下图为 netty官网上的一张体系结构图。



     

    源码阅读:

    先从Netty的ServerBootstrap初始化到bind()过程梳理

    Java代码  收藏代码
    1. bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(serverBossTF),  
    2.                 Executors.newCachedThreadPool(serverWorkerTF)));  
    3.   
    4. bootstrap.setPipelineFactory(new ChannelPipelineFactory() {  
    5.        public ChannelPipeline getPipeline() throws Exception {  
    6.               ChannelPipeline pipeline = new DefaultChannelPipeline();  
    7.               pipeline.addFirst("firewall", firewall);  
    8.               pipeline.addLast("decoder", new NettyProtocolDecoder());  
    9.               pipeline.addLast("encoder", new NettyProtocolEncoder());  
    10.               pipeline.addLast("handler", serverHandler);  
    11.               return pipeline;  
    12.        }  
    13. });  
    14.   
    15. bootstrap.bind(new InetSocketAddress(bindHost, listenPort));  

    以上三步为:

    1. 初始化ServerBootstrap,指定其ChannelFactory为NioServerSocketChannelFactory(用来创建NioServerSocketChannel)

    2. 为bootstrap指定了PipelineFactory,所有Accepted Channel在创建时会使用该工厂创建一个pipeline处理事件。最后的serverHandler为自定义实现的Handler,最终负责处理请求相关业务逻辑等。

    3. 绑定一个端口监听请求

    从上面三步可以看到Netty的使用还是比较简单,可以自定义的扩展pipeline和实现自己的Handler。

    整个过程屏蔽复杂的nio和多线程并发处理的细节,所以最近通过阅读源码了解Netty的部分实现。

    NioServerSocketChannelFactory关键构造方法:

    Java代码  收藏代码
    1. public NioServerSocketChannelFactory(BossPool<NioServerBoss> bossPool, WorkerPool<NioWorker> workerPool) {  
    2.        if (bossPool == null) {  
    3.               throw new NullPointerException("bossExecutor");  
    4.        }  
    5.        if (workerPool == null) {  
    6.               throw new NullPointerException("workerPool");  
    7.        }  
    8.        this.bossPool = bossPool;  
    9.        this.workerPool = workerPool;  
    10.        sink = new NioServerSocketPipelineSink();  
    11. }  

     bossPool 通过new NioServerBossPool(bossExecutor, bossCount, null)创建,这里bossCount默认为1,这里的boss线程通过new NioServerBoss(executor, determiner)创建,在这里完成Selector.open();主要会处理accept事件。

    workerPool 通过new NioWorkerPool(workerExecutor, workerCount)创建,workerCount为当前可以cpu数 ×2(Runtime.getRuntime().availableProcessors() * 2 ),通过new NioWorker(executor, determiner)创建,每个worker持有一个Selector;主要处理数据读写操作。

    sink NioServerSocketPipelineSink 接收并处理下行(downstream)末端的ChannelEvent,主要负责执行最终ServerSocketChannel绑定以及注册OP_ACCEPT到Selector上。

    接下来看bootstrap.bind()

    Java代码  收藏代码
    1. // ServerBootstrap.java --> bindAsync()  
    2. // 初始一个Binder,Binder继承自SimpleChannelUpstreamHandler,接收并处理上行的ChannelEvent  
    3. Binder binder = new Binder(localAddress);  
    4. // 初始化一个bossPipeline,之前初始化那个pipeline是工作线程中用的  
    5. ChannelPipeline bossPipeline = pipeline();  
    6. // 将binder放入pipline中  
    7. bossPipeline.addLast("binder", binder);  
    8. ... ...  
    9. // 获取ServerBootstrap初始化时候初始的NioServerSocketChannelFactory来完成服务端channel创建,创建出的是一个NioServerSocketChannel  
    10. Channel channel = getFactory().newChannel(bossPipeline);  

     NioServerSocketChannel

    Java代码  收藏代码
    1. // 这是真正java nio的Channel,Netty包装了一层channel的概念  
    2. socket = ServerSocketChannel.open();  
    3. // 设置为非阻塞模式  
    4. socket.configureBlocking(false);  
    5. // 将open事件作为一个上行事件丢到pipeline中(由对应的handler来handle事件)   
    6. // 这里的handler从前面的初始化来看就是交给binder来处理,binder继承自SimpleChannelUpstreamHandler,可以处理上行事件  
    7. fireChannelOpen(this);  

     Binder --> channelOpen()

    Java代码  收藏代码
    1. // 执行Channel的bind, 在pipeline中发送一个BOUND的下行事件  
    2. evt.getChannel().bind(localAddress)  

     DefaultChannelPipeline

    Java代码  收藏代码
    1. // 这里的sink是初始化时上面提到的NioServerSocketPipelineSink  
    2. getSink().eventSunk(this, e);  

     NioServerSocketPipelineSink --> eventSunk() --> handleServerSocket()

    Java代码  收藏代码
    1. // 这里的boss是初始化时设置的 NioServerBoss  
    2. ((NioServerBoss) channel.boss).bind(channel, future, (SocketAddress) value);  

     在boss的bind方法中new了一个内部的RegisterTask来完成真正的ServerSocket.bind()及注册到selector上,并唤醒Selector

    NioServerBoss --> RegisterTask

    Java代码  收藏代码
    1. //java NIO ServerSocketChannel.bind()操作  
    2. channel.socket.socket().bind(localAddress, channel.getConfig().getBacklog());  
    3. bound = true;  
    4. // 更新future  
    5. future.setSuccess();  
    6. // 出发一个绑定成功的事件  
    7. fireChannelBound(channel, channel.getLocalAddress());  
    8. // 向selector中注册OP_ACCEPT事件  
    9. channel.socket.register(selector, SelectionKey.OP_ACCEPT, channel);  

     接下来boss线程和worker线程就要开始干活接客了

    NioServerBoss 继承自 AbstractNioSelector,其run方法:

    Java代码  收藏代码
    1. public void run() {  
    2.        ...  
    3.        for (;;) {  
    4.               ...  
    5.               // 执行 selector.select(SELECT_TIMEOUT)  
    6.               // 阻塞等待客户端连接  
    7.               int selected = select(selector);  
    8.               ....  
    9.                 
    10.               // 当请求到达唤醒select,处理具体的事件  
    11.               // 该抽象方法由NioServerBoss实现  
    12.               process(selector);  
    13.        }  
    14.        ...  
    15. }  
    16.   
    17. protected void process(Selector selector) {  
    18.        // 获取select出来的SelectionKey,逐个accept  
    19.        Set<SelectionKey> selectedKeys = selector.selectedKeys();  
    20.        for (Iterator<SelectionKey> i = selectedKeys.iterator(); i.hasNext();) {  
    21.               NioServerSocketChannel channel = (NioServerSocketChannel) k.attachment();  
    22.                 
    23.               // accept connections in a for loop until no new connection is ready  
    24.         for (;;) {  
    25.                      // NIO 的accept,建立和客户端之间的socket连接  
    26.                      SocketChannel acceptedSocket = channel.socket.accept();  
    27.                      ....  
    28.                      registerAcceptedChannel(channel, acceptedSocket, thread);  
    29.               }  
    30.               ....  
    31.        }  
    32. }  
    33.   
    34. //将accept成功的SocketChannel交给工作线程处理读写操作   
    35. private static void registerAcceptedChannel(NioServerSocketChannel parent, SocketChannel acceptedSocket,  
    36.        Thread currentThread) {  
    37.        ....  
    38.        ChannelSink sink = parent.getPipeline().getSink();  
    39.        ChannelPipeline pipeline =  
    40.                      parent.getConfig().getPipelineFactory().getPipeline();  
    41.        NioWorker worker = parent.workerPool.nextWorker();  
    42.        worker.register(new NioAcceptedSocketChannel(  
    43.                      parent.getFactory(), pipeline, parent, sink  
    44.                      , acceptedSocket,  
    45.                      worker, currentThread), null);  
    46.        ....  
    47. }  

    总结

    以上过程梳理了Netty初始化的主流程,以及NIO服务端的初始化过程(以上红色部分);顺序梳理完一遍源码,反过来总结下Netty的设计和结构。

    代码梳理过程中涉及的核心接口及作用

     

     

    Bootstrap:Netty框架启动的工具类,分client,server和udp3种,其核心功能是初始化主channel和pipeline。

    ChannelFactory:创建Channel的工厂。针对不同场景,netty提供了各种ChannelFactory,比如ServerChannelFactory用来创建server端的Channel。

    Channel:封装了java.nio.channels包中Channel的相关实现,封装了各种IO操作。

    ChannelEvent:Netty中几乎所有的操作,都是以事件(ChannelEvent)驱动。事件处理都是通过Channels类的静态 方法调用开始的,将事件、channel传递给 channel持有的Pipeline进行处理。ChannelEvent分为两种,upstream events和downstream events。Upstream events指由底层传到高层的事件,一般指socket层发生某些事件通知到应用程序,比如当socket接受到数据后会发送 UpstreamMessageEvent事件,交给ChannelHandler来处理。Downstream events指由高层传到底层的事件,一般指用户主动发起的操作,比如用户调用channel.write(buffer),则会生成 DownstreamMessageEvent事件,经过ChannelHandler处理后,交给socket层发起通信。

    ChannelHandler:真正的处理类,一般由应用程序自己实现。主要分2个子接口ChannelUpstreamHandler和ChannelDownstreamHandler,分别处理UpstreamEvent和DownstreamEvent。

    ChannelPipeline:就是由ChannelHandler(ChannelHanlderContext)组成的双向链表。如果是 upstream events,则经过ChannelPipeline中由前往后的所有ChannelUpstreamHandler依次处理,最后事件被丢弃。如果是 downstream events,则经过ChannelPipeline中由后往前的所有ChannelDownstreamHandler依次处理,最后事件被转交给 ChannelSink。

    ChannelSink:所有的downstream events经过ChannelPipeline处理后,最后由ChannelSink的eventSunk方法处理。它是管理IO线程和事件处理的桥梁。

    ChannelPipeline的处理流程(源码注释中的流程图)

     

     

    服务端初始化和接收请求抽象流程

     

     

    Netty中的Reactor模式

    使用Doug Lea大师在Scalable IO in Java中的一张图来说明:

     

     

    这里的mainReactor对应了Netty中的NioServerBoss(AbstractNioSelector.run()),被分配到bossPool中执行 ,selector.select(SELECT_TIMEOUT)阻塞。

    有客户端连接请求被唤醒后执行accept,然后通过worker.register() 交给subReactor的ThreadPool执行(注册OP_READ到NioWorker的Selector中);在netty里为 workerPool,其中Worker的线程数由WorkerPoo大小决定,从上面初始化代码中看到默认是可用cpu个数*2。

    当客户端有数据写入时,NioWorker持有的Selector被唤醒执行read事件,后会触发messageReceived事件,交给pipeline中的handler执行。

    这里事件驱动的关键Selector在不同的操作系统中有不同的实现,系统级的select和pool原理都是通过轮询内核中等待中的FD,轮询到 就绪的FD后写用户空间,然后唤醒阻塞的Selector;java1.5版本开始支持epoll,epoll则是在等待的FD上注册回调函数(真正的事 件驱动),根本的提升了NIO的性能。

    参考资源

    Netty官网:http://netty.io/

    Mina官网:http://mina.apache.org/

    Trustin Lee :LinkedIn: http://www.linkedin.com/in/trustin

    Doug Lea: Scalable IO in Java: http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf

    Netty高性能: http://www.infoq.com/cn/articles/netty-high-performance

    Netty线程模型:http://www.infoq.com/cn/articles/netty-threading-model

    Netty-Mina:http://ifeve.com/netty-mina-in-depth-1/

    Select,poll,epoll: http://www.cnblogs.com/xuxm2007/archive/2011/08/15/2139809.html

  • 相关阅读:
    #背包方案 ——整数划分(Acwing900)
    #分组背包 #背包方案 ——Acwing 1013 机器分配
    #背包 #二进制优化 ——Acwing 5. 多重背包问题 II(二进制优化)
    #背包方案 AcWing 532. 货币系统
    #背包方案 ——AcWing 1021. 货币系统2
    背包问题求方案数
    有依赖的背包问题
    分组背包问题
    二维费用的背包问题
    混合背包问题
  • 原文地址:https://www.cnblogs.com/xiaoying1245970347/p/5479691.html
Copyright © 2011-2022 走看看