Netty源码详解

线程组初始化

EventLoopGroup bossGroup = new NioEventLoopGroup(1); 
EventLoopGroup workerGroup = new NioEventLoopGroup(1);

流程如下:

Created with Raphaël 2.2.0 开始 io.netty.util.concurrent.MultithreadEventExecutorGroup #MultithreadEventExecutorGroup(int, java.util.concurrent.Executor, java.lang.Object...) 创建线程组 chooser = new PowerOfTwoEventExecutorChooser(); 或者 chooser = new GenericEventExecutorChooser(); 创建选择器,用来选择下一个处理任务的线程 io.netty.channel.nio.NioEventLoopGroup #newChild 为线程组创建线程NioEventLoop for (EventExecutor e: children) { e.terminationFuture().addListener(terminationListener);} 为线程池中每一个线程都添加同一个监听器,监听线程的消亡

Netty启动器初始化


ServerBootstrap b = new ServerBootstrap(); 
b.group(bossGroup, workerGroup)
 .channel(NioServerSocketChannel.class) 
 .childHandler(new ChannelInitializer<SocketChannel>() { 
     @Override
     public void initChannel(SocketChannel ch) throws Exception {
          ch.pipeline().addLast(new ChannelHandler());
     }
 })
 .option(ChannelOption.SO_BACKLOG, 128)          
 .childOption(ChannelOption.SO_KEEPALIVE, true); 

// Bind and start to accept incoming connections.
ChannelFuture f = b.bind(port).sync(); 

b.bind(port)为启动命令,前面的都是配置项,流程如下:

Created with Raphaël 2.2.0 开始 io.netty.bootstrap.AbstractBootstrap#doBind 端口绑定 io.netty.bootstrap.AbstractBootstrap#initAndRegister 初始化和注册 io.netty.bootstrap.ServerBootstrap#init 系统初始化 pipeline.addLast(new ServerBootstrapAcceptor( currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)); 给父线程组的管道的责任链添加一个处理器: 这个处理器会将接受到的消息注册到子线程组的selector进行业务处理 io.netty.channel.MultithreadEventLoopGroup#register(io.netty.channel.Channel) 将channel注册到父线程组中的一个子线程的selector中 io.netty.bootstrap.AbstractBootstrap#doBind0 端口绑定 结束

    流程图中有一个:将channel注册到父线程组中的一个子线程的selector中,其实一开始不是很理解,明明是一个线程池(意味着可以有多个子线程),那为什么,channel只绑定到其中一个子线程的selector上呢,那其他的子线程做什么用呢?还是说我哪里的理解的有问题,后来看到网上有这么一段说法确认了没有看错源码,解释如下:绑定一个端口,就会把链接这个端口的NioServerSocketChannel注册到一个boss线程池中的某一个子线程的selector上,如果同时绑定多个端口,那此时boss线程池中的其他子线程就用处了,然后我尝试了一下:

        ChannelFuture f = b.bind(8088).sync(); // (7)
        ChannelFuture f2 = b.bind(8089).sync(); // (7)

通过这样让一个服务端同时绑定了8088端口和8089端口,此时通过http访问,两个端口都是通的,也就是一个Netty服务端是可以监听多个端口的。

Netty运行

    Netty的运行步骤,其实和多reactor多线程模型大同小异,首先boss线程池中的子线程开启一个死循环,进行监听来自客户端的请求,流程如下:

Created with Raphaël 2.2.0 开始 io.netty.channel.nio.NioEventLoop#select boss线程的selector进行select io.netty.channel.nio.NioEventLoop#processSelectedKeys boss进行数据处理 io.netty.channel.nio.AbstractNioMessageChannel.NioMessageUnsafe#read 读取数据 io.netty.channel.DefaultChannelPipeline#fireChannelRead boos线程进入责任链 io.netty.bootstrap.ServerBootstrap.ServerBootstrapAcceptor#channelRead 这个方法中会执行这么一段代码: childGroup.register(child).addListener(new ChannelFutureListener() 把数据的具体处理注册给了worker线程池中的某一个子线程的selector

boss线程池中的线程监听来自客户端的请求,并把具体的处理工作又注册给了worker线程池中的线程,然后自己再次监听客户端的请求。而worker线程池中的线程同样进入一个死循环,不断监听boss线程池注册过来的任务。

Created with Raphaël 2.2.0 开始 io.netty.channel.nio.NioEventLoop#select work线程的selector进行select io.netty.channel.nio.NioEventLoop#processSelectedKeys work进行数据处理 io.netty.channel.nio.AbstractNioMessageChannel.NioMessageUnsafe#read 读取数据 io.netty.channel.DefaultChannelPipeline#fireChannelRead work线程进入责任链,依次执行责任链中的工作,包括我们自定义加入到责任链中的任务 io.netty.util.concurrent.SingleThreadEventExecutor #runAllTasks()或者 io.netty.util.concurrent.SingleThreadEventExecuto r#runAllTasks(long) 需要根据设定的ioRatio来判断,任务执行的时间,如果ioRatio为100,则执行runAllTasks,默认为50 这里是会从线程中的一个任务队列执行一些其他任务

其实我们可以看到boss线程池中的线程和work线程池中的线程做的事情其实差不多,除了责任链,boss的责任链是Netty自己加了往worker线程池注册任务的一环,而work的责任链则是涉及到具体的业务逻辑了,所以我们可以通过增加责任链的环节来进行不同的处理。

Netty结构

结构图
    ServerBootStrap就是Netty的启动器,一个Netty服务所有内容都在里面,EventLoopGroup则是线程组,ServerBootStrap中有两个线程组,一个boss负责监听客户端连接,一个worker负责具体的数据接收和处理。NioEventLoop为线程组中的子线程,一个EventLoopGroup中可以有很多个NioEventLoop,根据最开始线程池初始化里设置决定的。ChannelPipeline为责任链容器,每对一个channel进行数据处理时就会产生一个ChannelPipeline,所以NioEventLoop中也会有多个ChannelPipeline,但是同一时刻,一个NioEventLoop中只会有一个ChannelPipeline。最后的ChannelHandler就是责任链中的每一环被称作事件,一个责任链上可以有很多个事件。

责任链模式

    上面多处说到责任链,其实这是一种设计模式,就是将处理流程设计成一个链条,每个处理操作都是这个链条中的一环,还是比较好理解的。这种模式能有效的降低业务中各个操作的耦合性。Netty的一条责任链中有一个分为两类事件,一种为入站事件,一种为出站事件。只有数据从客户端进入服务器的过程才会触发入栈事件,同样只有只有数据从服务端返回客户端的时候才会触发出栈事件。

ch.pipeline().addLast(new ChannelHandler());

我们上面初始化的代码中就这么一行,其实就是在责任链中加入一个我们自定义的事件。
    Netty的责任链有头和尾,数据进入服务器时从头部往尾部依次执行,数据从服务端返回客户端时,从尾部往头部依次执行。
责任链执行

另外Netty的责任链通过在每一个事件中调用next()来执行下一个事件,所以如果在某一个实践中没有手动继续往下执行,那么责任链就不会执行剩下的事件了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值