上一篇章介绍了下NIO的基础,并且也给出了一个简单的代码示例,但是如果想享受NIO带来的高速的快感,就得使用多线程编程了。那么在使用多线程编程之前,有一些关于多线程的东西想分享下:
一、关于多线程
- 分享点一:在单核CPU上,多线程不一定能比单线程更好。为什么要使用多线程?很多人也许未必去考虑过,我个人认为,使用多线程是为了减少CPU等待的时间,最大化的利用CPU的性能。那为什么说多线程未必比单线程好呢?我简单举个案例。你要扫地和洗马桶,扫地要10分钟,洗马桶要10分钟,你一个人完成这两件事要多少时间?使用单线程工作方式是20分钟,而你如果使用多线程,扫一半地跑过去洗马桶再回过来扫地再回去去洗马桶,那么完成这两件事必定要超过20分钟了。但是如果你要做的事是煮饭和扫地,那么使用多线程就体现出优势了。
- 分享点二:选择多少个线程能充分利用服务器的性能呢?有很多理论是服务器用的是几核CPU就使用几个线程,这是一个比较通用的配置方式,仅限当你线程中执行的代码无阻塞时。要服务器性能越高,就是等同于让CPU处理业务逻辑的时间越长,所以开几个多线程就是考虑能充分利用这些CPU,但是注意不要过度开多线程,线程间的调度本身会消耗CPU,开越多,调度的代价越高。
多线程的开发要比单线程复杂很多,所以在进行多线程编程的情况下,要特别的小心。
二、网络模型
Reactor模式是我在接触netty之后才知道的一个模式,关于这个模式,在Netty实现原理浅析的网络模型里面有详尽的描述,当然作者文章也说,是参考Doug Lea的Scalable IO in Java这位大牛的文章,想要理解这个模型的,可以先去看上述两篇文章,接下去的内容,是基于我对于这些模式的理解的记录,顺便会根据Doug Lea文章的内容稍微翻译摘出来点:
一个最简单的网络模型拥有的基本模型大概是这样的:
- 读取数据
- 对数据解码
- 处理数据
- 对数据编码
- 发送数据
模型一:单线程的reactor模型:
读取数据,对数据解码,处理数据,对数据编码和发送都是在一个线程中的。此时的Reactor可以认为是服务器端的主线程,负责接受客户端的链接,链接建立完毕后将此链接负责分派到一个handler线程去处理剩下的事情。伪代码如下:
/** * 代码来自Doug Lea **/ Class Server implements Runnable{ public void run(){ try{ ServerSocket ss = new ServerSocket(port); while(!Thread.interrupted()){ new Thread(new Handler(ss.accept())).start(); } } } } static class Handler implements Runnable{ final Socket socket; Handler(Socket s){ socket = s; } public void run(){ try{ byte[] input = new byte[MAX_INPUT]; socket.getInputStream().read(input); byte[] output = process(input); socket.getOutputStream().write(output); }catch(IOException ex){ /*...*/ } } }
这个模式有什么优缺点?我一个单词一个单词的查询翻译了下Doug Lea的文章,也貌似没明确说,但在Netty原理浅析里面是这样阐述的:
该模型 适用于处理器链中业务处理组件能快速完成的场景。不过,这种单线程模型不能充分利用多核资源,所以实际使用的不多。
那怎么样才算能充分利用多核CPU资源呢?难道开了多个Handler操作就只是用到了一个CPU核么?想想也不可能,
我用下面代码测试:
public class Test { public static void main(String[] ben){ Thread work1 = new Work(); work1.start(); Thread work2 = new Work(); work2.start(); } } class Work extends Thread{ public void run(){ Long sum=0l; int i=0; while(i<100000){ sum+=i; } System.out.println("end"); } }
我CPU是双核,两个核立马彪满:
所以说上面的这个模型不能充分利用多核CPU,我是持保留意见滴。那么怎样的情况不能利用多核CPU呢?
假设当前服务器为四核CPU,当前只有一个handler线程,handler线程里面执行的业务处理很庞大,比如像上面我的示例,1到100万相加总和这样的计算,在这种情况下,如果把1到100万相加的handler再拆成四个线程去计算,各计算四分之一的数据,那么就可以充分利用四核CPU了(但是如果执行很快,比如计算1到10,拆了反而更慢,因为CPU还要调度的)。
但是又假设如果当前已经有四个handler线程甚至更多,就算不拆也已经用到四核了。
但是真的如果handler的业务逻辑执行很慢(非阻塞,计算量大的情况),要不要拆呢,因为在服务器端我相信同一个时间用户的访问并发应该会超过CPU的核数?所以个人认为不到需要极端优化性能的前提下还是没必要去拆,太复杂。
所以说,该模型适用于能快速执行的业务逻辑的系统或者多核CPU系统,在某种场景或者程度上是可以这么理解。
题外话,假设handler线程里面出现堵塞,会不会让CPU处于空闲呢?回答这个问题钱这,我又回去翻阅了Think in java中线程的一章,首先看线程的四个状态:
- 新(New):线程对象已经创建,但尚未启动,所以不可运行
-
可运行(Runnable):意味着一旦时间分片机制有空闲的CPU周期提供给一个线程,那这个线程便可立即运行。因此线程可能在、也可能不在运行当众,但一旦条件许可,没有什么能阻止它的运行------它既没有“死”掉,也未被"堵塞"。
-
死(Dead):从自己的run()方法中返回后,一个线程便已经"死"掉。亦可调用stop()令其死掉。.
- 堵塞(Blocked):线程可以运行,但有种东西阻碍了它。若线程处于堵塞状态,调度机制可以简单的跳过它,不给他分配任何CPU时间。除非线程再次进入"可运行"状态,否则不会采取任何操作
那么何为线程堵塞:
-
调用sleep(毫秒数),使线程进入"睡眠"状态。在规定的时间内,这个线程是不会运行的。
-
用suspend()暂定了线程的执行。除非线程收到resume()消息,否则不会返回"可运行"状态。
- 用wait()暂停了线程的执行。除非线程收到notify()或者notifyAll()消息,否则不可变成"可运行".
- 线程正在等候一些IO(输入输出)操作完成。
- 线程实体调用另一个对象的"同步"方法,但那个对象处于锁定状态,暂时无法使用.
所以如果handler线程里面有堵塞操作,那么就多开几个线程让CPU去切换执行即可,至于开多少个合适,看堵塞的时间而定。这个淘宝内部有公式的,但是堵塞时间不一定,所以还是需要通过TPS测试才能确定.
模型二:多线程处理的Reactor模式
所以说上面那个模型也不能说不好,继续跟着Doug Lea的文章继续前行了。Doug Lea提出了几点模型可扩展性的目标(Scalability Goals).
- 优雅的流量降级(当客户端连接持续增大到很多时,能避免系统被压垮)
- 能够持续的控制资源的使用增长(CPU,memory,disk,bandwidth)
- 能满足一些性能方面的目标,包括低延迟,满足高峰需求,服务质量的保证。
- 分而自治(Divide-and-conquer)通常是最好的方式.其实就是模块化吧。
Doug Lea首先介绍了Divide-and-conquer,其核心是将任务划分成小任务来处理,并且是非堵塞的小任务.我猜应该是支持了我上述的理论,将较大的任务分拆成小任务来处理,可能可以更好的利用多核CPU的性能,所以才有了下面这个模型:
这个模型把原来handler(这个大handler也可叫worker线程)中的对数据解码,数据处理,对数据编码抽离出来行程一个小handler,用过netty的都知道,netty可以添加多个handler并顺序执行,不知道大家有没有思考过,handler是处理业务逻辑的,我为什么要写多个handler?我一个handler就可以写完。如果放到这个模型里面来,就可以理解了,netty希望我们自己将大的handler分解成一个一个小的handler进行执行,只不过默认情况下这些handler都是在一个线程里面顺序执行(其实就跟一个handler是一样的概念,所以默认情况下netty在worker的选择上是模型一)的,或者更多默认情况下,我们只写了一个handler而已。关于此模型,暂时认为讲到这里即可,暂时还不需要深入.
模型三:多Reator线程的Reator模式 (好怪的名字,将就下)
上述两个模型都有一个通病,Reactor的Acceptor中,首先是建立链接,然后再把链接分配给指定的handler线程去执行,那么我们知道,建立链接是个阻塞动作,操作很慢,那么Doug Lea为了匹配CPU和IO的速率(Use to match CPU and IO rates)提出了下面这个模型:
Doug Lea的所谓匹配IO和CPU的速率,其实所得直白点是因为只有一个Reator情况下,因为Reator只能一个一个处理创建连接这个请求,如果客户端连接数量一大,就会在Reator形成一个单线程操作,并且创建链接又是一个阻塞动作,只有前一个客户的链接创建完毕才会处理下一个客户的链接请求,性能直线下降。所以这个模型就有了mainReator和subReactor这两个reactor。mainReator负责将用户请求的链接提交给subReactor去执行,subReactor则负责建立链接,并将建立后的链接发送到具体的work线程去执行.
下一篇章:会先写netty所使用的reactor模型与其根据netty源码简化后的代码。.
相关推荐
总的来说,Netty的网络模型基于Java NIO,利用非阻塞I/O和事件驱动的机制,实现了高并发、低延迟的网络通信。同时,Netty提供的API简单易用,使得开发者可以更加专注于业务逻辑,而不是底层的网络细节。结合源码阅读...
1. `NioEventLoop`:这是Netty针对Java NIO实现的EventLoop类,其中包含了对选择器(Selector)的管理和事件的分发。 2. `ChannelHandlerContext`:它是Netty的上下文对象,用于封装了与特定Channel相关的所有操作...
Java网络编程领域中,NIO(Non-blocking Input/Output,非阻塞I/O)和Netty框架是两个关键概念。NIO是一种I/O模型,它与传统的BIO(Blocking I/O)模型不同,BIO在处理连接时一旦进行读写操作就会阻塞,直到数据传输...
Java异步NIO框架Netty实现高性能高并发无标题笔记 1. 背景 1.1. 惊人的性能数据 最近一个圈内朋友通过私信告诉我,通过使用Netty4 + Thrift压缩二进制编解码技术,他们实现了10W TPS(1K的复杂POJO对象)的跨 节点...
第二部分"NIO+Netty5各种RPC架构实战演练",则侧重于将NIO与Netty结合应用于实际的RPC(Remote Procedure Call)系统中。RPC允许程序调用另一个计算环境中执行的程序,而无需了解底层网络协议和细节。这部分可能涵盖...
3. **Netty的事件驱动模型**:Netty采用 reactor 模式,通过EventLoopGroup来管理事件循环线程,每个线程负责处理多个连接的事件。当有新连接、读写事件发生时,会触发相应的ChannelHandler进行处理。 4. **Netty的...
在 Netty 服务端,核心引擎采用了主从 Reactor 多线程模型,该模型在 Doug Lea 的“Scalable IO in Java”论文中有所提及,但 Netty 在实现上有所不同。 主 Reactor Group 和从 Reactor Group 是 Netty 中的两个...
在Netty中,Reactor线程模型是其核心设计之一,用于处理并发连接和网络I/O操作。本文将详细介绍Reactor的三种线程模型,并结合Netty的线程模型进行解析。 ### 1. Reactor 单线程模型 Reactor单线程模型是指所有的I...
1. **异步非阻塞I/O**:Netty采用基于Reactor模式的事件驱动架构,利用Java NIO的非阻塞I/O模型,提高了系统的并发能力,降低了CPU的等待时间。 2. **高性能**:Netty通过优化内存分配、零拷贝技术、高效的缓冲区...
Netty是基于Java NIO的一个高性能、异步事件驱动的网络应用程序框架,它简化了开发复杂的网络应用,比如TCP和UDP协议的应用。 Netty的整体结构复杂且灵活,它包含了多种组件,如Bootstrap、Channel、EventLoopGroup...
Mina是一个完全由Java编写的NIO框架,而Netty则是由JBOSS提供的一个高性能的异步事件驱动的网络应用框架,它们都是基于Java反应器模式设计的,用于处理大量并发连接的场景。 综上所述,基于Java NIO的反应器模式...
在Java中,NIO(非阻塞I/O)库为实现Reactor模式提供了基础。Netty基于NIO,并在其之上构建了一套高效且易用的网络编程框架。 Netty中的Reactor分为两个主要部分:主线程(BossGroup)和工作线程(WorkerGroup)。...
通过Netty基于Java NIO的https代理服务器的实现。 这是一个简单的工作原理: 要通过浏览器对其进行测试,我们将需要设置一个虚拟主机名,如下所示: 127.0.0.1 test.localdomain 这样浏览器就能将SNI发送到我们...
Socket NIO 单 Reactor 模式是一种在 Java 中实现高性能网络编程的技术,它结合了非阻塞I/O(New I/O,即NIO)和Reactor设计模式。本示例代码旨在帮助开发者理解如何使用Java NIO和Reactor模式构建网络服务。尽管...
Java Netty 面试题知识点总结 Java Netty 是一个基于 Java 的网络编程框架,使用 NIO 实现高...这些知识点是 Java Netty 面试题中的重要内容,掌握这些知识点能够帮助您更好地理解 Java Netty 框架和 NIO 编程模型。
Netty通过优化的Reactor线程模型解决了这些问题,它可以有效地处理大规模并发连接,减少线程创建和销毁的开销。 Netty的高性能主要体现在以下几个方面: 1. **传输层**:Netty支持多种传输模式,包括BIO、NIO和AIO...
Reactor Netty提供了多种性能优化选项,如线程模型的选择(NIO或Epoll)、内存池配置、连接超时等。根据具体的应用场景,合理配置这些选项可以进一步提升系统的性能。 总结,Reactor Netty是Java开发高性能网络应用...
Netty基于Reactor模式,采用主从线程模型,即一个BossGroup处理连接请求,多个WorkerGroup处理I/O事件。这种设计能够确保高并发下的性能和稳定性。 2. **Channel与Handler** Channel是Netty的核心概念,代表一个...
总的来说,Netty的Reactor模型通过高效地处理I/O事件和调度任务执行,实现了网络服务的高性能和可扩展性。理解Reactor的工作原理对于优化和调试Netty应用程序至关重要。本文提供的概述是后续深入研究Reactor如何处理...