Scalable IO in Java

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

大部分IO都是下面这个步骤,

Most have same basic structure:
Read request
Decode request
Process service
Encode reply
Send reply

关键是如何处理并发, 最原始就是单纯的用多线程

class Server implements Runnable {
public void run() {
try {
ServerSocket ss = new ServerSocket(PORT);
while (!Thread.interrupted())
new Thread(new Handler(ss.accept())).start(); //创建新线程来handle
// 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) { /* ... */ }
}
}

显然简单的多线程会带来扩展性问题, 当client数量变的很多的时候, 还其他的可用性, 性能的问题

解决方法就是Divide-and-conquer, 分开后, 就需要Event-driven Designs来串联起来...

 

单线程版本的Reactor, 所有事情read, process, write都由单个线程完成, 完成一步重新设置下一步的event, 然后干其他的事

问题当然就是, 其中任何步骤不能消耗太多时间, 因为只有一个线程, 你占住了就会block其他任务

ps, 不明白为什么要画那么大个acceptor, 只是作为第一步的callback对象...

看代码会更清楚,

class Reactor implements Runnable {
final Selector selector;
final ServerSocketChannel serverSocket;
Reactor(int port) throws IOException { //Reactor初始化
selector = Selector.open();
serverSocket = ServerSocketChannel.open();
serverSocket.socket().bind(new InetSocketAddress(port));
serverSocket.configureBlocking(false); //非阻塞
SelectionKey sk = serverSocket.register(selector, SelectionKey.OP_ACCEPT); //分步处理,第一步,接收accept事件
sk.attach(new Acceptor()); //attach callback object, Acceptor
} public void run() {
try {
while (!Thread.interrupted()) {
selector.select();
Set selected = selector.selectedKeys();
Iterator it = selected.iterator();
while (it.hasNext())
dispatch((SelectionKey)(it.next()); //Reactor负责dispatch收到的事件
selected.clear();
}
} catch (IOException ex) { /* ... */ }
} void dispatch(SelectionKey k) {
Runnable r = (Runnable)(k.attachment()); //调用之前注册的callback对象
if (r != null)
r.run();
} class Acceptor implements Runnable { // inner
public void run() {
try {
SocketChannel c = serverSocket.accept();
if (c != null)
new Handler(selector, c);
}
catch(IOException ex) { /* ... */ }
}
}
} final class Handler implements Runnable {
final SocketChannel socket;
final SelectionKey sk;
ByteBuffer input = ByteBuffer.allocate(MAXIN);
ByteBuffer output = ByteBuffer.allocate(MAXOUT);
static final int READING = 0, SENDING = 1;
int state = READING; Handler(Selector sel, SocketChannel c) throws IOException {
socket = c; c.configureBlocking(false);
// Optionally try first read now
sk = socket.register(sel, 0);
sk.attach(this); //将Handler作为callback对象
sk.interestOps(SelectionKey.OP_READ); //第二步,接收Read事件
sel.wakeup();
}
boolean inputIsComplete() { /* ... */ }
boolean outputIsComplete() { /* ... */ }
void process() { /* ... */ } public void run() {
try {
if (state == READING) read();
else if (state == SENDING) send();
} catch (IOException ex) { /* ... */ }
} void read() throws IOException {
socket.read(input);
if (inputIsComplete()) {
process();
state = SENDING;
// Normally also do first write now
sk.interestOps(SelectionKey.OP_WRITE); //第三步,接收write事件
}
}
void send() throws IOException {
socket.write(output);
if (outputIsComplete()) sk.cancel(); //write完就结束了, 关闭select key
}
} //上面 的实现用Handler来同时处理Read和Write事件, 所以里面出现状态判断
//我们可以用State-Object pattern来更优雅的实现
class Handler { // ...
public void run() { // initial state is reader
socket.read(input);
if (inputIsComplete()) {
process();
sk.attach(new Sender()); //状态迁移, Read后变成write, 用Sender作为新的callback对象
sk.interest(SelectionKey.OP_WRITE);
sk.selector().wakeup();
}
}
class Sender implements Runnable {
public void run(){ // ...
socket.write(output);
if (outputIsComplete()) sk.cancel();
}
}
}

 

单线程模式的局限还是比较明显的

所以改进是, 将比较耗时的部分, 从reactor线程中分离出去, 让reactor专门负责IO

而另外创建Thread Pool和queue来缓存和处理任务

所以其实已经进化成Proactor模式, 异步模式

 

class Handler implements Runnable {
// uses util.concurrent thread pool
static PooledExecutor pool = new PooledExecutor(...);
static final int PROCESSING = 3;
// ...
synchronized void read() { // ...
socket.read(input);
if (inputIsComplete()) {
state = PROCESSING;
pool.execute(new Processer()); //使用线程pool异步执行
}
} synchronized void processAndHandOff() {
process();
state = SENDING; // or rebind attachment
sk.interest(SelectionKey.OP_WRITE); //process完,开始等待write事件
} class Processer implements Runnable {
public void run() { processAndHandOff(); }
}
}

使用多个reactor进程, 主reactor只负责accept, 然后将接收到的socketchannel交给subReactor去listen和处理

当然也可以在subReactor下加上线程池进行异步处理

坦白的说, 没看出用多个reactor有啥大的提升, 降低mainReactor listen的负担?

Selector[] selectors; //subReactors集合, 一个selector代表一个subReactor
int next = 0;
class Acceptor { // ...
public synchronized void run() { ...
Socket connection = serverSocket.accept(); //主selector负责accept
if (connection != null)
new Handler(selectors[next], connection); //选个subReactor去负责接收到的connection
if (++next == selectors.length) next = 0;
}
}

Scalable IO in Java的更多相关文章

  1. 《Scalable IO in Java》笔记

    Scalable IO in Java http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf 基本上所有的网络处理程序都有以下基本的处理过程:Read reque ...

  2. 【精尽Netty源码解析】1.Scalable IO in Java——多Reactor的代码实现

    Java高伸缩性IO处理 在Doug Lea大神的经典NIO框架文章<Scalable IO in Java>中,具体阐述了如何把Reactor模式和Java NIO整合起来,一步步理论结 ...

  3. Netty Reator(二)Scalable IO in Java

    Netty Reator(二)Scalable IO in Java Netty 系列目录 (https://www.cnblogs.com/binarylei/p/10117436.html) Do ...

  4. 《Scalable IO in Java》译文

    <Scalable IO in Java> 是java.util.concurrent包的作者,大师Doug Lea关于分析与构建可伸缩的高性能IO服务的一篇经典文章,在文章中Doug L ...

  5. 学习 Doug Lea 大神写的——Scalable IO in Java

    学习 Doug Lea 大神写的--Scalable IO in Java 网络服务 Web services.分布式对象等等都具有相同的处理结构 Read request Decode reques ...

  6. 一文弄懂-《Scalable IO In Java》

    目录 一. <Scalable IO In Java> 是什么? 二. IO架构的演变历程 1. Classic Service Designs 经典服务模型 2. Event-drive ...

  7. Scalable IO in Java【java高效IO】

    第一次翻译,如有错误,请指正 1.Outline 大纲Scalable network services  高效网络服务 Event-driven processing  事件驱动处理 Reactor ...

  8. 【转载】高性能IO设计 & Java NIO & 同步/异步 阻塞/非阻塞 Reactor/Proactor

    开始准备看Java NIO的,这篇文章:http://xly1981.iteye.com/blog/1735862 里面提到了这篇文章 http://xmuzyq.iteye.com/blog/783 ...

  9. hive运行query语句时提示错误:org.apache.hadoop.ipc.RemoteException: java.io.IOException: java.io.IOException:

    hive> select product_id, track_time from trackinfo limit 5; Total MapReduce jobs = 1 Launching Jo ...

随机推荐

  1. php的instanceof和判断闭包Closure

    类型运算符 instanceof 用于确定一个 PHP 变量是否属于某一类 class 的实例,在此之前用 is_a(),但是后来 is_a() 被废弃 <?php class MyClass ...

  2. 【转载】Oracle之内存结构(SGA、PGA)

    [转自]http://blog.itpub.net/25264937/viewspace-694917/ 一.内存结构 SGA(System Global Area):由所有服务进程和后台进程共享: ...

  3. size_t详细解释

    在学习sizeof运算符的时候,它的值类型为size_t,结果在使用printf函数显示的时候,凭空多了很多警告,有点不不理解,为啥搞这么复杂?直接用个int类型多省事? 经过一番搜索和阅读文档,找到 ...

  4. MVC & Entity Framework(2)- controller、Models单独DLL

    继上一篇MVC & Entity Framework(1)- 开发环境之后,已经很久没更新了.接下来记录一下怎么把MVC中的controller单独拆为一个类库,然后在web项目中引用.另外, ...

  5. 阿里云ECS网站备案流程

    首先登录阿里云账号-->点击选项卡中的备案专区-->开始备案选项卡-->增加网站 然后是一大堆信息核查,之后填写网站备案信息,这里注意,有个文件审批号的选项可以不填(选择“请选择”) ...

  6. php中Http请求方法和响应状态码整理教程

    HTTP请求报文由请求行(request line).请求头部(header).空行和请求数据4个部分组成,格式如下 可见请求行由请求方法字段.URL字段和HTTP协议版本字段3个字段组成,它们用空格 ...

  7. linux学习笔记18--文件/chmod/chown/chgrp

    文件类型与扩展名: Linux文件类型和Linux文件的文件名所代表的意义是两个不同的概念.我们通过一般应用程序而创建的比如file.txt.file.tar.gz ,这些文件虽然要用不同的程序来打开 ...

  8. hadoop集群的配置文件

    export JAVA_HOME=/home/hadoop/apps/jdk1..0_131 1.hadoop-env.sh中配置JAVA_HOME export HADOOP_YARN_USER=/ ...

  9. uva11383 Golden Tiger Claw 深入理解km算法

    /** 题目: uva11383 Golden Tiger Claw 深入理解km算法 链接:https://vjudge.net/problem/UVA-11383 题意:lv 思路:lrj训练指南 ...

  10. Python中sort以及sorted函数初探

    sorted(...) Help on built-in function sorted in module __builtin__: sorted(...) sorted(iterable, cmp ...