线程模型

线程模型

1.传统服务设计模型

特点:

  • 通过阻塞I/O来获取数据
  • 每个连接都需要独立的线程来完成业务输入、数据处理、数据返回

存在的问题:

  • 1.当并发数很大时,需要创建大量线程,占用了很多系统资源。
  • 2.连接创建后,如果连接中没有数据可读,线程会被阻塞,操作线程资源浪费。

2. NIO分发模型

分发模型具有以下几个机制:

  • 将一个完整处理过程分解为一个个细小的任务。
  • 每个任务执行相关的动作且不产生阻塞。
  • 在任务执行状态被触发时才会去执行,例如,只在有数据时才会触发读操作。

3.事件驱动模型

事件驱动模型简单来说,就是当有事件来临时,任务才会被触发执行。
事件驱动的编程难度相对较高,它必须为服务设计多个逻辑状态,以便跟踪和中断恢复。这些逻辑状态也代表着相应的事件。
在Java NIO中,有以下逻辑状态:

  • OP_ACCEPT:接收连接事件,表示服务器监听到了客户连接。
  • OP_CONNECT:连接就绪事件,表示客户与服务器的连接已经建立
  • OP_READ:读就绪事件,表示通道中已有可读的数据。
  • OP_WRITE:写就绪事件,表示已经可以向通道写数据了。一般写就绪事件发生时机:当我们需要主动的向通道中写数据时,一般来说有两种方式:1.直接从Selector中拿到通道,然后直接写;2.将需要写的数据附加在SelectionKey上,然后调用key.interestOps(SelectionKey.OP_WRITE),来将该key标记,当下次select时,该key会触发写操作。

Java NIO 示例

// 在需要写时拿到key,附加需要写的byteBuffer,然后设置感兴趣的操作
key.attach(msg);
key.interestOps(SelectionKey.OP_WRITE);
//...
// 下次select时,会判断到该key可写,执行写操作
if (key.isWritable()) {
    ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
    byteBuffer.flip();
    ((SocketChannel)key.channel()).write(byteBuffer);
}

4.Reactor模型

有多种叫法,分别是反应器模式、分发者模式(Dispatcher)、通知者模式(Notifier)。

针对传统阻塞I/O模型的缺点,提出了两个解决方案:

  1. 基于IO复用模型:多个连接公用一个阻塞对象,应用程序只需要在一个阻塞对象等待,无需阻塞等待所有连接,当某个连接有新的数据可以处理时,操作系统通知应用程序从阻塞状态返回,开始进行业务处理。

  2. 基于线程池复用线程资源:不必为每个连接都创建线程,将连接完成后的业务处理任务交给线程来进行处理,一个线程可处理多个连接的业务。

优点:

  • 响应快
  • 可以最大程度的避免复杂的多线程及同步问题,并且避免了线程/进程切换开销
  • 扩展性好,可以通过增加Reactor实例个数来充分利用CPU资源
  • 可复用性高,Reactor模型与具体事件处理逻辑无关,具有很高的复用性

4.1 单Reactor单线程模型

所有的处理流程仅在一个线程中完成,Reactor、Acceptor、Handler仅做了代码上的划分。

处理流程如下:

  1. 客户端发送请求,服务端Reactor监听到请求后,调用dispatch处理
  2. 如果是连接事件,则调用Acceptor处理
  3. 如果是读写事件,则调用Handler处理

使用案例:Redis

4.2 单Reactor多线程模型

该模型下监听、读写事件仍然由Reactor线程来处理,将耗时的业务处理流程交给了线程池来处理。

处理流程如下:

  1. 客户端发送请求,服务端Reactor线程监听到事件,调用dispatch处理
  2. 如果是连接事件,则交给Acceptor来处理
  3. 如果是读事件,则调用Handler读取到数据,然后交给Worker线程来进行业务逻辑处理,然后将操作结果返回给Reactor
  4. 如果是写事件,则Reactor调用Handler执行写操作

4.3 主从Reactor多线程模型

img
模型处理流程如下:

  1. 客户端发起连接请求,主Reactor通过Acceptor监听到连接请求。
  2. Acceptor从多个副Reactor中挑选一个出来,然后将新连接交给该副Reactor。
  3. 将该连接注册到副Reactor单独维护的Selector中。
  4. 当连接可读时,副Reactor通过Handler来执行读操作,然后将处理数据的过程放到Worker来执行。
  5. Workder线程处理完成后,Worker将返回操作结果给副Reactor,副Reactor再将操作结果返回给用户。

其中主Reactor是单线程的,副Reactor由多个线程组成,Workder线程池由多个线程组成。

优点:

  • 主Reactor和副Reactor分工明确交互简单,主Reactor只需要接受新连接,副Reactor负责监听连接读写事件

缺点:

  • 编程复杂度较高

使用案例:Netty、Nginx 等

posted @ 2023-07-08 17:32  zolmk  阅读(53)  评论(0编辑  收藏  举报