线程模型是Netty的核心设计,设计地很巧妙,之前项目中有一块处理并发的设计和Netty的Eventloop单线程设计类似,效果得到了实证。

Netty5的类层次结构和之前的版本变化很大,网上也有很多文章写Netty的线程模型,Reactor模式,比如这篇http://blog.csdn.net/xiaolang85/article/details/37873059, 应该是引自《Netty权威指南》,写得比较全面,但是有几个关键的概念没讲清楚。

这篇文章只讲Netty5线程模型最重要的几个关键点

第一个概念是如何理解NioEventLoop和NioEventLoopGroup:NioEventLoop实际上就是工作线程,可以直接理解为一个线程。NioEventLoopGroup是一个线程池,线程池中的线程就是NioEventLoop。Netty设计这几个类的时候,层次结构挺复杂,反而让人迷惑。

还有一个让人迷惑的地方是,创建ServerBootstrap时,要传递两个NioEventLoopGroup线程池,一个叫bossGroup,一个叫workGroup。《Netty权威指南》里只说了bossGroup是用来处理TCP连接请求的,workGroup是来处理IO事件的。

这么说是没错,但是没说清楚bossGroup具体如何处理TCP请求的。实际上bossGroup中有多个NioEventLoop线程,每个NioEventLoop绑定一个端口,也就是说,如果程序只需要监听1个端口的话,bossGroup里面只需要有一个NioEventLoop线程就行了。

在上一篇文章介绍服务器端绑定的过程中,我们看到最后是NioServerSocketChannel封装的Java的ServerSocketChannel执行了绑定,并且执行accept()方法来创建客户端SocketChannel的连接。一个端口只需要一个NioServerSocketChannel即可。

  1. EventLoopGroup bossGroup = new NioEventLoopGroup();
  2. EventLoopGroup workerGroup = new NioEventLoopGroup();
  3. try {
  4. ServerBootstrap b = new ServerBootstrap();
  5. b.group(bossGroup, workerGroup)
  6. .channel(NioServerSocketChannel.class)
  7. .option(ChannelOption.SO_BACKLOG, 1024)
  8. .childHandler(new ChildChannelHandler());
  9. ChannelFuture f = b.bind(port).sync();
  10. f.channel().closeFuture().sync();
  11. } finally {
  12. bossGroup.shutdownGracefully();
  13. workerGroup.shutdownGracefully();
  14. }
  15. protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
  16. if (nThreads <= 0) {
  17. throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
  18. }
  19. if (executor == null) {
  20. executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
  21. }
  22. children = new EventExecutor[nThreads];
  23. for (int i = 0; i < nThreads; i ++) {
  24. boolean success = false;
  25. try {
  26. children[i] = newChild(executor, args);
  27. success = true;
  28. } catch (Exception e) {
  29. // TODO: Think about if this is a good exception type
  30. throw new IllegalStateException("failed to create a child event loop", e);
  31. } finally {

第二个概念是每个NioEventLoop都绑定了一个Selector,所以在Netty5的线程模型中,是由多个Selecotr在监听IO就绪事件。而Channel注册到Selector。

举个例子,比如有100万个连接连到服务器端。平时的写法可能是1个Selector线程监听所有的IO就绪事件,1个Selector面对100万个连接(Channel)。

而如果使用了1000个NioEventLoop的线程池来说,1000个Selector面对100万个连接,每个Selector只需要关注1000个连接(Channel)

  1. public final class NioEventLoop extends SingleThreadEventLoop {
  2. /**
  3. * The NIO {@link Selector}.
  4. */
  5. Selector selector;
  6. private SelectedSelectionKeySet selectedKeys;
  7. private final SelectorProvider provider;

第三个概念是一个Channel绑定一个NioEventLoop,相当于一个连接绑定一个线程,这个连接所有的ChannelHandler都是在一个线程中执行的,避免的多线程干扰。更重要的是ChannelPipline链表必须严格按照顺序执行的。单线程的设计能够保证ChannelHandler的顺序执行。

  1. public interface Channel extends AttributeMap, Comparable<Channel> {
  2. /**
  3. * Return the {@link EventLoop} this {@link Channel} was registered too.
  4. */
  5. EventLoop eventLoop();

第四个概念是一个NioEventLoop的selector可以被多个Channel注册,也就是说多个Channel共享一个EventLoop。EventLoop的Selecctor对这些Channel进行检查。

这段代码展示了线程池如何给Channel分配EventLoop,是根据Channel个数取模

  1. public EventExecutor next() {
  2. return children[Math.abs(childIndex.getAndIncrement() % children.length)];
  3. }
  4. private void processSelectedKeysOptimized(SelectionKey[] selectedKeys) {
  5. for (int i = 0;; i ++) {
  6. // 逐个处理注册的Channel
  7. final SelectionKey k = selectedKeys[i];
  8. if (k == null) {
  9. break;
  10. }
  11. final Object a = k.attachment();
  12. if (a instanceof AbstractNioChannel) {
  13. processSelectedKey(k, (AbstractNioChannel) a);
  14. } else {
  15. @SuppressWarnings("unchecked")
  16. NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
  17. processSelectedKey(k, task);
  18. }
  19. if (needsToSelectAgain) {
  20. selectAgain();
  21. // Need to flip the optimized selectedKeys to get the right reference to the array
  22. // and reset the index to -1 which will then set to 0 on the for loop
  23. // to start over again.
  24. //
  25. // See https://github.com/netty/netty/issues/1523
  26. selectedKeys = this.selectedKeys.flip();
  27. i = -1;
  28. }
  29. }
  30. }

理解了这4个概念之后就对Netty5的线程模型有了清楚的认识:

在监听一个端口的情况下,一个NioEventLoop通过一个NioServerSocketChannel监听端口,处理TCP连接。后端多个工作线程NioEventLoop处理IO事件。每个Channel绑定一个NioEventLoop线程,1个NioEventLoop线程关联一个selector来为多个注册到它的Channel监听IO就绪事件。NioEventLoop是单线程执行,保证Channel的pipline在单线程中执行,保证了ChannelHandler的执行顺序。

下面这张图来之http://blog.csdn.net/xiaolang85/article/details/37873059, 基本能说清楚。

netty的线程池-----揭示了使用两个线程池的原因的更多相关文章

  1. SessionFactory是线程安全的吗?Session是线程安全的吗?两个线程能共享一个Session吗?

    (1)SessionFactory对应Hibernate的一个数据存储的概念,它是线程安全的,可以被多个线程并发访问.SessionFactory一般只会在启动的时候构建.对于应用程序,最好将Sess ...

  2. JavaSE中线程与并行API框架学习笔记——线程为什么会不安全?

    前言:休整一个多月之后,终于开始投简历了.这段时间休息了一阵子,又病了几天,真正用来复习准备的时间其实并不多.说实话,心里不是非常有底气. 这可能是学生时代遗留的思维惯性--总想着做好万全准备才去做事 ...

  3. Qt 进程和线程之三:线程同步、可重入与线程安全

    一.同步线程方法 使用线程的目的是允许代码并行运行,但是有时线程必须停止并等待其他线程.例如,如果两个线程试图同时写入相同的变量,结果是不确定的,所以需要同步线程.同步线程是一种保护共享资源等数据的常 ...

  4. AsyncTask两种线程池

        AsyncTask两种线程池  http://bbs.51cto.com/thread-1114378-1.html (API 3.0以后): 1.THREAD_POOL_EXECUTOR, ...

  5. MyCAT 在 Cobar 的基础上,完成了彻底的 NIO 通讯,并且合并了两个线程池

    研读: 1.http://www.mycat.io <Mycat权威指南> 第 2 章 Mycat 前世今生: 浏览: 深度认识 Sharding-JDBC:做最轻量级的数据库中间层 - ...

  6. 你创建线程池最好分为两种线程池,io密集型线程池,或者cpu密集型线程池

    你创建线程池最好分为两种线程池,io密集型线程池,或者cpu密集型线程池. 否则,如果只用一个线程池的话,不管是iO密集的线程,或者cpu消耗大的都放在同一个线程池的话,会发生线程池被撑满的情况

  7. Mina、Netty、Twisted一起学(十):线程模型

    要想开发一个高性能的TCP服务器,熟悉所使用框架的线程模型非常重要.MINA.Netty.Twisted本身都是高性能的网络框架,如果再搭配上高效率的代码,才能实现一个高大上的服务器.但是如果不了解它 ...

  8. Java线程池的原理及几类线程池的介绍

    刚刚研究了一下线程池,如果有不足之处,请大家不吝赐教,大家共同学习.共同交流. 在什么情况下使用线程池? 单个任务处理的时间比较短 将需处理的任务的数量大 使用线程池的好处: 减少在创建和销毁线程上所 ...

  9. 浅谈线程池(中):独立线程池的作用及IO线程池

    原文地址:http://blog.zhaojie.me/2009/07/thread-pool-2-dedicate-pool-and-io-pool.html 在上一篇文章中,我们简单讨论了线程池的 ...

随机推荐

  1. POST、GET请求中文参数乱码问题

    POST请求中文乱码问题解决方法: 在web.xml文件中添加编码过滤器,如下: <!-- 解决post乱码 --> <filter> <filter-name>C ...

  2. UICollectionView Demo

    1. 利用系统自动布局UICollectionViewFlowLayout进行布局. ViewController1 #import "ViewController1.h" @in ...

  3. linux 下查看磁盘IO状态

    from:脚本之家 linux 查看磁盘IO状态操作 作者:佚名 字体:[增加 减小] 来源:互联网 时间:11-15 15:13:44我要评论 Linux系统出现了性能问题,一般我们可以通过top. ...

  4. System.Web.HttpValueCollection.ThrowIfMaxHttpCollectionKeysExceeded

    昨天客户跟我说,突然一个页面频繁地报ThrowIfMaxHttpCollectionKeysExceeded这个异常.而且是数据量大的时候报错,数据量小的时候OK. 根据异常的名称也能看得差不多超过了 ...

  5. 从前端的UI开始

    MVC分离的比较好,开发顺序没有特别要求,先开发哪一部分都可以,这次我们主要讲解前端UI的部分. ASP.NET MVC抛弃了WebForm的一些特有的习惯,例如服务器端控件,ViewState这些东 ...

  6. Bitter Sweet Symphony

    当我写下这段话时,另一个我觉醒了. 时间仿佛一下子从2013年的末尾跳到了2014年了,是那么的猝不及防.1990——2014,24岁了,一瞬间,不知不觉已经走过了24个岁月了.过去,我时常会反省着, ...

  7. Linux shell下30个有趣的命令

    Tips 原文作者:Víctor López Ferrando 原文地址:30 interesting commands for the Linux shell 这些是我收集了多年的Linux she ...

  8. FreeSWITCH收到重复的DTMF信号

    一.背景 用户是运营商手机,拨打的是运营商的固话号码进入的FreeSWITCH的IVR,进入IVR语音播报后,按指定的分机号呼相关人员. 二.现象 用户反映拨打124870找不到指定人员,以前是正常的 ...

  9. win10 标注工具LabelImg 安装使用

    安装步骤(默认已经安装了Python3.X ): pip 安装PyQt5 进入cmd(Win键 + R键,输入cmd,enter键入),输入: >>pip install PyQt5 如果 ...

  10. easyui中combobox的值改变onchang事件

    今天在公司里,那jquery中的easy-ui-里面的combobox,真的郁闷死了! 把郁闷的事情记下来,下次就不会犯错了! 首先,肯定少不了,引入jquery的js文件!请大家注意了! 下面是代码 ...