天天看点

30分钟入门nettyBootstrapServerBootstrap

netty是一款用于网络通信的框架, 优点是异步、事件驱动型、高吞吐量、低延时; 支持大量应用层协议如FTP, SMTP, HTTP等, 简化了用户在客户端或者服务端的开发. 客户端开发使用netty提供的Bootstrap, 服务端可以使用ServerBootstrap.

文中涉及代码为netty 4.1.32.Final版本

Bootstrap和ServerBootstrap都继承AbstractBootstrap. 先讲Bootstrap. 因为netty是异步的事件驱动的,所以在代码的很多逻辑都是将一个个功能片段通过listener回调的方式连接起来的

Bootstrap

在写NIO客户端请求时,我们都知道无非就是: 创建SocketChannel连接服务器; 通过channel发送请求; 从channel中读取响应 这3部分; Bootstrap同样也提供这些功能的使用.

//1.连接服务器
Bootstrap:: public ChannelFuture connect(String inetHost, int inetPort)
返回的是future表示连接服务器可能还没处理成功;后续的发送请求操作需要通过
ChannelFuture.addListener(EventListener send)来处理

//2.发送请求 (发送成功一段时间后才会收到服务器响应)
Channel:: ChannelFuture writeAndFlush(Object msg);
返回依然是future, 可以在future成功后 通过添加listener来处理发送处理结束的事件.

//3.接受响应 
netty在读服务端响应是通过在pipeline中添加handler来处理的.为什么?
  因为数据可读是一个事件,netty获取到这个事件后会通知对应channel 通过pipeline进行处理
           

既然返回的是future说明在调用这些方法时用到了别的线程, 这就引出netty中EventLoopGroup事件处理器(可以把它类比为一个线程池)

EventLoopGroup

看下AbstractBootstrap的主要属性如下: 

//处理线程池
volatile EventLoopGroup group;
    //创建channel
private volatile ChannelFactory<? extends C> channelFactory;
    private volatile SocketAddress localAddress;
    //channel初始化使用
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
    //channel初始化使用
private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>();
    //channel初始化使用 加到channel的pipeline中
private volatile ChannelHandler handler;
           

先看下图1, 图中红色标注的是持有成员属性

30分钟入门nettyBootstrapServerBootstrap

图1

如上图每个MultithreadEventLoopGroup都对应持有children的一组自己工作线程, 对应关系如下

group worker 备注
NioEventLoopGroup NioEventLoop 使用select多路复用
EpollEventLoopGroup EpollEventLoop 只用于linux
KQueueEventLoopGroup KQueueEventLoop 只用于BSD
图1中的readOnlyChildren供迭代器使用; worker只做2件事:一,响应网络事件;二,处理提交进来的task. 提交到worker中需要延时的task通过schedule方法提交到scheduledTaskQueue中(比如超时处理的task); 不需延时的通过addTask方法提交到taskQueue中.      

在MultithreadEventExecutorGroup的构造函数中会实例化children对应的worker数组. 此时没为worker创建对应线程, 线程的创建是在第一次往worker中提交task时候.然后worker线程循环处理上面的2件事.

Bootstrap中的channelFactory是用于创建channel; 为了逻辑完整性以下以NioSocketChannel来说明.

NioSocketChannel 

先看一下图2, 

30分钟入门nettyBootstrapServerBootstrap

图2

如上图nioSocketChannel持有部分属性:

NioSocketChannelConfig(config)
SocketChannelImpl(ch):  
DefaultChannlePipeline(pipeline):持有一个head-tail的双向链表 用于事件处理
eventLoop: 在nioSocketChannel注册selector时候,从eventloopGroup中找一个worker用于执行对此channel上的所有操作
selectionKey:注册返回的key
      

pipeline对事件的处理如图2:

30分钟入门nettyBootstrapServerBootstrap

图2

inbound事件: 处理顺序是从head到tail

outbound事件: 处理顺序是从tail到head

请求过程

以上的背景了解我们重新来看一下bootstrap请求的过程, 以NioEventLoopGroup为例:

1.连接服务器 bootstrap::connect 并为返回的future, 为future添加connectionListener(用于发送请求)--MARK1

 1.1. channel创建和注册: AbstractBootstrap::initAndRegister(): a:从NioEventLoopGroup中获取一个nioEvenloop(worker)关联到channel中; b:将channel注册到selector上(b是作为一个task有worker来执行的) --此后channel上的所以事情都是有此task线程来处理

1.2.为initAndRegister()返回的channelFuture添加listener: listener中创建一个连接服务器的task(执行bootstrap::doConnect操作)放到evenloop的taskQueue中.  

1.3. eventloop执行bootstrap::doConnect的task 调用channel::connect() 通过pipeline的tail到head处理 然后再到channel::doConnect进行连接服务器操作; 期间创建了一个超时处理的task到worker的scheduledTask中.

2. worker处理完连接服务器task后开始处理IO响应发现连接成功

2.1. 通过连接成功的listener 去发送请求信息 (和上文MARK1对应)  --MARK2

2.2. 在pipeline的fireChannelActive()新增selectKey的READ属性

2.3. 取消掉上面那个超时任务

3. worker在后续的select, 发现有数据可读事件; 通过channel的pipeline(从head到tail方向)读取服务端的返回信息.

可以通过在pipeline最后加一inbound事件处理器(持有一个future成员属性, future在业务调用发送请求时候返回给业务 提供业务一个响应回调处理的切入口), 事件处理器响应channelRead(或者channelReadComplete)事件回调业务逻辑callback通知业务. inbound事件处理器的添加可以在处理MARK2发送请求时候添加到pipeline中.

从上面的处理过程中我们可以看到netty提供2个供开发者介入的地方:

  1. 获取future, 通过添加listener来对future成功/失败作出处理
  2. 向pipeline中添加事件处理器,来监听处理关注的事件

ServerBootstrap

我们用NIO写服务端的时

  1. 先创建ServerSocketChannel并把serverChannel绑定到端口;
  2. 然后建serverChannel注册到selector上;
  3. 再循环select操作获取IO事件并对IO事件进行处理: serverChannel监听到客户端连接请求,创建和客户端通信的channel,将channel也注册到selector上; 获取channel的读写事件进行IO的数据读写

同样ServerBootstrap(使用NioEventLoopGroup时)也同样是这样的逻辑. ServerBootstrap有2个EventLoopGroup成员属性: group和childGroup:

  • group 给serverChannel使用, 监听处理连接请求事件, 有连接建立时通过ServerBootstrapAcceptor(是serverChannel初始化时候驾到pipeline中的)委托childGroup去处理后续的和客户度通信的请求/响应事务
  • childGroup 用于客户端读写channel的处理     

可以看到ServerBootstrap只不过将事情分给了两组EventLoopGroup去处理而已. 在逻辑处理上和Bootstrap基本相同, 因为2者都继承AbstractBootstrap, 主要的功能都是AbstractBootstrap的实现, 就不再赘述.