线程模型是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. 详解SpringMVC中Controller的方法中参数的工作原理——基于maven

    转自:http://www.tuicool.com/articles/F7byQn 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:ht ...

  2. MM bound 与 Jensen's inequality

    MM bound 与 Jensen's inequality 简森不等式 在使用最大似然估计方法求解模型最优解的时候,如果使用梯度下降(GD or SGD)或者梯度上升(GA or SGA),可能收敛 ...

  3. iOS 持续集成

    iOS 持续集成系列 - 开篇 前言 iOS 开发在经过这几年的野蛮生长之后,慢慢地趋于稳定.无论开发语言是 Objective-C 还是 Swift,工程类型是 Hybird 还是原生,开发思想是 ...

  4. 集成支付宝钱包支付 iOS SDK 的方法与经验

    下载 首先,你要想找到这个SDK,都得费点功夫.现在的SDK改名叫移动支付集成开发包了,下载页面在 这里 (http://t.cn/8ksiklD)的 “请点此下载集成开发包(http://t.cn/ ...

  5. 线程同步之mutex和event区别

    之前只是用过 关键段来对同进程不同线程进行互斥,防止对同一份资源或代码段的竞争: mutex可以理解为不同进程或者同一进程内防止对同一份资源的竞争: event更多的是同步,当然也是不同进程或者同一进 ...

  6. linux下软链接与硬链接及其区别

    linux下创建链接命令 ln -s 软链接 这是linux中一个非常重要命令,请大家一定要熟悉.它的功能是为某一个文件在另外一个位置建立一个不同的链接,这个命令最常用的参数是-s, 具体用法是:ln ...

  7. 关于使用coreseek并为其做分页的介绍(转)

    coreseek 做分页时找数据总量还真不好找.以为他会给一个方法(函数)什么的去获取,结果却不是.首先需要了解:num_matches: 当前返回的结果数,<= limit设置值.max_ma ...

  8. Checkstyle-Configuration

    <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE module PUBLIC "-/ ...

  9. 获取*.jks签名的方法(Android studio)

  10. java与C++之间进行SOCKET通讯要点简要解析

    原文链接: http://blog.csdn.net/hslinux/article/details/6214594 java与C++之间进行SOCKET通讯要点简要解析 hslinux 0.篇外语 ...