传统同步阻塞I/O(BIO)

在NIO之前编写服务器使用的是同步阻塞I/O(Blocking I/O)。下面是一个典型的线程池客服端服务器示例代码,这段代码在连接数急剧上升的情况下,这个服务器代码就会不好使了,因为serverSocket.accept(),以及IO的read(),write()方法都是同步阻塞的,虽然通过线程池,避免频繁创建线程开销,但是该系统过于依赖线程,一个是线程的创建和销毁很耗时,再者线程的切换开销很大,尤其是在高并发的情况下系统压力不堪设想。

BIO线程池客服端服务器示例代码

  1. /**
  2. * BIO服务器
  3. * @author monkjavaer
  4. * @date 2019/7/17 13:55
  5. */
  6. public class BioServer {
  7. public static final int PORT = 8888;
  8. public static void main(String[] args) {
  9. ServerSocket serverSocket = null;
  10. try {
  11. serverSocket = new ServerSocket(PORT);
  12. Socket socket = null;
  13. ThreadFactory namedThreadFactory = new ThreadFactory() {
  14. @Override
  15. public Thread newThread(Runnable r) {
  16. Thread t = new Thread(r);
  17. t.setName("demo-pool-%d");
  18. return t;
  19. }
  20. };
  21. //通过线程池,避免频繁创建线程开销
  22. ExecutorService singleThreadPool = new ThreadPoolExecutor(1, 1,
  23. 0L, TimeUnit.MILLISECONDS,
  24. new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
  25. //主线程死循环等待新连接到来
  26. while(!Thread.currentThread().isInterrupted()){
  27. socket = serverSocket.accept();
  28. singleThreadPool.execute(new BioServerHandler(socket));
  29. }
  30. } catch (IOException e) {
  31. //TODO异常处理
  32. } finally {
  33. //TODO关闭资源
  34. }
  35. }
  36. }
  1. /**
  2. * BIO服务器事件处理方法
  3. * @author monkjavaer
  4. * @date 2019/7/17 14:00
  5. */
  6. public class BioServerHandler implements Runnable {
  7. private Socket socket;
  8. public BioServerHandler(Socket socket) {
  9. this.socket = socket;
  10. }
  11. @Override
  12. public void run() {
  13. try {
  14. byte[] input = new byte[1024];
  15. //服务器接收的数据
  16. socket.getInputStream().read(input);
  17. byte[] output = "服务器返回数据".getBytes();
  18. socket.getOutputStream().write(output);
  19. } catch (IOException e) {
  20. //TODO异常处理
  21. } finally {
  22. //TODO关闭资源
  23. }
  24. }
  25. }
  1. /**
  2. * 服务端
  3. * @author monkjavaer
  4. * @date 2019/7/17 15:06
  5. */
  6. public class BioClient {
  7. public static final int PORT = 8888;
  8. public static final String IP = "127.0.0.1";
  9. public static void main(String[] args) {
  10. Socket socket = null;
  11. PrintWriter printWriter = null;
  12. try {
  13. socket = new Socket(IP,PORT);
  14. socket.setSoTimeout(5000);
  15. printWriter = new PrintWriter(socket.getOutputStream());
  16. printWriter.println("客户端发送数据");
  17. printWriter.flush();
  18. } catch (IOException e) {
  19. //TODO异常处理
  20. } finally {
  21. //TODO关闭资源
  22. }
  23. }
  24. }

NIO(非阻塞I/O)

NIO就是非阻塞I/O(Non-blocking I/O)

NIO重要组件回顾

  • 缓冲区(Buffer):一个Buffer对象是固定数量的数据的容器。其作用是一个存储器,或者分段运输区,在这里数据可被存储并在之后用于检索。ByteBuffer、IntBuffer、CharBuffer、LongBuffer、DoubleBuffer、FloatBuffer、ShortBuffer都是其实现类。
  • 通道(Channel):Channel 用于在字节缓冲区和位于通道另一侧的实体(通常是一个文件或套接字)之间有效地传输数据。 Channel是全双工的。
  • 选择器(Selector):Selector是NIO的多路复用器。Selector会不断轮询注册在它上面的通道Channel,找出就绪状态的Channel(Channel通道发生读、写事件)。Selector是基于底层操作系统机制,不同模式、不同版本都存在区别。Linux 上依赖于epoll;所以没有最大句柄的限制,因此一个线程做Selector轮询就能接入大量的客户端连接。

NIO服务器示例代码

NIO实现服务器代码步骤非常多,比较繁杂,所以推荐使用成熟的NIO框架Netty等。

  1. public class NioServer implements Runnable {
  2. private static Logger LOGGER = LoggerFactory.getLogger(NioServer.class);
  3. @Override
  4. public void run() {
  5. try {
  6. //1、打开ServerSocketChannel,监听客户端的链接
  7. ServerSocketChannel serverSocket = ServerSocketChannel.open();
  8. //2、绑定监听端口,设置backlog(默认50):请求传入连接队列的最大长度
  9. serverSocket.socket().bind(new InetSocketAddress(9011), 1024);
  10. //3、false,设置为非阻塞模式
  11. serverSocket.configureBlocking(false);
  12. //4、创建Selector,Selector是NIO的多路复用器,Selector会不断轮询注册在它上面的通道Channel,
  13. //找出就绪状态的Channel(Channel通道发生读、写事件)。
  14. Selector selector = Selector.open();
  15. //5、注册通道Channel到多路复用器Selector,并说明关注点SelectionKey.OP_ACCEPT,监听ACCEPT事件
  16. serverSocket.register(selector, SelectionKey.OP_ACCEPT);
  17. LOGGER.info("Listening on port {}" , 9011);
  18. //6、Selector轮询就绪的Channel
  19. while (true) {
  20. // 阻塞等待就绪的 Channel,这是关键点之一
  21. //selector1秒被唤醒
  22. int n = selector.select(1000);
  23. if (n == 0) {
  24. continue;
  25. }
  26. Set<SelectionKey> selectedKeys = selector.selectedKeys();
  27. Iterator<SelectionKey> iter = selectedKeys.iterator();
  28. while (iter.hasNext()) {
  29. SelectionKey key = iter.next();
  30. if (key.isValid()) {
  31. if (key.isAcceptable()) {
  32. //SelectionKey可以获取就绪状态的Channel
  33. ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
  34. //7、多路复用器Selector监听到有新的客户端连接,完成TCP三次握手建立连接。
  35. SocketChannel clientSocketChannel = serverSocketChannel.accept();
  36. //8、设置客户端SocketChannel为非阻塞模式
  37. clientSocketChannel.configureBlocking(false);
  38. //9、注册加入新的通道OP_READ
  39. clientSocketChannel.register(selector, SelectionKey.OP_READ);
  40. }
  41. //读取客户端数据
  42. //if(key.isReadable())等价于if((key.readyOps( ) & SelectionKey.OP_READ) != 0)
  43. if (key.isReadable()) {
  44. SocketChannel socketChannel = (SocketChannel) key.channel();
  45. //创建buffer
  46. ByteBuffer readBuffer = ByteBuffer.allocate(1024);
  47. int readPosition = socketChannel.read(readBuffer);
  48. if (readPosition > 0) {
  49. //flip()方法,Buffer从写模式切换到读模式,将limit设置为position,position设为0。
  50. readBuffer.flip();
  51. byte[] bytes = new byte[readBuffer.remaining()];
  52. //从可读buffer中读取数据
  53. readBuffer.get(bytes);
  54. LOGGER.info("接收客户端发送消息:{}" , new String(bytes, StandardCharsets.UTF_8));
  55. byte[] sendBytes = "server 收到".getBytes();
  56. ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
  57. writeBuffer.flip();
  58. //put 向buffer添加元素
  59. writeBuffer.put(sendBytes);
  60. socketChannel.write(writeBuffer);
  61. }
  62. if (readPosition < 0) {
  63. // Close channel on EOF, invalidates the key
  64. key.cancel();
  65. socketChannel.close();
  66. }
  67. }
  68. }
  69. iter.remove();
  70. }
  71. }
  72. } catch (IOException e) {
  73. e.printStackTrace();
  74. }
  75. }
  76. public static void main(String[] args) {
  77. new Thread(new NioServer()).start();
  78. }
  79. }
  1. public class NioClient {
  2. private static Logger LOGGER = LoggerFactory.getLogger(NioClient.class);
  3. private static int PORT = 9011;
  4. private static String[] messages = {"这是服务器"};
  5. public static void main(String[] args) {
  6. try {
  7. SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(InetAddress.getLocalHost(), PORT));
  8. for (String msg : messages) {
  9. ByteBuffer myBuffer = ByteBuffer.allocate(1024);
  10. myBuffer.put(msg.getBytes());
  11. myBuffer.flip();
  12. socketChannel.write(myBuffer);
  13. }
  14. LOGGER.info("Closing Client connection...");
  15. socketChannel.close();
  16. } catch (IOException e) {
  17. LOGGER.error(e.getMessage());
  18. }
  19. }
  20. }

Reactor模式

Reactor模式首先是事件驱动的,有一个或多个并发输入源,有一个Service Handler,有多个Request Handlers;这个Service Handler会同步的将输入的请求(Event)多路复用的分发给相应的Request Handler。是一种为处理并发服务请求,并将请求提交到一个或者多个服务处理程序的事件设计模式。

Reactor模式模块组成

http://www.blogjava.net/DLevin/archive/2015/09/02/427045.html

Scalable IO in Java原文和翻译

http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf

https://www.cnblogs.com/luxiaoxun/archive/2015/03/11/4331110.html

Reactor An Object Behavioral Pattern for Demultiplexing and Dispatching Handles for Synchronous Events

http://www.dre.vanderbilt.edu/~schmidt/PDF/reactor-siemens.pdf

JAVA BIO,NIO,Reactor模式总结的更多相关文章

  1. tomcat bio nio apr 模式性能测试

    转自:tomcat bio nio apr 模式性能测试与个人看法 11.11活动当天,服务器负载过大,导致部分页面出现了不可访问的状态.那后来主管就要求调优了,下面是tomcat bio.nio.a ...

  2. Java BIO NIO 与 AIO

    回顾 上一章我们介绍了操作系统层面的 IO 模型. 阻塞 IO 模型. 非阻塞 IO 模型. IO 复用模型. 信号驱动 IO 模型(用的不多,知道个概念就行). 异步 IO 模型. 并且介绍了 IO ...

  3. Java NIO Reactor模式

    一.NIO介绍: NIO模型: 1.Channel为连接通道,相当于一个客户端与服务器的一个连接,Selector为通道管理器,将Channel注册到Selector上,Selector管理着这些Ch ...

  4. JAVA bio nio aio

    [转自]http://qindongliang.iteye.com/blog/2018539 在高性能的IO体系设计中,有几个名词概念常常会使我们感到迷惑不解.具体如下: 序号 问题 1 什么是同步? ...

  5. java BIO/NIO/AIO 学习

    一.了解Unix网络编程5种I/O模型 1.1.阻塞式I/O模型 阻塞I/O(blocking I/O)模型,进程调用recvfrom,其系统调用直到数据报到达且被拷贝到应用进程的缓冲区中或者发生错误 ...

  6. 3. 彤哥说netty系列之Java BIO NIO AIO进化史

    你好,我是彤哥,本篇是netty系列的第三篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 上一章我们介绍了IO的五种模型,实际上Java只支持其中的三种,即BIO/NIO/ ...

  7. Java IO的Reactor模式

    1.    Reactor出现的原因 Reator模式是大多数IO相关组件如Netty.Redis在使用时的IO模式,为什么需要这种模式,如何设计来解决高性能并发的呢? 最最原始的网络编程思路就是服务 ...

  8. java BIO/NIO

    一.BIO Blocking IO(即阻塞IO); 1.      特点: a)   Socket服务端在监听过程中每次accept到一个客户端的Socket连接,就要处理这个请求,而此时其他连接过来 ...

  9. java BIO NIO IO

    参考 https://www.cnblogs.com/zedosu/p/6666984.html 摘要: 关于BIO和NIO的理解 最近大概看了ZooKeeper和Mina的源码发现都是用Java N ...

随机推荐

  1. Win10《芒果TV》商店版更新v3.2.1:优化手机版卡顿,修复推送故障

    此版本是小版本更新,主要是修复上一版本发布后暴露的部分体验问题,以免进一步扩大影响,小幅修复后更新上线. 芒果TV UWP V3.2.1更新内容清单: 1.优化和修复列表预加载机制的本地保存丢失导致的 ...

  2. VS点击调试卡住的问题解决方案(转载)

    本来今天好好的,不知道弄到了什么,调试不了了,一点击立马卡住,就一直在那转,就在网上找了找解决方案,下面给大家列出来几种可能会卡住的问题已经解决方案 1:加载调试符号引起的卡住 解决方案: 在“选项” ...

  3. CSS3 GENERATOR可以同时为一个元素完成border-radius、box-shadow、gradient和opacity多项属性的设置

    CSS3 GENERATOR可以同时为一个元素完成border-radius.box-shadow.gradient和opacity多项属性的设置 CSS3 GENERATOR 彩蛋爆料直击现场 CS ...

  4. file.delete()与file.deleteOnExit(); 的区别

    file.delete()   //删除文件,删除的是创建File对象时指定与之关联创建的那个文件.这是一个立刻执行的操作 file.deleteOnExit();   //在JVM进程退出的时候删除 ...

  5. Linux编辑器Vim和Emacs入门

    sudo 命令 debian系统没有自带,需要安装: apt-get install sudo 安装位置为 /usr/bin/sudo,对应配置文件为 /etc/sudoers sudoers授权格式 ...

  6. [2017.02.21-22] 《Haskell趣学指南 —— Learning You a Haskell for Great Good!》

    {- 2017.02.21-22 <Haskell趣学指南 -- Learning You a Haskell for Great Good!> 学习了Haskell的基本语法,并实现了一 ...

  7. RocketMQ(1)-架构原理

    RocketMQ(1)-架构原理 RocketMQ是阿里开源的分布式消息中间件,跟其它中间件相比,RocketMQ的特点是纯JAVA实现:集群和HA实现相对简单:在发生宕机和其它故障时消息丢失率更低. ...

  8. github 上传更新代码(最简单的方法)

    1.首先你需要一个github账号,还没有的朋友先去注册一个吧! GitHub地址:https://github.com/ 我们使用git需要先安装git工具,这里给出下载地址,下载后一路直接安装即可 ...

  9. Spring Framework 组件注册 之 @Import

    Spring Framework 组件注册 之 @Import 写在前面 向spring中注册组件或者叫javaBean是使用spring的功能的前提条件.而且spring也提供了很多种方式,让我们可 ...

  10. Storm 学习之路(六)—— Storm项目三种打包方式对比分析

    一.简介 在将Storm Topology提交到服务器集群运行时,需要先将项目进行打包.本文主要对比分析各种打包方式,并将打包过程中需要注意的事项进行说明.主要打包方式有以下三种: 第一种:不加任何插 ...