前言:
最近由于公司项目重构,需要用SOA分布式架构。在这里我采用的是比较轻量级的ZBUS总线来实现。而使用ZBUS总线最关键的就是底层socket通信问题。这里的通信问题就涉及到了java的NIO非阻塞机制,下面就参考一些别的资料,做一些简要的分析。
首先来分析下阻塞IO与非阻塞IO的区别
1、阻塞IO:阻塞io是指jdk1.4之前版本面向流的io,服务端需要对每个请求建立一堆线程等待请求,而客户端发送请求后,先咨询服务端是否有线程相应,如果没有则会一直等待或者遭到拒 绝请求,如果有的话,客户端会线程会等待请求结束后才继续执行。
当并发量大,而后端服务或客户端处理数据慢时就会产生产生大量线程处于等待中,即上述的阻塞。
非阻塞IO:无阻塞io是使用单线程或者只使用少量的多线程,每个连接共用一个线程,当处于等待(没有事件)的时候线程资源可以释放出来处理别的请求,通过事件驱动模型当有accept/read/write等事件发生后通知(唤醒)主线程分配资源来处理相关事件。java.nio.channels.Selector就是在该模型中事件的观察者,可以将多个SocketChannel的事件注册到一个Selector上,当没有事件发生时Selector处于阻塞状态,当SocketChannel有accept/read/write等事件发生时唤醒Selector。
这个Selector是使用了单线程模型,主要用来描述事件驱动模型,要优化性能需要一个好的线程模型来使用,目前比较好的nio框架有Netty,apache的mina等。
从上图可以分析出,阻塞IO的缺点有:
1. 当客户端多时,会创建大量的处理线程。且每个线程都要占用栈空间和一些CPU时间
2. 阻塞可能带来频繁的上下文切换,且大部分上下文切换可能是无意义的。
而非阻塞IO就可以很好地解决这个问题,它的工作原理:
1. 由一个专门的线程来处理所有的 IO 事件,并负责分发。
2. 事件驱动机制:事件到的时候触发,而不是同步的去监视事件。
3. 线程通讯:线程之间通过 wait,notify 等方式通讯。保证每次上下文切换都是有意义的。减少无谓的线程切换。
Java NIO的服务端只需启动一个专门的线程来处理所有的 IO 事件,这种通信模型是怎么实现的呢?呵呵,我们一起来探究它的奥秘吧。java NIO采用了双向通道(channel)进行数据传输,而不是单向的流(stream),在通道上可以注册我们感兴趣的事件。一共有以下四种事件:
事件名 | 对应值 |
服务端接收客户端连接事件 | SelectionKey.OP_ACCEPT(16) |
客户端连接服务端事件 | SelectionKey.OP_CONNECT(8) |
读事件 | SelectionKey.OP_READ(1) |
写事件 | SelectionKey.OP_WRITE(4) |
下面是我理解的java NIO的通信模型示意图:
1、服务端和客户端各自维护一个管理通道的对象,我们称之为selector,该对象能检测一个或多个通道 (channel) 上的事件,下面以服务器端为例:
1). 首先需要进行初始化工作。该初始化工作主要目的是将selector(这时候的selector必须先要进行注册,这样才知道我们接下来要怎么处理数据)与channel进行绑定。只有selector管理通道与通道channel进行了绑定,在客户端发送消息,将selector绑定到channel通道的时候,服务器端才能通过该通道的端口,拿到并对服务器端数据进行操作。
- public void initServer(int port) throws IOException {
- // 获得一个ServerSocket通道
- ServerSocketChannel serverChannel = ServerSocketChannel.open();
- // 设置通道为非阻塞
- serverChannel.configureBlocking(false);
- // 将该通道对应的ServerSocket绑定到port端口
- serverChannel.socket().bind(new InetSocketAddress(port));
- // 获得一个通道管理器
- this.selector = Selector.open();
- //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
- //当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
- serverChannel.register(selector, SelectionKey.OP_ACCEPT);
- }
2).通过selector的监听,采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理 。
- @SuppressWarnings("unchecked")
- public void listen() throws IOException {
- System.out.println("服务端启动成功!");
- // 轮询访问selector
- while (true) {
- //当注册的事件到达时,方法返回;否则,该方法会一直阻塞
- selector.select();
- // 获得selector中选中的项的迭代器,选中的项为注册的事件
- Iterator ite = this.selector.selectedKeys().iterator();
- while (ite.hasNext()) {
- SelectionKey key = (SelectionKey) ite.next();
- // 删除已选的key,以防重复处理
- ite.remove();
- // 客户端请求连接事件
- if (key.isAcceptable()) {
- ServerSocketChannel server = (ServerSocketChannel) key
- .channel();
- // 获得和客户端连接的通道
- SocketChannel channel = server.accept();
- // 设置成非阻塞
- channel.configureBlocking(false);
- //在这里可以给客户端发送信息哦
- channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息").getBytes()));
- //在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
- channel.register(this.selector, SelectionKey.OP_READ);
- // 获得了可读的事件
- } else if (key.isReadable()) {
- read(key);
- }
- }
- }
- }
- /**
- * 处理读取客户端发来的信息 的事件
- * @param key
- * @throws IOException
- */
- public void read(SelectionKey key) throws IOException{
- // 服务器可读取消息:得到事件发生的Socket通道
- SocketChannel channel = (SocketChannel) key.channel();
- // 创建读取的缓冲区
- ByteBuffer buffer = ByteBuffer.allocate(10);
- buffer.clear();
- channel.read(buffer);
- byte[] data = buffer.array();
- String msg = new String(data).trim();
- System.out.println("服务端收到信息:"+msg);
- ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
- channel.write(outBuffer);// 将消息回送给客户端
- }
- /**
- * 启动服务端测试
- * @throws IOException
- */
- public static void main(String[] args) throws IOException {
- NIOServer server = new NIOServer();
- server.initServer(8000);
- server.listen();
- }
- }
代码中关键的几个点在:
Selector.open();
selector.select();
阻塞后唤醒可以通过注册在selector上的socket有事件发生 或者 selector.select(timeOut)超时 或者 selector.wakeup()主动唤醒;
整个阻塞和唤醒的过程涉及到的点非常多,先上一张梳理出的整体图,再进入源码会比较容易理解
具体的源码分析就不在这里一一说明了。今天注定是个难忘的日子,昨天深圳下了100多年来的第一场雪,今天周一来上班,好冷好冷,写这篇文章手都快僵了!!!!
具体详解,可参考:
http://weixiaolu.iteye.com/blog/1479656
http://zhhphappy.iteye.com/blog/2032893
相关推荐
### Java NIO原理 图文分析及代码实现 #### 前言 在深入探讨Java NIO之前,我们先简要回顾一下NIO的概念及其引入的原因。随着互联网的发展,越来越多的应用程序需要处理高并发的网络连接请求。传统的阻塞I/O模型在...
**Java IO原理:** Java IO基于流模型,分为输入流和输出流。流是一维数据序列,可以是从源到目标的单向流动。IO操作是阻塞的,即当一个线程执行读或写操作时,如果数据未准备好,线程会被阻塞,直到数据准备就绪。...
理解并掌握NIO的原理和使用,对于提升Java应用的性能和可扩展性至关重要。通过以上介绍的知识点,你可以开始编写基于NIO的应用,例如使用SocketChannel实现一个简单的非阻塞服务器。在实际编码时,参考博文链接中的...
本文将深入探讨Java NIO中的Selector机制,并通过源码分析来理解其实现原理。 Selector机制是Java NIO中的核心组件,它允许单线程同时监控多个通道(Channels)的状态变化,例如连接就绪、数据可读或可写等。这种...
- **Java NIO原理及通信模型**:NIO采用了非阻塞的方式,通过一个单独的线程来处理所有IO事件,使用事件驱动机制,当事件发生时才进行处理。它依赖于选择器(Selector)来监控多个通道(Channel)上的事件,如连接...
Java NIO,全称为Non-Blocking Input/Output(非阻塞输入/输出),是Java标准库提供的一种替代传统I/O模型的新...通过分析和运行这个示例,开发者可以更深入地理解Java NIO的工作原理,并能更好地运用到实际项目中。
Java NIO(New IO)是Java 1.4版本引入的一种新的I/O API,它提供了非阻塞I/O操作的能力,极大地提升了Java在...通过分析和学习这个源码,开发者可以深入理解Java NIO的工作原理,并将其应用于实际的网络编程项目中。
Java NIO(New IO)是Java 1.4版本引入的一个新模块,全称为New Input/Output,是对传统IO API的扩展...通过分析和理解这个项目,开发者不仅可以深入理解NIO的工作原理,还能掌握如何将NIO应用到实际的并发网络编程中。
Java NIO,全称为New Input/Output,是Java在1.4版本引入的一个新特性,旨在提供一种更高效、更具选择性的I/O模型。...通过分析NIO源码,我们可以深入了解其内部工作原理,进一步优化和调试相关代码。
### Java NIO与ByteBuffer详解 #### 一、Java NIO简介 Java NIO(New I/O)是Java SE 1.4版本引入的一种新的I/O处理方式,它提供了与传统BIO(Blocking I/O)不同的数据处理机制。NIO的核心组件包括`Channels`、`...
在JDK 1.4及后续版本中,原生的IO包(java.io.*)与NIO进行了良好的集成。许多传统IO类,如FileInputStream和FileOutputStream,现在可以直接使用块I/O方法,提升效率。同时,NIO库也可用于实现标准IO功能,提供了更...
此书对于希望深入理解Java NIO工作原理、以及如何在应用中有效利用NIO特性的开发者来说,是一本宝贵的资源。它不仅提供了理论知识,还包含了许多实际的代码示例和应用场景分析,帮助读者更好地掌握NIO的使用。
本文探讨了Java NIO(New I/O)框架中的非阻塞通信机制,并对其原理及应用进行了深入研究。NIO是一种现代I/O处理方法,通过引入缓冲区、通道和选择器等新概念,显著提升了文件处理和网络服务器程序的性能。本文首先...
这些例子可以帮助开发者深入理解NIO的工作原理,掌握如何在实际项目中应用NIO技术,提高Java应用程序的性能和并发处理能力。通过分析和学习这些示例,可以更好地理解和实践Java NIO的各种特性。
源码分析可以帮助我们深入理解NIO的工作原理,从而更好地利用它来优化我们的应用程序。在"nioSamples"这个压缩包中,可能包含了各种NIO场景的示例代码,可以作为学习和调试NIO的参考。 通过学习和实践这些源码,...
### Java NIO 详细讲解 #### 一、引言与背景 随着计算机技术的发展和互联网应用的日益...通过理解NIO的核心概念(如Channel和Buffer)、工作原理以及高级特性,开发者可以更好地利用这项技术来优化应用程序的性能。
通过学习这些例子,你可以深入理解NIO的工作原理和实际应用。 总结来说,TCP/IP协议提供了网络通信的基础,而NIO则是Java中提高I/O性能和并发处理能力的关键技术。在分布式Java应用中,结合两者可以构建出高效、可...
`NonBlockingServer.java`和`Client.java`的代码分析和实践,可以帮助我们更好地理解和掌握Java NIO在Socket通信中的应用。在实际开发中,我们还需要考虑异常处理、资源管理等细节,以确保系统的稳定性和可靠性。