`
weishiym
  • 浏览: 34455 次
  • 性别: Icon_minigender_1
  • 来自: 广西
社区版块
存档分类
最新评论

深入研究Netty之线程模型详解

 
阅读更多

本文主要介绍Netty线程模型及其实现,介绍Netty线程模型前,首先会介绍下经典的Reactor线程模型,目前大多数网络框架都是基于Reactor模式进行设计和开发,Reactor模式基于事件驱动,非常适合处理海量的I/O事件。下面简单介绍下Reactor模式及其线程模型。

Reactor模式

Reactor模式首先是事件驱动的,有一个或多个并发输入源,有一个Service Handler,有多个Request Handlers;这个Service Handler会同步的将输入的请求(Event)多路复用的分发给相应的Request Handler。下面先回顾下Reactor线程模型。

单线程模型

单线程模型下,所有的IO操作都由同一个Reactor线程来完成,其主要职责如下:

  • 作为服务端,接收客户端的TCP连接;
  • 作为客户端,向服务端发起TCP连接;
  • 读取通信对端的请求或者应答消息;
  • 向通信对端发送消息请求或者应答消息。

Reactor单线程模型原理图如下:

如图所示,由于Reactor模式使用的是异步非阻塞IO,所有的IO操作都不会导致阻塞。通常Reactor线程中聚合了多路复用器负责监听网络事件,当有新连接到来时,触发连接事件,Disdatcher负责使用Acceptor接受客户端连接,建立通信链路;当I/O事件就绪后,Disdatcher负责将事件分发到对应的event handler上负责处理。

该模型的缺点很明显,不适用于高负载、高并发的应用场景;由于只有一个Reactor线程,一旦挂彩,整个系统通信模块将不可用。

多线程模型

先看原理图:

该模型的特点:

  • 专门由一个Reactor线程-Acceptor线程用于监听服务端,接收客户端连接请求;
  • 网络I/O操作读、写等由Reactor线程池负责处理;
  • 一个Reactor线程可同时处理多条链路,但一条链路只能对应一个Reactor线程,这样可避免并发操作问题。

绝大多数场景下,Reactor多线程模型都可以满足性能需求,但是,在极个别特殊场景中,一个Reactor线程负责监听和处理所有的客户端连接可能会存在性能问题。例如并发百万客户端连接,或者服务端需要对客户端握手进行安全认证,但是认证本身非常损耗性能。因此,诞生了第三种线程模型。

主从多线程模型

先看原理图:

该模型的特点:

  • 服务端使用一个独立的主Reactor线程池来处理客户端连接,当服务端收到连接请求时,从主线程池中随机选择一个Reactor线程作为Acceptor线程处理连接;
  • 链路建立成功后,将新创建的SocketChannel注册到sub reactor线程池的某个Reactor线程上,由它处理后续的I/O操作。

Netty线程模型

Netty同时支持Reactor单线程模型 、Reactor多线程模型和Reactor主从多线程模型,用户可根据启动参数配置在这三种模型之间切换。Netty线程模型原理图如下:

服务端启动时,通常会创建两个NioEventLoopGroup实例,对应了两个独立的Reactor线程池。常见服务端启动代码实现如下:

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
    ServerBootstrap b = new ServerBootstrap();
    b.group(bossGroup, workerGroup)
     .channel(NioServerSocketChannel.class)
     .option(ChannelOption.SO_BACKLOG, 100)
     .handler(new LoggingHandler(LogLevel.INFO))
     .childHandler(new ChannelInitializer<SocketChannel>() {
         @Override
         public void initChannel(SocketChannel ch) throws Exception {
               ......

bossGroup负责处理客户端的连接请求,workerGroup负责处理I/O相关的操作,执行系统Task、定时任务Task等。

用户可根据服务端引导类ServerBootstrap配置参数选择Reactor线程模型,进而最大限度地满足用户的定制化需求;同时,为了最大限度地提升性能,netty很多地方采用了无锁化设计,如为每个Channel绑定唯一的EventLoop,这意味着同一个Channel生命周期内的所有事件都将由同一个Reactor线程来完成,这种串行化处理方式有效地避免了多线程操作之间锁的竞争和上下文切换带来的开销。此外,每个Reactor线程配备了一个task队列和Delay task队列,分别用于存放系统Task和周期性Task,也就是说每个Reactor线程不仅要处理I/O事件,还会处理一些系统任务和调度任务。

EventLoop家族

EventLoop又叫事件循环,旨在通过运行任务来处理在连接的生命周期内发生的事件。Netty的EventLoop是协同设计的一部分,主要采用了两个基本的API:并发和网络编程。首先,io.netty.util.concurrent包构建在JDK的java.util.concurrent包上,用来提供线程执行器。其次,io.netty.channel包中的类,为了与Channel的事件进行交互,扩展了io.netty.util.concurrent包中的接口和类。下面通过类图来说明:

类图

成员简介

首先从io.netty.util.concurrent开始:

EventExecutorGroup:字面含义是事件执行器组,管理着一组EventExecutor,负责通过其next()方法提供EventExecutor的使用,并负责管理这些EventExecutor的生命周期。EventExecutorGroup扩展了JDK的ScheduledExecutorService,使得其子类具有提交执行调度任务的能力;同时还扩展了Iterable,说明EventExecutorGroup是可迭代的。此外,EventExecutorGroup还定义了优雅退出的方法。

AbstractEventExecutorGroup:EventExecutorGroup的抽象实现,并没有提供新的API,只是简单的为EventExecutorGroup中定义的方法提供了默认实现。EventExecutorGroup本身并不能执行任务,它首先通过next()选择一个EventExecutor对象,然后将执行任务的工作都是委托给这个对象。换句话说,具体实现由EventExecutor的子类来完成。

MultithreadEventExecutorGroup:EventExecutorGroup的抽象实现,内部组合了多个EventExecutor用于对外提供服务,并负责管理一组EventExecutor实例。它还提供了抽象方法newChild用于构造EventExecutor实例,由子类提供实现,便于构造定制化的EventExecutor。它还聚合了一个EventExecutorChooser对象,用于定制通过next从数组中选择EventExecutor对象的规则。

DefaultEventExecutorGroup:EventExecutorGroup的默认实现,当需要使用DefaultEventExecutor来执行任务时,可使用该实现,比较少用。

EventExecutor:事件执行器,它是一个只使用一个线程来执行任务的特殊线程池,其扩展了EventExecutorGroup,主要是为了方便代理EventExecutorGroup中的方法;此外,EventExecutor中也定义一些自己的API,如:用于识别线程身份的方法inEventLoop,创建各种通知器Promise的方法。

AbstractEventExecutor:EventExecutor的抽象实现,其实现了EventExecutorGroup中的抽象方法,提交任务的方法委托给父类AbstractExecutorService来完成,但不支持提交调度任务,调用schedule相关方法都会抛出UnsupportedOperationException。此外,为了实现iterator,其内部定义了一个只能包含一个元素的Collection,且这个元素就是当前EventExecutor实例,因此迭代AbstractEventExecutor只会返回自身实例。用户可直接调用next方法,默认也是返回当前EventExecutor实例,更方便快捷。

AbstractScheduledEventExecutor:EventExecutor的抽象实现,主要为了支持调度任务的执行。其持有一个PriorityQueue用于存放调度任务,并实现了ScheduledExecutorService中定义的提交调度任务的方法。

OrderedEventExecutor:它是一个标识接口,没有任何方法和属性,仅仅表明实现该接口的类拥有按顺序串行执行任务的能力。

SingleThreadEventExecutor:一个可执行普通任务和调度任务的单线程执行器,也就是说,提交到该线程池的所有任务都有同一个线程来完成。内部具有一个阻塞队列用于保存所有提交到该执行器的任务。同时,其扩展了OrderedEventExecutor,表明需要按顺序串行执行所有提交的任务,这里体现了Netty无锁化设计。该接口实现了执行任务内部运作逻辑,后续会对其源码进行深入分析。即使如此,由于它还没有和特定的Selector绑定,因此不能执行I/O相关的操作。

io.netty.channel:

EventLoopGroup:Event Loop线程组,用于管理一组EventLoop对外提供服务,其扩展了EventExecutorGroup,同时定义了注册Channel的方法,用于将一个EventLoop与Channel绑定。

MultithreadEventLoopGroup:EventLoopGroup的抽象实现,初始化时会确认用于IO操作的EventLoop线程数量,默认值是处理器个数的2倍。从EventLoopGroup继承的register方法主要委托给next返回的EventLoop来完成。同时还扩展了MultithreadEventExecutorGroup,这样从EventExecutorGroup继承的方法都得到默认实现,但从MultithreadEventExecutorGroup中继承的newChild没有默认实现,它需要由由最终子类来实现。

DefaultEventLoopGroup:用于只能用于本地传输的EventLoop实现。

EventLoop:一旦与Channel绑定,将处理该Channel上的所有I/O操作。EventLoop可同时处理多个Channel中I/O操作,也可以只处理一个Channel上的I/O操作,具体由不同网络I/O确定。如Oio只能处理单个Channel的I/O操作,NIO则可以处理多个Channel的I/O操作。EventLoop所有子类都将顺序串行执行任务,因为其扩展了OrderedEventExecutor。

SingleThreadEventLoop:EventLoop的抽象实现,同时扩展了SingleThreadEventExecutor,负责用单个线程来执行所有提交到当前EventLoop的任务。SingleThreadEventLoop内部持有一个tailTasks队列,不知道干嘛用,目前内部也没有任何地方调用。SingleThreadEventLoop中主要实现了register相关方法。不同网络I/O类型通过扩展该类来完成底层实现。

ThreadPerChannelEventLoop:主要用于Oio的EventLoop实现,一个EventLoop只处理一个Channel的I/O操作。

io.netty.channel.nio:

NioEventLoopGroup:扩展自MultithreadEventLoopGroup,定义NIO的独特实现,主要实现了newChild方法。对于用户,代码中使用较多的也就NioEventLoopGroup了。

NioEventLoop:NIO实现,内部聚合了Java Selector,使得EventLoop成为一个真正意义的Reactor线程。内部除了实现Selector相关的一些操作,同时实现了执行任务的核心逻辑run方法。后续会详细分析它的源码。

线程管理

Netty线程模型的卓越性能取决于它对当前执行的Thread的身份确定,也就是说,确定他是否是分配给当前Channel以及它的EventLoop的那个线程(通过调用inEventLoop(Thread))。

如果当前调用线程正是支撑EventLoop的线程,那么所提交的代码块都将被直接执行。否则,EventLoop将调度该任务以便以后执行,并将它放入到内部队列中。当EventLoop下次处理它的事件时,它会执行队列中的任务、事件。这也解释了任何的Thread是如何与Channel直接交互而无需在ChannelHandler中进行额外同步的。不过,这仅对Netty4或更高的版本有效,在Netty3中只保证了入站事件在EventLoop对应的线程中执行,所有的出站事件都由调用线程处理,调用线程可能是EventLoop线程也可能是别的线程,因此,需要在ChannelHandler中对出站事件的处理进行同步,保证线程安全。

每个EventLoop都有它自己的任务队列,独立于其他的EventLoop。实际开发过程中,绝不应该阻塞当前I/O线程,或是将一个长时间运行的任务放入任务队列,因为它将阻塞需要在同一个线程上执行的任何其他任务。

此外,需注意EventLoop的分配方式对ThreadLocal使用的影响。由于NIO实现中EventLoop通常用于支持多个Channel,所以对于所有相关联的Channel来说,ThreadLocal都是一样的。

线程模型选择

下面以服务端的配置为例,说明如何选择不同的线程模型。

单线程模型

 EventLoopGroup bossGroup = new NioEventLoopGroup(1);
 try {
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup)
         .channel(NioServerSocketChannel.class)
        ......

以上示例中实例化了一个NIOEventLoopGroup,并传入线程数量为1,然后调用ServerBootstrap的group方法绑定线程组,看实现:

    @Override
    public ServerBootstrap group(EventLoopGroup group) {
        return group(group, group);
    }
    public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
        super.group(parentGroup);
        if (childGroup == null) {
            throw new NullPointerException("childGroup");
        }
        if (this.childGroup != null) {
            throw new IllegalStateException("childGroup set already");
        }
        this.childGroup = childGroup;
        return this;
    }

从源码可知,实际仍然绑定了 bossGroup 和 workerGroup,只是都是同一个NioEventLoopGroup实例而已,这样Netty中的acceptor和后续的所有客户端连接的IO操作都是在一个线程中处理,这就相当于Reactor的单线程模型。

多线程模型

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
      ServerBootstrap b = new ServerBootstrap();
      b.group(bossGroup, workerGroup)
       .channel(NioServerSocketChannel.class)
       ......

创建1个线程的bossGroup线程组,这个线程负责处理客户端的连接请求,而workerGroup默认使用处理器个数*2的线程数量来处理I/O操作。这就相当于Reactor的多线程模型。

主从多线程模型

EventLoopGroup bossGroup = new NioEventLoopGroup(4);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
      ServerBootstrap b = new ServerBootstrap();
      b.group(bossGroup, workerGroup)
       .channel(NioServerSocketChannel.class)
       ......

 

欢迎指出本文有误的地方,转载请注明原文出处https://my.oschina.net/7001/blog/1480153

分享到:
评论

相关推荐

    Netty服务器线程模型概览-线程模型

    Netty服务器线程模型概览_线程模型

    Netty简介 Netty线程模型和EventLoop Codec编码与解码 ByteBuf容器

    EventLoopGroup是EventLoop的集合,通常有老板线程组和工作线程组之分,老板线程负责接受新的连接,然后分配给工作线程处理。 **ByteBuf容器** ByteBuf是Netty提供的字节缓冲区,它是Java NIO ByteBuffer的一个...

    05.Netty线程模型.rar

    总的来说,Netty的线程模型是其高性能的关键之一,通过合理地调度和管理线程,Netty能够在高并发环境下保持高效和稳定。了解并掌握Netty的线程模型,对于开发高性能的网络应用至关重要。通过学习"课时06:Netty线程...

    Netty核心精讲之Reactor线程模型源码分析.mp4

    在深入探讨Netty的Reactor线程模型源码之前,我们需要先理解Reactor模式的基本概念。 Reactor模式是一种设计模式,常用于处理并发I/O事件。在多路复用I/O(如epoll、kqueue)中,Reactor模式是关键组成部分,它负责...

    04-Netty线程模型源码剖析1

    总的来说,深入理解Netty的线程模型和内存管理机制对于提升系统性能至关重要。通过阅读源码,我们可以学习到设计模式,如无锁串行化设计、内存池的实现等,这些都能帮助我们在日常开发中做出更高效、更稳定的系统。...

    netty 线程模型

    这是一本详细介绍了netty线程模型的书籍,包括 nio核心

    netty5多线程编程

    在分析Netty5多线程编程之前,首先需要了解Java内存模型与多线程编程的关系。硬件技术的进步,特别是多核处理器的发展和成本的降低,使得多任务处理成为操作系统的基本功能。多核处理器能够同时执行多个线程,使得...

    02-VIP-Netty核心功能与线程模型精讲1

    总的来说,Netty 是一个强大且灵活的网络通信框架,通过其高效的线程模型、易于使用的 API 和丰富的功能,极大地降低了开发网络应用的复杂性,提高了系统的性能和可扩展性。无论是在微服务架构、游戏后端,还是...

    简单了解Java Netty Reactor三种线程模型

    在Netty中,Reactor线程模型是其核心设计之一,用于处理并发连接和网络I/O操作。本文将详细介绍Reactor的三种线程模型,并结合Netty的线程模型进行解析。 ### 1. Reactor 单线程模型 Reactor单线程模型是指所有的I...

    使用Netty4实现多线程的消息分发

    在本文中,我们将深入探讨如何利用 Netty 4 实现多线程的消息分发,这对于构建分布式系统、游戏服务器或者任何需要高效处理并发连接的应用尤其重要。 一、Netty 框架简介 Netty 是由 JBoss 提供的一个开源项目,它...

    netty中的多线程应用

    在 Netty 中,多线程的应用是其处理高并发、高性能的关键因素之一。下面我们将深入探讨 Netty 中的多线程并发应用。 1. **线程模型** Netty 采用了 Boss-Worker 线程模型,它由两部分组成:Boss 线程和 Worker ...

    Netty多线程并发编程

    Netty多线程并发编程知识点总结 Netty多线程并发编程是指在Netty框架中使用多线程技术来实现高性能、高并发的网络编程。下面是关于Netty多线程并发编程的知识点总结: 一、 JAVA 内存模型与多线程编程 在Java中,...

    深入理解Netty线程模型

    当我们谈论Netty的线程模型时,首先会想到的是经典的Reactor IO多路复用线程模型。从这篇文章中,大家可以学习到如下知识:什么是I/O多路复用Reactor三种线程模型Netty线程模型NioEventLoop源码分析JDKepollbug学习I...

    Netty多线程案例集锦

    "Netty多线程案例集锦" Netty 多线程案例集锦是指在 Netty 框架中应用多线程技术来提高网络编程的性能和可扩展性。多线程技术可以让程序同时处理多个任务,从而提高程序的运行效率和响应速度。在 Netty 框架中,多...

    netty技术文档,socket技术 多线程

    这个文件可能深入探讨了如何在Netty中实现和优化多线程并发编程,包括但不限于以下主题: 1. **线程安全**:在多线程环境下,如何保证共享数据的安全访问。 2. **线程池配置**:如何根据系统资源和业务需求配置合适...

    深入Hotspot源码与Linux内核理解NIO与Netty线程模型.rar

    《深入Hotspot源码与Linux内核理解NIO与Netty线程模型》这份资料主要探讨了Java的Hotspot虚拟机源码、Linux内核、非阻塞I/O(NIO)以及Netty框架的线程模型。这些知识点在构建高性能、高并发的网络应用中至关重要。 ...

    Netty 消息接收和线程处理模型

    在本文中,我们将对 Netty 消息接收和线程处理模型进行深入分析,并从性能、时延和可靠性等角度对比串行模式和并行模式的差异,并给出具体的建议。 串行模式和并行模式是两种常见的线程处理模型,分别对应串行执行...

    多线程并发编程在Netty的应用分析

    《多线程并发编程在Netty中的应用分析》 ...在阅读《多线程并发编程在Netty中的应用分析》这份文档时,建议重点关注Netty的线程模型、事件驱动模型、ByteBuf内存管理以及异步编程机制,这些是理解和应用Netty的关键点。

Global site tag (gtag.js) - Google Analytics