Netty实战-EventLoop和线程模型

Posted 程序员读书笔记

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Netty实战-EventLoop和线程模型相关的知识,希望对你有一定的参考价值。

简单来说,线程模型制定了操作系统、编程语言、框架或者应用程序上下文中的线程管理的关键方面。因此开发人员需要理解不同模型相关的权衡。在本章中,我们将详细的讨论Netty的线程模型。

一、线程模型概述

基本的线程池化模式可以描述为

  • 从池的空闲线程列表中选择一个Thread,并且指派它去运行一个已经提交的任务

  • 当任务完成时,将该Thread返回给该列表,使其可被重用

虽然池化和重用线程相对于简单的为每个任务都创建和销毁线程是一个进步,但是它并不能消除由上下文切换所带来的开销,其将随着线程数量的增加很快变得明显,并且在高负载下会越来越严重。

二、EventLoop接口

运行任务来处理在连接的生命周期内发生的事件是任何网络框架的基本功能。与之相应的编程上的构造通常被称为事件循环。一个Netty使用了EventLoop来适用的术语。以下代码说明了事件循环的基本思想

 
   
   
 
  1. //阻塞,直到有事件已经就绪可被运行

  2. while(!terminated) {

  3. List<Runnable> readyEvents = blockUntilEventsReady();

  4. for (Runnable ev: readyEvents) {

  5. //循环遍历 并处理所有事件

  6. ev.run();

  7. }

  8. }

在这个模型中,一个EventLoop将由一个永远都不会改变的Thread驱动,同时任务可以直接提交给EventLoop实现,以立即执行或者调度执行。根据配置和核心的不同,可能会创建多个EventLoop实例用以优化资源的使用,并且单个EventLoop可能会被指派用于服务多个Channel。

1、Netty4中的IO和事件处理

在Netty中,所有的IO操作和事件都由已经被分配废了EventLoop的那个线程来处理

三、任务调度

偶尔你需要调度一个任务以便稍后执行或者周期性执行。在接下来的几节中,我们将展示如何使用核心的Java API和Netty的EventLoop来调度任务。

1、使用EventLoop调度任务

ScheduledExecutorService的线程池实现具有局限性。例如,作为线程池管理的一部分,将会有额外的线程创建。如果有大量任务被紧凑调度,那么这将成为一个瓶颈。Netty通过Channel的EventLoop实现任务调度解决了这一问题。

 
   
   
 
  1. Channel ch = ...;

  2. ScheduledFuture future = ch.eventLoop().schedule(new Runnable() {

  3. @Override

  4. public void run() {

  5. System.out.println("60 seconds");

  6. }

  7. }, 60, TimeUnit.SECONDS);

经过60秒之后,Runnable实例将由分配给Channel的EventLoop执行。

四、实现细节

1、线程管理

Netty线程模型的卓越性能取决于对于当前执行的Thread的身份的确定(通过调用EventLoop的inEventLoop方法实现),也就是说,确定它是否是分配给当前Channel以及它的EventLoop的那一个线程。如果调用线程正式支撑EventLoop的线程,那么所提交的代码块将会被直接执行。否则,EventLoop将调用该任务以便稍后执行,并将它放入到内部队列。当EventLoop下次处理它的事件时,他就会执行队列中的那些任务/事件。这也就解释了任何的Thread是如何与Channel直接交互而无需在ChannelHandler中进行额外同步的。注意,每个EventLoop都有它自己的任务队列,独立于任何其他的EventLoop。下图展示了EventLoop用于调度任务的执行逻辑。这是Netty线程模型的关键组成部分。 

我们之前已经阐明了不要阻塞当前IO线程的重要性。我们要再次重申,永远不要将一个长时间运行的任务放到执行队列中,因为他将阻塞需要在同一线程上执行的任何其他任务。如果必须要进行阻塞调用或者执行长时间运行的任务,我们建议使用一个专门的EventExecutor(见上一章的ChannelHandler的执行和阻塞)。

2、EventLoop/线程的分配

服务于Channel的IO和事件的EventLoop包含在EventLoopGroup中。根据不同的传输实现,EventLoop的创建和分配方式也不同。

(1)异步传输

异步传输实现只使用了少量的EventLoop(以及他们相关联的Thread),而且在当前的线程模型中,他们可能会被多个Channel所共享。这使得可以通过尽可能少量的Thread来支撑大量的Channel,而不是每个Channel分配一个Thread。下图显示了一个EventLoopGroup,它具有3个固定大小的EventLoop(每个EventLoop都由一个Thread支撑)。在创建EventLoopGroup时就直接分配了EventLoop,以确保在需要时他们是可用的。 

EventLoopGroup负责为每个新创建的Channel分配一个EventLoop。在当前实现中,使用顺序循环的方式进行分配以获取一个均衡的分布,并且相同的EventLoop可能会被分配给多个Channel。一旦一个Channel被分配给一个EventLoop,它将在他的整个生命周期都使用这个EventLoop。

(2)阻塞传输

用于像OIO这样的其他传输的设计略有不同。这里每一个Channel都将分配给一个EventLoop。

每个Channel的IO事件都将只会被一个Thread处理


以上是关于Netty实战-EventLoop和线程模型的主要内容,如果未能解决你的问题,请参考以下文章

Netty的EventLoop和线程模型

Netty精粹之基于EventLoop机制的高效线程模型

Netty实战

牛了Netty之EventLoop线程

netty源码:4 事件调度层:为什么 EventLoop 是 Netty 的精髓?

「Netty实战 03」大白话 Netty 核心组件分析