Scalable IO in Java
本文翻译Doug Lea的著名文章《Scalable IO in Java》,文章链接http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf
概述
可扩展的网络服务
事件驱动处理
Reactor模型
基础版本
多线程版本
其他变种
java.nio nonblocking IO APIs
网络服务
web服务,分布式对象等等
大多数的基础结构为
读请求
解码请求内容
处理服务
编码回复内容
发送回复
但是每一步在本质上和资源消耗都不相同,XML解析、文件转换、网页生成、计算服务等
针对读取数据,解码,计算,编码,发送整个流程,每个处理器都在各自的线程中开始。
经典的ServerSocket循环
class Server implements Runnable {
public void run() {
try {
ServerSocket ss = new ServerSocket(PORT);
while (!Thread.interrupted()){
new Thread(new Handler(ss.accept())).start();
// or, single-threaded, or a thread pool
}
}catch (IOException ex){ /* ... */ }
}
static class Handler implements Runnable {
final Socket socket;
Handler(Socket s){ socket = s;}
public void run(){
try {
byte[] input = new byte[MAX_INPUT];
socket.getInputStream().read(input);
byte[] output = process(input);
socket.getOutputStream().write(output);
}catch(IOException ex){ /* ... */}
}
private byte[] process(byte[] cmd){ /* ... */ }
}
}
代码示例中省略了异常处理
可伸缩性的目标
负载增加时(更多的客户端),优雅地降级
持续改进不断增长的资源消耗(CPU,内存,硬盘,带宽)
满足可用性和性能要求
低延时
满足高峰需求(并发请求)
服务质量可调节
分治法通常是实现所有可伸缩目标的最好方法
分治法
把网络处理流程分成几个小的任务,每个任务非阻塞执行一个动作。
当每个任务是可用的时候才执行。在这里,一个IO事件通常作为一个触发器。
[外链图片转存失败(img-ehhJb4UV-1567959447348)(handler.png)]
java.nio支持的基础机制有Non-blocking读和写,Dispatch tasks(与IO事件相关联的任务调度)。
无穷无尽的变化可能,许多事件驱动设计。
事件驱动设计
通常比其他方式更有效
更少的资源占用。不需要每一个客户端开启一个线程
更少的开销,更少的上下文切换,通常更少涉及到锁
当时调度会更慢,必须手动绑定任务到事件
通常在编程上更困难
必须分解为简单的非阻塞任务
类似GUI的事件驱动任务
不能消除所有的阻塞,比如GC,页面错误等
必须跟踪服务的逻辑状态
事件驱动IO使用类似的想法,但是使用不同的设计。
Reactor模型
Reactor通过调度合适的处理器来响应IO事件,类似于AWT的线程
Handlers执行非阻塞的任务,类似AWT的ActionListeners
通过绑定处理器到事件来进行管理,类似AWT的addActionListener
参见Schmidt等人的Patern-Oriented Software Architecture,Volume2(POSA2),Richard Stevens的网络相关的书籍,Matt Welsh的SEDA框架等。
java.nio 支持
Channels
支持非阻塞读的文件、socket连接
EDA框架等。
java.nio 支持
Channels
支持非阻塞读的文件、socket连接