Netty线程模型和核心概念

一 传统阻塞线程模型

在这里插入图片描述

第一:服务器端有一个Acceptor线程接收客户端请求
第二:Acceptor接收到每一个客户端请求后,为每一个线程分配一个线程处理客户端请求

缺点:
第一:当数据量很大或者客户端很多的时候,就会创建大量的线程对象
第二:连接建立后,没有数据可读,会一直处于阻塞状态

二 Reactor单线程模型

在这里插入图片描述

第一:Reactor对象通过Selector(多路复用器)监控客户端请求事件
第二: 收到事件后,进行分发
第三:如果是建立连接的事件则交给Acceptor,通过accept处理连接请求,然后创建一个Handler对象处理连接完成后的后续业务处理;如果不是建立连接则直接调用Handler来处理

缺点:
单个线程要负责连接、读写请求,如果客户端连接数量太多,则会无法发挥CPU多核的性能,容易造成性能瓶颈

三 Reactor多线程模型

在这里插入图片描述

第一:Reactor线程通过Selector多路复用器监听客户端事件
第二:收到事件后,进行转发
第三:如果是建立连接的请求,则通过Acceptor线程的accept处理连接,然后创建一个Handler对象处理后续的请求;如果不是连接请求则由Reactor分发,调用Handler来处理
第四:Handler不会自己处理具体的读写请求,而是直接提交给一个线程池去执行,然后就可以返回了,然后由线程池获取一个线程执行这个请求

优点:
可以充分利用多核CPU处理能力
缺点:
Reactor要处理所有的事件监听和响应,是单线程运行,并发量大的时候,容易出现性能瓶颈

四 主从Reactor多线程模型

在这里插入图片描述

第一:MainReactor通过通过多路复用器,监听客户端连接请求,然后调用Acceptor的accept处理连接请求

第二:Acceptor处理连接事件,然后Main Reactor将连接分配给SubReactor
第三:SubReactor将连接加入到连接队列并监听,然后创建Handler处理各种事件
第四:Handler不处理具体的业务逻辑,而是将请求或者事件提交给线程池去处理
第五:线程池分配线程处理业务逻辑,并返回结果

五 Netty模型

5.1 Netty模型图

在这里插入图片描述

第一: 有两个线程池(BossGroup和WorkerGroup)
Boss Group: 专门负责接收客户端连接
WorkerGroup:专门负责网络的读写

第二:BossGroup和WorkerGroup都是NioEventLoopGroup
第三:NioEventLoopGroup相当于一个事件循环组,这个组包括多个事件循环,即NioEventLoop
第四:NioEventLoop表示一个不断循环的执行处理任务的线程,每一个NioEventLoop都有一个Selector(事件监听器),监听绑定在通道上的对应socket的网络通信,并且还可以从任务队列执行任务
第五:NioEventLoopGroup包含多少个NioEventLoop数量是可以指定的
第六:BossGroup执行流程
#1 轮询accept事件,与客户端建立连接,生成一个NioSocketChannel
#2 将NioSocketChannel注册到Worker Group上的selector
#3 处理任务队列里的任务
第七:Worker Group的执行流程
#1 轮询read/write事件
#2 读取read、write事件,在对应NioSocketChannel上读写
#3 处理任务队列的其他任务
第八:可以将NioSocketChannel放入管道Pipeline,Pipeline可以包含多个通道处理器ChannelHandler对通道进行处理,也就是意味着Pipeline中可以获取通道

5.2 Pipeline和Channel以及Handler和ChannelHandlerContext之间是什么关系

5.2.1 Channel

通道即连接,目标与源打开的连接,比如SocketChannel,计算机之间建立的连接,可以进行通信;FileChannel表示应用程序和文件之间的连接,通道一般会提供一些能力,方便操作

5.2.2 ChannelHandler(通道处理器)

有时候我们需要对通道中的数据进行拦截或者处理,可能存在多个地方需要拦截处理,所以Netty提供了ChannelHandler来处理通道数据,如果有多个处理器以何种顺序来处理通道数据呢?则ChannelHandler是以链表形式存在,通道中每一个数据都会经历处理器链上的处理器进行数据处理。简单的说, 处理通道的I/O事件,并且可以将I/O事件传递到ChannelPipeline中下一个ChannelHandler。
既然是请求,那就有响应的问题,所以通道处理器可以处理进来的请求,也可以处理出去出去的响应,因此ChannelHandler根据进出关系 分为ChannelInboudHandler和ChannelOutboundHandler

5.2.3 ChannelPipeline(管道)

有很多的ChannelHandler我们知道可以以链表形式存在,但是并不是每一个通道都需要使用所有的ChannelHandler, 所以Netty中Channel通常和Pipeline是一对一的关系,即一个通道对应一个管道,该通道只能这个管道里的处理器ChannelHandler处理,不在这个管道里的则不处理

5.2.4 ChannelHandlerContext

ChannelHandlerContext是ChannelHandler上下文,保存了很多相关的数据信息,比如当前通道是哪一个,管道是哪一个,上一个处理器是谁,下一个处理器是什么?
并且可以将拦截的事件传递到下一个处理器处理

5.3 任务队列使用场景

有些处理器对通道数据可能处理时间很长,影响性能,则将这些任务扔到队列中异步执行。

5.3.1 用户自定义普通任务

public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    // 用户自定义普通任务来完成
    // ctx.channel().eventLoop() 拿到的是同一个线程,比如:nioEventLoopGroup-3-1
    ctx.channel().eventLoop().execute(new Runnable() {
        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName());
                Thread.sleep(10 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ctx.writeAndFlush(Unpooled.copiedBuffer("您好, 客户端!任务队列测试~", CharsetUtil.UTF_8));
        }
    });
}

5.3.2 用户自定义定时任务

public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
   // 用户自定义定时任务,任务存放队列shceduleTaskQueue
    ctx.channel().eventLoop().schedule(new Runnable() {
        @Override
        public void run() {
            ctx.writeAndFlush(Unpooled.copiedBuffer("您好, 客户端!定时任务队列测试~",CharsetUtil.UTF_8));
        }
    },5, TimeUnit.SECONDS);
}

5.3.3 非当前Reactor线程的调用Channel的各种方法

5.4 Future和ChannelFuture

Future代表一个异步任务结果,并可以根据结果判断任务是否完成等,ChannelFuture是Netty中的概念,对Future进行了进一步扩展,比如可以添加监听器,监听事件是否发生,如果发生,则触发监听器中的业务逻辑。比如添加监听器:
future = bootstrap.bind(6666).sync();
// 判断绑定端口是否成功

future.addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture cf) throws Exception {
        if (cf.isSuccess()) {
            System.out.println("监听端口6666成功!");
        } else {
            System.out.println("监听端口6666失败!");
        }
    }
});

5.5 NIO的ByteBuffer和Netty的ByteBuf区别

ByteBuffer的position并没有区分读写位置,所以写完了需要读取的时候需要flip切换读写模式;但是ByteBuf维护了readerIndex和writerIndex两种索引,所以不需要flip就可以进行读写。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莫言静好、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值