《Scalable IO in Java》笔记
Scalable IO in Java
http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf
基本上所有的网络处理程序都有以下基本的处理过程:
Read request
Decode request
Process service
Encode reply
Send reply
Classic Service Designs
简单的代码实现:
- 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) { /* ... */ }
- }
- }
对于每一个请求都分发给一个线程,每个线程中都独自处理上面的流程。
这种模型由于IO在阻塞时会一直等待,因此在用户负载增加时,性能下降的非常快。
server导致阻塞的原因:
1、serversocket的accept方法,阻塞等待client连接,直到client连接成功。
2、线程从socket inputstream读入数据,会进入阻塞状态,直到全部数据读完。
3、线程向socket outputstream写入数据,会阻塞直到全部数据写完。
client导致阻塞的原因:
1、client建立连接时会阻塞,直到连接成功。
2、线程从socket输入流读入数据,如果没有足够数据读完会进入阻塞状态,直到有数据或者读到输入流末尾。
3、线程从socket输出流写入数据,直到输出所有数据。
4、socket.setsolinger()设置socket的延迟时间,当socket关闭时,会进入阻塞状态,直到全部数据都发送完或者超时。
改进:采用基于事件驱动的设计,当有事件触发时,才会调用处理器进行数据处理。
Basic Reactor Design
代码实现:
- 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模式的一些概念:
Reactor:负责响应IO事件,当检测到一个新的事件,将其发送给相应的Handler去处理。
Handler:负责处理非阻塞的行为,标识系统管理的资源;同时将handler与事件绑定。
Reactor为单个线程,需要处理accept连接,同时发送请求到处理器中。
由于只有单个线程,所以处理器中的业务需要能够快速处理完。
改进:使用多线程处理业务逻辑。
Worker Thread Pools
参考代码:
- 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仍为单个线程。
继续改进:对于多个CPU的机器,为充分利用系统资源,将Reactor拆分为两部分。
Using Multiple Reactors
参考代码:
- 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;
- }
- }
mainReactor负责监听连接,accept连接给subReactor处理,为什么要单独分一个Reactor来处理监听呢?因为像TCP这样需要经过3次握手才能建立连接,这个建立连接的过程也是要耗时间和资源的,单独分一个Reactor来处理,可以提高性能。
参考:
http://www.cnblogs.com/fxjwind/p/3363329.html
《Scalable IO in Java》笔记的更多相关文章
- HTML+CSS笔记 CSS笔记集合
HTML+CSS笔记 表格,超链接,图片,表单 涉及内容:表格,超链接,图片,表单 HTML+CSS笔记 CSS入门 涉及内容:简介,优势,语法说明,代码注释,CSS样式位置,不同样式优先级,选择器, ...
- CSS笔记--选择器
CSS笔记--选择器 mate的使用 <meta charset="UTF-8"> <title>Document</title> <me ...
- HTML+CSS笔记 CSS中级 一些小技巧
水平居中 行内元素的水平居中 </a></li> <li><a href="#">2</a></li> &l ...
- HTML+CSS笔记 CSS中级 颜色&长度值
颜色值 在网页中的颜色设置是非常重要,有字体颜色(color).背景颜色(background-color).边框颜色(border)等,设置颜色的方法也有很多种: 1.英文命令颜色 语法: p{co ...
- HTML+CSS笔记 CSS中级 缩写入门
盒子模型代码简写 回忆盒模型时外边距(margin).内边距(padding)和边框(border)设置上下左右四个方向的边距是按照顺时针方向设置的:上右下左. 语法: margin:10px 15p ...
- HTML+CSS笔记 CSS进阶再续
CSS的布局模型 清楚了CSS 盒模型的基本概念. 盒模型类型, 我们就可以深入探讨网页布局的基本模型了.布局模型与盒模型一样都是 CSS 最基本. 最核心的概念. 但布局模型是建立在盒模型基础之上, ...
- HTML+CSS笔记 CSS进阶续集
元素分类 在CSS中,html中的标签元素大体被分为三种不同的类型:块状元素.内联元素(又叫行内元素)和内联块状元素. 常用的块状元素有: <div>.<p>.<h1&g ...
- HTML+CSS笔记 CSS进阶
文字排版 字体 我们可以使用css样式为网页中的文字设置字体.字号.颜色等样式属性. 语法: body{font-family:"宋体";} 这里注意不要设置不常用的字体,因为如果 ...
- HTML+CSS笔记 CSS入门续集
继承 CSS的某些样式是具有继承性的,那么什么是继承呢?继承是一种规则,它允许样式不仅应用于某个特定html标签元素,而且应用于其后代(标签). 语法: p{color:red;} <p> ...
- HTML+CSS笔记 CSS入门
简介: </span>年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的<span>脚本解释程序</span>,作为ABC语言的一种继承. & ...
随机推荐
- fedora自带OpenJDK,所以如果安装官方的JDK的话要先删除OpenJDK
1:rpm -qa|grep jdk 查看当前的jdk情况. 2:yum -y remove java java-1.7.0-openjdk* 卸载openjdk,这个过程中因为依赖原因可能会卸载一些 ...
- RabbitMQ学习系列(五): RPC 远程过程调用
前面讲过一些RabbitMQ的安装和用法,也说了说RabbitMQ在一般的业务场景下如何使用.不知道的可以看我前面的博客,http://www.cnblogs.com/zhangweizhong/ca ...
- photoshop常见抠图方法
1.多边形套索:这种工具是用于抠图的边界比较平直,但颜色比较复杂类的图像,它也是最笨最无奈的方法,只能是利用鼠标一点一点去点击抠选.2.磁性套索工具:分为三种:套索,多边形,磁性.这类工具一般只用于边 ...
- centos环境搭建
1.php -v 与phpinfo(); 不符,查看centos是否有自带的php:更改centos环境变量 /etc/profile,source /etc/profile生效: 2.pecl ...
- npm 发布到远程资源库
一.npm 发布到远程资源库 1.创建package.json Package.json 属性说明 name - 包名. version - 包的版本号. description - 包的描述. ho ...
- 学习MySQL之多表操作(三)
##多表查询 ##使用数据库 mytest USE mytest; ##删除,并重新创建表 t_dept DROP TABLE t_dept; CREATE TABLE t_dept ( deptno ...
- 如何修改geditor的配置文件 -好像geditor没有文本格式的配置文件? 要使用dconf-editor来配置- geditor自己配置编码格式
好像geditor没有文本格式的配置文件? 好像是通过一个程序, 叫 dconf-editor 来配置geditor的? 以前是通过gconf-editor来配置的, 但是gconf-editor的配 ...
- logback日志写入数据库(mysql)配置
如题 建议将日志级别设置为ERROR.这样可以避免存储过多的数据到数据中. 1 logback 配置文件(如下) <?xml version="1.0" encoding ...
- 第3月第19天 cxx_destruct dispatch_get_main_queue()死锁
1. http://blog.jobbole.com/65028/ 2. - (void)viewDidLoad { [super viewDidLoad]; NSLog("); dispa ...
- 他山之石——vs2013 安装与部署及程序打包
C#打包需要这个:InstallShield 2013 Limited Edition for Visual Studio .下载地址: InstallShield 2013 Limited Edi ...