伪异步IO编程

BIO主要的问题在于每当有一个新的客户端请求接入时,服务端必须创建一个新的线程处理新接入的客户端链路,一个线程只能处理一个客户端连接。在高性能服务器应用领域,往往需要面向成千上万个客户端的并发连接,这种模型显然无法满足高性能、高并发接入的场景。为了改进一线程一连接模型,后来又演进出了一种通过线程池或者消息队列实现1个或者多个线程处理N个客户端的模型,由于它的底层通信机制依然使用同步阻塞I/O,所以被称为“伪异步”。

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

当有新的客户端接入的时候,将客户端的Socket封装成一个Task(该任务实现java.lang.Runnable接口)投递到后端的线程池中进行处理,JDK的线程池维护一个消息队列和N个活跃线程对消息队列中的任务进行处理。由于线程池可以设置消息队列的大小和最大线程数,因此,它的资源占用是可控的,无论多少个客户端并发访问,都不会导致资源的耗尽和宕机。

服务端示例:

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class TimeServer {//首先创建一个时间服务器处理类的线程池,当接收到新的客户端连接的时候,将请求Socket封装成一个Task,//然后调用线程池的execute方法执行,从而避免了每个请求接入都创建一个新的线程。public static void main(String[] args) throws IOException {int port = 8080;if (args != null && args.length > 0) {try {port = Integer.valueOf(args[0]);} catch (NumberFormatException e) {// 采用默认值
            }}ServerSocket server = null;try {server = new ServerSocket(port);System.out.println("The time server is start in port : " + port);Socket socket = null;TimeServerHandlerExecutePool singleExecutor = new TimeServerHandlerExecutePool(50,10000);//创建I/O任务线程池while (true) {socket = server.accept();singleExecutor.execute(new TimeServerHandler(socket));}} finally {if (server != null) {System.out.println("The time server close");server.close();server = null;}}}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class TimeServerHandler implements Runnable {private Socket socket;public TimeServerHandler(Socket socket) {this.socket = socket;}@Overridepublic void run() {BufferedReader in = null;PrintWriter out = null;try {in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));out = new PrintWriter(this.socket.getOutputStream(), true);String currentTime = null;String body = null;while (true) {body = in.readLine();if (body == null)break;System.out.println("The time server receive order : " + body);currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new java.util.Date(System.currentTimeMillis()).toString() : "BAD ORDER";out.println(currentTime);}} catch (Exception e) {if (in != null) {try {in.close();} catch (IOException e1) {e1.printStackTrace();}}if (out != null) {out.close();out = null;}if (this.socket != null) {try {this.socket.close();} catch (IOException e1) {e1.printStackTrace();}this.socket = null;}}}
}
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class TimeServerHandlerExecutePool {private ExecutorService executor;public TimeServerHandlerExecutePool(int maxPoolSize, int queueSize) {executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), maxPoolSize, 120L, TimeUnit.SECONDS,new ArrayBlockingQueue(queueSize));}public void execute(java.lang.Runnable task) {executor.execute(task);}
}

由于线程池和消息队列都是有界的,因此,无论客户端并发连接数多大,它都不会导致线程个数过于膨胀或者内存溢出,相比于传统的一连接一线程模型,是一种改良。客户端代码并没有改变,详见BIO的类。

伪异步I/O弊端分析

I/O类库输入流InputStreamAPI说明:

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

  1. 有数据可读;
  2. 可用数据已经读取完毕;
  3. 发生空指针或者I/O异常。

这意味着当对方发送请求或者应答消息比较缓慢、或者网络传输较慢时,读取输入流一方的通信线程将被长时间阻塞,如果对方要60s才能够将数据发送完成,读取一方的I/O线程也将会被同步阻塞60s,在此期间,其他接入消息只能在消息队列中排队。

I/O类库输出流OutputStream的API文档:

当调用OutputStream的write方法写输出流的时候,它将会被阻塞,直到所有要发送的字节全部写入完毕,或者发生异常。学习过TCP/IP相关知识的人都知道,当消息的接收方处理缓慢的时候,将不能及时地从TCP缓冲区读取数据,这将会导致发送方的TCP window size不断减小,直到为0,双方处于Keep-Alive状态,消息发送方将不能再向TCP缓冲区写入消息,这时如果采用的是同步阻塞I/O,write操作将会被无限期阻塞,直到TCP window size大于0或者发生I/O异常。

故障分析

通过对输入和输出流的API文档进行分析,我们了解到读和写操作都是同步阻塞的,阻塞的时间取决于对方I/O线程的处理速度和网络I/O的传输速度。本质上来讲,我们无法保证生产环境的网络状况和对端的应用程序能足够快,如果我们的应用程序依赖对方的处理速度,它的可靠性就非常差。也许在实验室进行的性能测试结果令人满意,但是一旦上线运行,面对恶劣的网络环境和良莠不齐的第三方系统,问题就会如火山一样喷发。

伪异步I/O实际上仅仅只是对之前I/O线程模型的一个简单优化,它无法从根本上解决同步I/O导致的通信线程阻塞问题。下面我们就简单分析下如果通信对方返回应答时间过长,会引起的级联故障。

(1)服务端处理缓慢,返回应答消息耗费60s,平时只需要10ms。

(2)采用伪异步I/O的线程正在读取故障服务节点的响应,由于读取输入流是阻塞的,因此,它将会被同步阻塞60s。

(3)假如所有的可用线程都被故障服务器阻塞,那后续所有的I/O消息都将在队列中排队。

(4)由于线程池采用阻塞队列实现,当队列积满之后,后续入队列的操作将被阻塞。

(5)由于前端只有一个Accptor线程接收客户端接入,它被阻塞在线程池的同步阻塞队列之后,新的客户端请求消息将被拒绝,客户端会发生大量的连接超时。

(6)由于几乎所有的连接都超时,调用者会认为系统已经崩溃,无法接收新的请求消息。

转载于:https://www.cnblogs.com/wade-luffy/p/6164223.html

JDK 伪异步编程(线程池)相关推荐

  1. 异步编程-线程实现异步编程

    异步编程-线程实现异步编程 使用线程实现异步 第一种方式 第二种方式 问题 在日常开发中我们经常会遇到这样的情况,即需要异步地处理一些事情,而不需要知道异步任务的结果.比如在调用线程里面异步打日志,为 ...

  2. Springboot异步任务线程池

    文章目录 1. 启动类添加@EnableAsync注解 2. 异步方法添加@Async注解 3. 自定义线程池以及线程池异常策略 1. 启动类添加@EnableAsync注解 package com. ...

  3. 多线程编程—线程池的实现

    多线程编程-线程池的实现 执行与任务分离的组件- 线程池 https://github.com/wangbojing/threadpool 多线程技术主要解决了处理器单元内多个线程执行的问题,它可以显 ...

  4. python线程池原理_Django异步任务线程池实现原理

    这篇文章主要介绍了Django异步任务线程池实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 当数据库数据量很大时(百万级),许多批量数据修改 ...

  5. Java 并发编程 -- 线程池源码实战

    一.概述 小编在网上看了好多的关于线程池原理.源码分析相关的文章,但是说实话,没有一篇让我觉得读完之后豁然开朗,完完全全的明白线程池,要么写的太简单,只写了一点皮毛,要么就是是晦涩难懂,看完之后几乎都 ...

  6. JDK四种常见线程池及使用场景、两种提交任务的方法

    转载:JDK 提供的线程池及使用场景 - 编程猎人 (programminghunter.com) 目录 四种常见线程池 1.newFixedThreadPool 2.newSingleThreadE ...

  7. java 任务池_多线程的应用-异步任务线程池的简单实现

    对于服务端的应用而言,经常会出现比如定时任务,或者任务的异步执行,熟悉Java开发的开发者,经常会使用Executors类,其提供了4种不同的线程池: ​newCachedThreadPool, ne ...

  8. Java并发编程——线程池的使用

    在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统 ...

  9. java并发编程——线程池的工作原理与源码解读

    2019独角兽企业重金招聘Python工程师标准>>> 线程池的简单介绍 基于多核CPU的发展,使得多线程开发日趋流行.然而线程的创建和销毁,都涉及到系统调用,比较消耗系统资源,所以 ...

最新文章

  1. Spring Session - 使用Spring Session从零到一构建分布式session
  2. map容器的使用 逆序遍历map容器当中的数据
  3. 自然语言处理与文本检索
  4. Vue的钩子函数是什么意思?Vue都有哪些钩子函数?
  5. win7旗舰版梦幻桌面高清视频下载
  6. 如何批量等比例缩放图片尺寸?
  7. vmware使用显卡
  8. An Introduction to Be-trees and Write Optimization 学习笔记
  9. flex justify-content: space-around;和justify-content: space-evenly;的区别
  10. 游戏光枪坐标定位原理及算法
  11. HTML基础---01
  12. openFOAM中的Scalar
  13. 关于直播电商平台的一些数据
  14. SCI论文从入门到精通——IEEE论文那些事儿
  15. 如何从 OpenVINO 的主分支构建 Python Wheel 包
  16. 东北天(ENU)和北东地(NED)
  17. python效率计算-提高python中重心坐标计算的效率
  18. 如何在PDF里添加页码,添加页码的步骤
  19. 简单几步就可以把pdf转换成excel格式
  20. 利用电影贴吧引流轻松变现,超适合新手

热门文章

  1. 腾讯PCG光影研究室招聘计算机视觉算法/实习生
  2. 2019年财政收支分析_2019年模锻行业经济运行情况分析
  3. 机器学习(十五)隐马尔科夫模型-未完待续
  4. apache php 工作模式,PHP Apache中两种工作方式区别(CGI模式、Apache 模块DLL)
  5. 不需要安全实验证也可以开微信号_热门行业:电子专用设备工程师证考试报名时间及报名须知...
  6. PHP输出一段励志的话,一段励志的好句子,经典透彻!
  7. 用字典存储学生成绩查询_中考用分数评价学生音体美成绩未尝不是好事
  8. oracle没用过元数据,案例:Oracle RAC asm备份元数据之md_backup和md_restore 好处与
  9. Windows server 2012 r2更新
  10. 软考网络管理员学习笔记4之第四章局域网技术