Netty Reator(二)Scalable IO in Java

Netty 系列目录 (https://www.cnblogs.com/binarylei/p/10117436.html)

相关文章:

Doug Lea 大神的《Scalable IO in Java》http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf:可伸缩的 IO 模型

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

  • Read request
  • Decode request
  • Process service
  • Encode reply
  • Send reply

一、经典的网络 IO 模型

传统的 IO 模型是一个 socket 一个线程,代码如下:

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 来串联起来...

二、单线程( BasicReactor Design)

所有事情 read、process、write 都由单个线程完成,完成一步重新设置下一步的 event。问题当然就是,其中任何步骤阻塞其它任务就阻塞了,因为只有一个线程。

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 模式,异步模式。

三、多线程(Worker Threads)

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 交给 Thread Pool 去处理。

四、主从(Multiple Reactor Threads)

Selector[] selectors; // 一个 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;
}
}

每天用心记录一点点。内容也许不重要,但习惯很重要!

Netty Reator(二)Scalable IO in Java的更多相关文章

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

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

  2. 《Scalable IO in Java》译文

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

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

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

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

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

  5. 《Scalable IO in Java》笔记

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

  6. Scalable IO in Java

    Scalable IO in Java http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf 大部分IO都是下面这个步骤, Most have same basi ...

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

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

  8. Java基础(十二)IO输入输出

    一.IO 概述 1.IO 概念 IO:I 代表 Input 输入:O 代表 Output 输出. Java 中 IO 是以流为基础进行输入输出,所有的数据被串行化(保存)写入输出流,或者从输入流读入. ...

  9. NetCore Netty 框架 BT.Netty.RPC 系列随讲 二 WHO AM I 之 NETTY/NETTY 与 网络通讯 IO 模型之关系?

    一:NETTY 是什么? Netty 是什么?  这个问题其实百度上一搜一堆. 这是官方话的描述:Netty 是一个基于NIO的客户.服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个 ...

随机推荐

  1. Executors与ThreadPoolExecutor

    最近阿里发布的 Java开发手册中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗 ...

  2. java和c#中String

    java中: c#中: 1.拼接字符串 sql语句中 in() str="'001','002','003'";至于产生string就这样  str1="'001'&qu ...

  3. 解题1(Suduku)

    题目描述 问题描述:数独(Sudoku)是一款大众喜爱的数字逻辑游戏.玩家需要根据9X9盘面上的已知数字,推算出所有剩余空格的数字,并且满足每一行.每一列.每一个粗线宫内的数字均含1-9,并且不重复. ...

  4. 665. Non-decreasing Array

    Given an array with n integers, your task is to check if it could become non-decreasing by modifying ...

  5. Linux 设置Redis开机启动

    Debian 也就是Ubun16.04 亲测可用 http://www.tuicool.com/articles/UR73ieq Centos7.0 http://blog.csdn.net/alex ...

  6. log4j最中意的配置

    log4j.rootLogger=DEBUG, Console, logfile log4j.appender.Console=org.apache.log4j.ConsoleAppender log ...

  7. SoundChannel和soundTransform的关系

    SoundChannel 音乐的  播放  暂停  当前获取当前位置  长度 leftPeak : Number[只读] 左声道的当前幅度(音量),范围从 0(静音)至 1(最大幅度) rightPe ...

  8. Python+Selenium学习--定位iframe中的对象

    场景 在web 应用中经常会出现frame 嵌套的应用,假设页面上有A.B 两个frame,其中B 在A 内,那么定位B 中的内容则需要先到A,然后再到B.      switch_to_frame ...

  9. VCSA 6.5, 初始化设置root密码失败can't set root password 或 安装时卡在80%

    是因为下载的非官方的包密码过期了,如果是Windows引导安装: 安装完虚拟机的时候就马上执行下面步骤,修改密码有效期即可. 重启vcsa,在引导界面(photon的图形界面)里按e编辑启动项 在co ...

  10. Win7系统不能记忆窗口大小与位置解决方法

    似在某此系统优化后,无意发现系统在注销或重启后,打开资源管理器,它以默认大小及位置显示. 对于习惯自定义操作来说,甚为不便,遍找方法未有奏效者,但总萦绕心头,时时记起. 今日再找问题解决方法,难兄难弟 ...