netty权威指南学习笔记一——NIO入门(2)伪异步IO
在上一节我们介绍了四种IO相关编程的各个特点,并通过代码进行复习了传统的网络编程代码,伪异步主要是引用了线程池,对BIO中服务端进行了相应的改造优化,线程池的引入,使得我们在应对大量客户端请求的时候不会导致虚拟机可贵的线程资源耗尽而宕机,在本小节,我们来看看伪异步代码。多注意引入的线程池代码。
伪异步IO模型图如下():

当有新的客户端请求到来时,将socket封装成一个Task(该任务实现了Runnable接口),投递到线程池中进行处理。而线程池始终维护一个消息队列和N个活跃着的线程,这些线程被分配,对消息队列中的Task进行处理。由于线程池可以设置消息队列的大小和线程的数量,因此不论多少个客户端请求都不会导致资源耗尽和宕机。
下面附上刚改造完成的代码:为了便于理解,在代码的最后本博主添加上关于线程池运行的原理的基本知识,以便于理解伪I/O的代码。
改造后的服务端代码:
package com.example.biodemo; import java.io.*;
import java.net.ServerSocket;
import java.net.Socket; public class TimeServer {
public static void main(String[] args) throws IOException {
int port = 8090;
if (args != null && args.length > 0) {
try {
port = Integer.valueOf(args[0]);
} catch (NumberFormatException e) {
port = 8090;
}
}
ServerSocket server = null;
try {
server = new ServerSocket(port);
System.out.println("the timeServer is start in port :" + port);
Socket socket = null;
// 引入线程池start
TimeServerHandlerExecutePool singleExecutor = new TimeServerHandlerExecutePool(50,10000);
while (true) {
socket = server.accept();
// 替换BIO中new Thread(new TimeServerHandler(socket)).start();为下一行代码
singleExecutor.execute(new TimeServerHandler(socket));
// 引入线程池end }
} finally {
if (server != null) {
System.out.println("the time server close");
server.close();
server = null;
}
} }
}
服务端引用的线程池,自定义的线程池代码
package com.example.biodemo; 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){
// new ThreadPoolExecutor(线程池基本大小,线程池最大数量,存活时间,时间单位,消息队列)
executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),maxPoolSize,120L,
TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(queueSize)); }
// 该方法会不断从消息队列中获取任务执行
public void execute(java.lang.Runnable task){
executor.execute(task);
} }
下面是和上一小节一样的 TimeServerHandler和客户端代码
package com.example.biodemo; 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;
} @Override
public void run() {
BufferedReader in = null;
PrintWriter out = null;
try {
in = new BufferedReader(new InputStreamReader(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 el) {
el.printStackTrace();
}
}
if (out != null) {
out.close();
out = null;
}
if (this.socket != null) {
try {
this.socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
this.socket = null;
}
}
}
}
客户端代码
package com.example.biodemo; import java.io.*;
import java.net.Socket; public class TimeClient {
public static void main(String[] args) {
int port = 8090;
if (args != null && args.length > 0) {
try {
port = Integer.valueOf(args[0]);
} catch (NumberFormatException ne) {
port = 8090;
}
}
Socket socket = null;
BufferedReader in = null;
PrintWriter out = null;
try {
socket = new Socket("127.0.0.1", port);
System.out.println(socket.getInputStream());
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
out.println("QUERY TIME ORDER");
System.out.println("send order 2 server succeed.");
String resp = in.readLine();
System.out.println("now is :" + resp);
} catch (IOException e1) { } finally {
if (out != null) {
out.close();
out = null;
} if (in != null) {
try {
in.close();
} catch (IOException e2) {
e2.printStackTrace();
}
in = null;
if (socket != null) {
try {
socket.close();
} catch (IOException e3) {
e3.printStackTrace();
} }
socket = null;
} }
}
}
其运行与小节一结果一致。
下面为了便于理解添加的线程池相关的代码部分,附上一些线程池运行的原理等相关知识。

当提交一个新任务到线程池时,线程池的处理流程如上图所示,具体为:
1、判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程。

上面这个图是ThreadPoolExecutor 执行 executor的示意图。ThreadPoolExecutor 执行 executor方法分为下面4种情况:
1、如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。
2、如果运行的线程等于或者多于corePoolSize,则任务添加到BlockingQueue。
3、如果无法将任务加入到BlockingQueue(队列已满),则创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。
4、如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。
如上,相信大家能够更好的理解伪异步I/O与BIO的不同了。
netty权威指南学习笔记一——NIO入门(2)伪异步IO的更多相关文章
- netty权威指南学习笔记一——NIO入门(1)BIO
公司的一些项目采用了netty框架,为了加速适应公司开发,本博主认真学习netty框架,前一段时间主要看了看书,发现编程这东西,不上手还是觉得差点什么,于是为了加深理解,深入学习,本博主还是决定多动手 ...
- netty权威指南学习笔记一——NIO入门(3)NIO
经过前面的铺垫,在这一节我们进入NIO编程,NIO弥补了原来同步阻塞IO的不足,他提供了高速的.面向块的I/O,NIO中加入的Buffer缓冲区,体现了与原I/O的一个重要区别.在面向流的I/O中,可 ...
- netty权威指南学习笔记一——NIO入门(4)AIO
NIO2.0引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现.异步通道提供以下两种方式获取操作结果. 1.通过java.util.concurrent.Future 类来表示异步操 ...
- netty权威指南学习笔记二——netty入门应用
经过了前面的NIO基础知识准备,我们已经对NIO有了较大了解,现在就进入netty的实际应用中来看看吧.重点体会整个过程. 按照权威指南写程序的过程中,发现一些问题:当我们在定义handler继承Ch ...
- netty权威指南学习笔记六——编解码技术之MessagePack
编解码技术主要应用在网络传输中,将对象比如BOJO进行编解码以利于网络中进行传输.平常我们也会将编解码说成是序列化/反序列化 定义:当进行远程跨进程服务调用时,需要把被传输的java对象编码为字节数组 ...
- netty权威指南学习笔记八——编解码技术之JBoss Marshalling
JBoss Marshalling 是一个java序列化包,对JDK默认的序列化框架进行了优化,但又保持跟java.io.Serializable接口的兼容,同时增加了一些可调参数和附加特性,这些参数 ...
- netty权威指南学习笔记七——编解码技术之GoogleProtobuf
首先我们来看一下protobuf的优点: 谷歌长期使用成熟度高: 跨语言支持多种语言如:C++,java,Python: 编码后消息更小,更利于存储传输: 编解码性能高: 支持不同协议版本的兼容性: ...
- netty权威指南学习笔记五——分隔符和定长解码器的应用
TCP以流的方式进行数据传输,上层应用协议为了对消息进行区分,通常采用以下4中方式: 消息长度固定,累计读取到长度综合为定长LEN的报文后,就认为读取到了一个完整的消息,将计数器置位,重新开始读取下一 ...
- netty权威指南学习笔记三——TCP粘包/拆包之粘包现象
TCP是个流协议,流没有一定界限.TCP底层不了解业务,他会根据TCP缓冲区的实际情况进行包划分,在业务上,一个业务完整的包,可能会被TCP底层拆分为多个包进行发送,也可能多个小包组合成一个大的数据包 ...
随机推荐
- redis 之redis持久化rdb与aof
redis是内存型的数据库 重启服务器丢失数据 重启redis服务丢失数据 断电丢失数据 Redis是一种内存型数据库,一旦服务器进程退出,数据库的数据就会丢失,为了解决这个问题,Redis提供了两种 ...
- php后门拿下当前目录
Weevely是一个模拟类似telnet的连接的隐形PHP网页shell.它是Web应用程序后期开发的必备工具,可以作为隐形后门程序,也可以作为一个web外壳来管理合法的Web帐户,甚至可以免费托管. ...
- 警示框UIAlertController的使用(看完马上会用!!)
本文尽量图文并茂,并且提供对应的代码,确保看到这篇文章马上能够上手使用UIAlertController控件.-我要兑现我的务实宣言- 本文构思: 1.出具效果图,通过这种最直接方式了解该控件的展示效 ...
- mabatisplus-update
/** * <p> * 根据 whereEntity 条件,更新记录 * </p> * * @param entity 实体对象 (set 条件值,不能为 null) * @p ...
- Numpy 广播(Broadcast)
广播(Broadcast)是 numpy 对不同形状(shape)的数组进行数值计算的方式,对数组的算术运算通常在相应的元素上进行. 如果两个数组 a 和 b 形状相同,即满足a.shape == b ...
- Qt中QListWidget的verticalScrollMode选项设置为ScrollPerPixel无效果的原因
设置为ScrollPerPixel无效果,根据Qt手册的描述,需要在设置一次setSingleStep()的值,才会生效
- TP-Link TL-WR841N v14 CVE-2019-17147 缓冲区溢出漏洞分析笔记v2018.12.31
0x00 背景 Httpd服务中的缓冲区溢出漏洞 复现参考文章https://www.4hou.com/posts/gQG9 Binwalk -Me 解压缩 File ./bin/busybox文件类 ...
- css 盒子模型应用
盒子模型应用 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> < ...
- 大公司病,为什么要PPT背锅?
新东方年会上,一曲吐槽式的<释放自我>火遍网络,据说这六位员工,是冒着被解雇的风险完成演出的,歌词内容涉及到非常敏感的大企业病,如机构臃肿.效率低下.内耗甩锅.拍领导马屁等等,而最高潮的部 ...
- canvas象棋 画图
今天写了一个canvas画图的象棋 .js基础不行,只画了个图,以后补充... <!DOCTYPE html> <html lang="en"> <h ...