netty的线程池-----揭示了使用两个线程池的原因
线程模型是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即可。
- EventLoopGroup bossGroup = new NioEventLoopGroup();
- EventLoopGroup workerGroup = new NioEventLoopGroup();
- try {
- ServerBootstrap b = new ServerBootstrap();
- b.group(bossGroup, workerGroup)
- .channel(NioServerSocketChannel.class)
- .option(ChannelOption.SO_BACKLOG, 1024)
- .childHandler(new ChildChannelHandler());
- ChannelFuture f = b.bind(port).sync();
- f.channel().closeFuture().sync();
- } finally {
- bossGroup.shutdownGracefully();
- workerGroup.shutdownGracefully();
- }
- protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
- if (nThreads <= 0) {
- throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
- }
- if (executor == null) {
- executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
- }
- children = new EventExecutor[nThreads];
- for (int i = 0; i < nThreads; i ++) {
- boolean success = false;
- try {
- children[i] = newChild(executor, args);
- success = true;
- } catch (Exception e) {
- // TODO: Think about if this is a good exception type
- throw new IllegalStateException("failed to create a child event loop", e);
- } finally {
第二个概念是每个NioEventLoop都绑定了一个Selector,所以在Netty5的线程模型中,是由多个Selecotr在监听IO就绪事件。而Channel注册到Selector。
举个例子,比如有100万个连接连到服务器端。平时的写法可能是1个Selector线程监听所有的IO就绪事件,1个Selector面对100万个连接(Channel)。
而如果使用了1000个NioEventLoop的线程池来说,1000个Selector面对100万个连接,每个Selector只需要关注1000个连接(Channel)
- public final class NioEventLoop extends SingleThreadEventLoop {
- /**
- * The NIO {@link Selector}.
- */
- Selector selector;
- private SelectedSelectionKeySet selectedKeys;
- private final SelectorProvider provider;
第三个概念是一个Channel绑定一个NioEventLoop,相当于一个连接绑定一个线程,这个连接所有的ChannelHandler都是在一个线程中执行的,避免的多线程干扰。更重要的是ChannelPipline链表必须严格按照顺序执行的。单线程的设计能够保证ChannelHandler的顺序执行。
- public interface Channel extends AttributeMap, Comparable<Channel> {
- /**
- * Return the {@link EventLoop} this {@link Channel} was registered too.
- */
- EventLoop eventLoop();
第四个概念是一个NioEventLoop的selector可以被多个Channel注册,也就是说多个Channel共享一个EventLoop。EventLoop的Selecctor对这些Channel进行检查。
这段代码展示了线程池如何给Channel分配EventLoop,是根据Channel个数取模
- public EventExecutor next() {
- return children[Math.abs(childIndex.getAndIncrement() % children.length)];
- }
- private void processSelectedKeysOptimized(SelectionKey[] selectedKeys) {
- for (int i = 0;; i ++) {
- // 逐个处理注册的Channel
- final SelectionKey k = selectedKeys[i];
- if (k == null) {
- break;
- }
- final Object a = k.attachment();
- if (a instanceof AbstractNioChannel) {
- processSelectedKey(k, (AbstractNioChannel) a);
- } else {
- @SuppressWarnings("unchecked")
- NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
- processSelectedKey(k, task);
- }
- if (needsToSelectAgain) {
- selectAgain();
- // Need to flip the optimized selectedKeys to get the right reference to the array
- // and reset the index to -1 which will then set to 0 on the for loop
- // to start over again.
- //
- // See https://github.com/netty/netty/issues/1523
- selectedKeys = this.selectedKeys.flip();
- i = -1;
- }
- }
- }
理解了这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的线程池-----揭示了使用两个线程池的原因的更多相关文章
- SessionFactory是线程安全的吗?Session是线程安全的吗?两个线程能共享一个Session吗?
		(1)SessionFactory对应Hibernate的一个数据存储的概念,它是线程安全的,可以被多个线程并发访问.SessionFactory一般只会在启动的时候构建.对于应用程序,最好将Sess ... 
- JavaSE中线程与并行API框架学习笔记——线程为什么会不安全?
		前言:休整一个多月之后,终于开始投简历了.这段时间休息了一阵子,又病了几天,真正用来复习准备的时间其实并不多.说实话,心里不是非常有底气. 这可能是学生时代遗留的思维惯性--总想着做好万全准备才去做事 ... 
- Qt 进程和线程之三:线程同步、可重入与线程安全
		一.同步线程方法 使用线程的目的是允许代码并行运行,但是有时线程必须停止并等待其他线程.例如,如果两个线程试图同时写入相同的变量,结果是不确定的,所以需要同步线程.同步线程是一种保护共享资源等数据的常 ... 
- AsyncTask两种线程池
		AsyncTask两种线程池 http://bbs.51cto.com/thread-1114378-1.html (API 3.0以后): 1.THREAD_POOL_EXECUTOR, ... 
- MyCAT 在 Cobar 的基础上,完成了彻底的 NIO 通讯,并且合并了两个线程池
		研读: 1.http://www.mycat.io <Mycat权威指南> 第 2 章 Mycat 前世今生: 浏览: 深度认识 Sharding-JDBC:做最轻量级的数据库中间层 - ... 
- 你创建线程池最好分为两种线程池,io密集型线程池,或者cpu密集型线程池
		你创建线程池最好分为两种线程池,io密集型线程池,或者cpu密集型线程池. 否则,如果只用一个线程池的话,不管是iO密集的线程,或者cpu消耗大的都放在同一个线程池的话,会发生线程池被撑满的情况 
- Mina、Netty、Twisted一起学(十):线程模型
		要想开发一个高性能的TCP服务器,熟悉所使用框架的线程模型非常重要.MINA.Netty.Twisted本身都是高性能的网络框架,如果再搭配上高效率的代码,才能实现一个高大上的服务器.但是如果不了解它 ... 
- Java线程池的原理及几类线程池的介绍
		刚刚研究了一下线程池,如果有不足之处,请大家不吝赐教,大家共同学习.共同交流. 在什么情况下使用线程池? 单个任务处理的时间比较短 将需处理的任务的数量大 使用线程池的好处: 减少在创建和销毁线程上所 ... 
- 浅谈线程池(中):独立线程池的作用及IO线程池
		原文地址:http://blog.zhaojie.me/2009/07/thread-pool-2-dedicate-pool-and-io-pool.html 在上一篇文章中,我们简单讨论了线程池的 ... 
随机推荐
- POST、GET请求中文参数乱码问题
			POST请求中文乱码问题解决方法: 在web.xml文件中添加编码过滤器,如下: <!-- 解决post乱码 --> <filter> <filter-name>C ... 
- UICollectionView Demo
			1. 利用系统自动布局UICollectionViewFlowLayout进行布局. ViewController1 #import "ViewController1.h" @in ... 
- linux 下查看磁盘IO状态
			from:脚本之家 linux 查看磁盘IO状态操作 作者:佚名 字体:[增加 减小] 来源:互联网 时间:11-15 15:13:44我要评论 Linux系统出现了性能问题,一般我们可以通过top. ... 
- System.Web.HttpValueCollection.ThrowIfMaxHttpCollectionKeysExceeded
			昨天客户跟我说,突然一个页面频繁地报ThrowIfMaxHttpCollectionKeysExceeded这个异常.而且是数据量大的时候报错,数据量小的时候OK. 根据异常的名称也能看得差不多超过了 ... 
- 从前端的UI开始
			MVC分离的比较好,开发顺序没有特别要求,先开发哪一部分都可以,这次我们主要讲解前端UI的部分. ASP.NET MVC抛弃了WebForm的一些特有的习惯,例如服务器端控件,ViewState这些东 ... 
- Bitter Sweet Symphony
			当我写下这段话时,另一个我觉醒了. 时间仿佛一下子从2013年的末尾跳到了2014年了,是那么的猝不及防.1990——2014,24岁了,一瞬间,不知不觉已经走过了24个岁月了.过去,我时常会反省着, ... 
- Linux shell下30个有趣的命令
			Tips 原文作者:Víctor López Ferrando 原文地址:30 interesting commands for the Linux shell 这些是我收集了多年的Linux she ... 
- FreeSWITCH收到重复的DTMF信号
			一.背景 用户是运营商手机,拨打的是运营商的固话号码进入的FreeSWITCH的IVR,进入IVR语音播报后,按指定的分机号呼相关人员. 二.现象 用户反映拨打124870找不到指定人员,以前是正常的 ... 
- win10 标注工具LabelImg 安装使用
			安装步骤(默认已经安装了Python3.X ): pip 安装PyQt5 进入cmd(Win键 + R键,输入cmd,enter键入),输入: >>pip install PyQt5 如果 ... 
- easyui中combobox的值改变onchang事件
			今天在公司里,那jquery中的easy-ui-里面的combobox,真的郁闷死了! 把郁闷的事情记下来,下次就不会犯错了! 首先,肯定少不了,引入jquery的js文件!请大家注意了! 下面是代码 ... 
