美文网首页
【网络编程】伪异步I/O编程

【网络编程】伪异步I/O编程

作者: 程就人生 | 来源:发表于2023-02-17 11:04 被阅读0次

上次说了BIO编程。为了解决同步阻塞I/O面临的一个链路需要一个线程处理的问题,后来人们对它的线程模型进行了优化,服务器端通过一个线程池来处理多个客户端的请求接入,形成客户端个数M对应线程池N的比例关系。通过线程池可以灵活调配线程资源,设置线程的最大值,防止由于海量并发客户端接入导致线程耗尽。这就是伪异步I/O编程。

如下图所示,采用线程池和任务队列可以实现伪异步I/O通信框架。

图片

伪异步I/O服务器端通信模型服务器端代码:

package com.test.aio1;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
 * 伪异步I/O服务器端
 * @author 程就人生
 * @Date
 */
public class HelloServer {

  public static void main( String[] args ){
    int port = 8080;
        ServerSocket serverSocket = null;
        try {
      serverSocket = new ServerSocket(port);
      Socket socket = null;
      HelloServerHandleExecutePool execute = new HelloServerHandleExecutePool(50, 1000);
      // 通过无限循环监听客户端连接
      while(true){
        // 如果没有客户端连接,则阻塞在Accept操作上
        socket = serverSocket.accept();
        // 如果有客户端连接,则加入线程池执行
        execute.execute(new HelloHandler(socket));
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
    }
}

/**
 * 线程池类
 * @author 程就人生
 * @Date
 */
class HelloServerHandleExecutePool{  

  private ExecutorService executorService;  
  
  public HelloServerHandleExecutePool(int maxPoolSize, int queueSize) {    
    executorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), maxPoolSize, 120L, TimeUnit.SECONDS, new ArrayBlockingQueue<java.lang.Runnable>(queueSize));
  }
  
  public void execute(java.lang.Runnable task){
    executorService.execute(task);
  }  
}

/**
 * hello处理类
 * @author 程就人生
 * @Date
 */
class HelloHandler implements Runnable{
  
  private Socket socket;

  public HelloHandler(Socket socket) {
    this.socket = socket;
  }

  public void run() {
    BufferedReader reader = null;
    PrintWriter writer = null;
    try {
      reader = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
      // 返回给客户端,是否刷新设置为true
      writer = new PrintWriter(this.socket.getOutputStream(), true);
      String body = null;
      while(true){
        body = reader.readLine();
        if(body == null){
          break;
        }
        System.out.println("服务器端接收到的:" + body);
        writer.println("来自服务器端的响应!");        
      }
    } catch (IOException e) {
      e.printStackTrace();
      // 出现异常时,对资源的释放
      if(reader != null){
        try {
          reader.close();
        } catch (IOException e1) {
          e1.printStackTrace();
        }
      }
      if(writer != null){
        writer.close();
        writer = null;
      }
      if(socket != null){
        try {
          socket.close();
        } catch (IOException e1) {
          e1.printStackTrace();
        }
      }
    }
  }  
}
伪异步I/O服务器端的主函数有了变化,先创建一个Hello服务处理类的线程池。当接收到新的客户端连接时,将请求soket封装成一个Task,然后调用线程池的execute方法执行,从而避免每个客户端连接时都创建一个新线程。由于线程池和消息队列都是有界的。因此,无论客户端并发连接数多大,都不会导致服务器端线程个数过于膨胀而导致内存溢出,相比于传统的BIO线程模型是一种改良。 运行服务器端结果。 图片 运行客户端结果 虽然伪异步I/O通信框架采用了线程池实现,因此避免了每个请求都创建一个独立线程造成的线程资源耗尽问题,但它的底层的通信依旧采用同步阻塞模型,因此无法从根本上解决问题。一起看下socket的InputStream方法。 图片 图片

再看下InputStream类。


图片

当对Socket的输入流进行读取操作时,它会一直堵塞下去,直到发生三种事件:

  • 有数据可读。
  • 可用数据已经读完。
  • 发生异常。

如果对方发生请求或者应答比较缓慢,或者网络传输比较慢,读取输入流一方的通信线程将会被长时间阻塞。如果对方要60s才能将数据发送完成,读取一方的I/O也会被阻塞60s。在这60s期间,其他接入的客户端只能在消息队列中排队。伪异步I/O只是对BIO线程模型的一个简单优化,无法从根本上解决同步IO导致的通信线程阻塞问题。
伪异步I/O的缺点显而易见。

  • 如果客户端请求量增加,会导致服务器端处理应答消息缓慢。
  • 如果有故障节点,由于读取输入流的阻塞的,它会被阻塞,直到释放。
  • 如果所有的线程都被故障服务器阻塞,那么后续的IO消息都将在队列中排队等等。

相关文章

  • I/O模型学习小记

    基础概念 通过I/O模型学习同步/异步、阻塞/非阻塞基础概念,参考资料如下:《Unix网络编程》《网络编程释疑之:...

  • java原生各种IO和Netty框架的对比

    异步非阻塞I/O JDK 1.4的NIO框架经常被称为异步非阻塞I/O,但是,如果严格按照UNIX网络编程模型和J...

  • 关于node.js的那些事(四)—异步编程的优势与难点

    有异步I/O,必有异步编程。异步编程有它的优势,也有它的难点,下面我们就这门语言异步编程的优势和难点来展开谈...

  • 深入浅出Node.js学习笔记(三)

    异步I/O 在众多高级编程语言或运行平台中,Node是首个将异步作为主要编程方式和设计理念。 Node的基调:异步...

  • 2018-03-12

    python异步与协程 异步编程: 异步I/O selet/poll/epoll 事件循环 + 回调共享状态管理困...

  • 异步I/O

    为什么要用异步I/O? 单线程同步编程会引阻塞I/O导致硬件资源得不到更优使用,多线程编程会出现死锁、状态同步等问...

  • 【文集】Unity中的异步编程技术

    什么是异步编程?异步编程是指由于异步 I/O 等因素,无法同步获得执行结果时,在回调函数 中进行下一步操作,这个技...

  • 单线程和多线程,异步式和同步式

    Node.js最大的特点就是异步式I/O与事件紧密结合的编程模式。这种模式与传统的同步式I/O线性的编程思路有很大...

  • Java NIO之Reactor和Preactor模型

    1、基础I/O模型 在《UNIX网络编程》中介绍了5中I/O模型:阻塞I/O、非阻塞I/O、I/O复用、SIGIO...

  • Java I/O 之Netty实战

    Netty实战 landon资深网络游戏服务器架构师 UNIX网络编程5种I/O模型 I/O复用 I/O 多路复用...

网友评论

      本文标题:【网络编程】伪异步I/O编程

      本文链接:https://www.haomeiwen.com/subject/oubmkdtx.html