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


网络服务

Web services、分布式对象等等都具有相同的处理结构

  1. Read request
  2. Decode request
  3. Process service
  4. Encode reply
  5. Send reply

基础的网络设计



每一个处理的 handler 都在各自的线程中处理。

代码示例

public class Server01 implements Runnable {
@Override public void run() {
try {
ServerSocket serverSocket = new ServerSocket(9898);
while (!Thread.interrupted()) {
// serverSocket.accept() 会阻塞到有客户端连接,之后 Handler 会处理
new Thread(new Handler(serverSocket.accept())).start();
}
} catch (Exception e) {
e.printStackTrace();
}
} private static class Handler implements Runnable {
private final Socket socket; Handler(Socket socket) {
this.socket = socket;
} @Override public void run() {
try {
byte[] input = new byte[1024];
// 假设能全部读取出来
socket.getInputStream().read(input);
byte[] output = process(input);
socket.getOutputStream().write(output);
} catch (Exception e) {
e.printStackTrace();
}
} private byte[] process(byte[] input) {
// 里面处理逻辑
return new byte[0];
}
}
}

这样做的好处是通过 accept 事件来触发任务的执行,将每个任务单独的去执行。但是缺点也很明显如果客户端链接过大那么需要新建若干个线程去执行,每台服务器可以运行的线程数是有限的。那么多线程的上下文切换的消耗也是巨大的。

Reactor Pattern

首先我们先来看下什么是事件驱动,在 java AWT 包中广泛的得到了使用。用户在点击一个 button 按钮的时候就会触发一个事件,然后会使用观察者模式来触发 Listener 中的处理事件。

Reactor 设计模式是基于事件驱动的一种实现方式,处理多个客户端并发的向服务端请求服务的场景。每种服务在服务端可能由多个方法组成。reactor 会解耦并发请求的服务并分发给对应的事件处理器来处理。目前,许多流行的开源框架都用到了。类似 AWT 中的 Thread。

Handlers 执行非阻塞操作的具体类,类似 AWT 中的 ActionListeners。

Reactor 单线程处理任务的设计

代码示例

public class Reactor implements Runnable {
private final Selector selector;
private final ServerSocketChannel serverSocketChannel; public Reactor(int port) throws IOException {
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(port));
serverSocketChannel.configureBlocking(false);
SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
selectionKey.attach(new Acceptor());
} @Override public void run() {
try {
while (!Thread.interrupted()) {
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
for (SelectionKey selectionKey : selectionKeys) {
dispatch(selectionKey);
selectionKeys.clear();
}
}
} catch (Exception e) {
e.printStackTrace();
}
} private void dispatch(SelectionKey selectionKey) {
Runnable runnable = (Runnable) selectionKey.attachment();
if (null != runnable) {
runnable.run();
}
} private class Acceptor implements Runnable {
@Override public void run() {
try {
SocketChannel socketChannel = serverSocketChannel.accept();
if (null != socketChannel) {
new Handler(selector, socketChannel);
}
} catch (IOException e) {
e.printStackTrace();
}
}
} private class Handler implements Runnable {
private final SocketChannel socketChannel;
private final SelectionKey selectionKey;
private ByteBuffer input = ByteBuffer.allocate(1024);
private ByteBuffer output = ByteBuffer.allocate(1024);
private static final int READING = 0, SENDING = 1;
private int state = READING; Handler(Selector selector, SocketChannel socketChannel) throws IOException {
this.socketChannel = socketChannel;
this.socketChannel.configureBlocking(false);
selectionKey = this.socketChannel.register(selector, 0);
selectionKey.attach(this);
selectionKey.interestOps(SelectionKey.OP_READ);
selector.wakeup();
} void process() {
} @Override public void run() {
try {
if (state == READING) {
socketChannel.read(input);
process();
state = SENDING;
selectionKey.interestOps(SelectionKey.OP_WRITE);
}
if (state == READING) {
socketChannel.write(output);
selectionKey.cancel();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

这个程序的重点在于 selectionKey.attach(); 方法每次把需要的对象传入进去,之后在有事件触发的时候会在 dispatch 中 attachment() 获取到这个对象,之后直接调用 run 方法。

Reactor 多线程处理任务的设计

只需要稍微修改下 Handler 这个类

// 添加一个线程池开发时请使用自定义或 spring 的线程池
private final ExecutorService executorService = Executors.newCachedThreadPool(); // 修改 run 方法
@Override public void run() {
try {
if (state == READING) {
socketChannel.read(input);
executorService.execute(new Runnable() {
@Override public void run() {
process();
state = SENDING;
selectionKey.interestOps(SelectionKey.OP_WRITE);
}
});
}
if (state == READING) {
socketChannel.write(output);
selectionKey.cancel();
}
} catch (Exception e) {
e.printStackTrace();
}
}

多个 Reactor



当看到这幅图的时候感觉这不就是 Netty EventLoopGroup 的工作模式吗

  1. mainReactor 不就是 bossGroup
  2. subReactor 不就是 workGroup

至此粗略的看完了这篇文章,感觉太 6 了,需要后面重复学习,这次只是了解大概。后面学习完会持续更新这篇文章!

学习 Doug Lea 大神写的——Scalable IO in Java的更多相关文章

  1. Netty Reator(二)Scalable IO in Java

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

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

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

  3. 《Scalable IO in Java》译文

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

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

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

  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. 五月的仓颉大神写的 三年java程序员面试感悟 值得分享给大家

    感谢 五月的仓颉  的这篇文章 , 让我重新认识到自己身上的不足之处 .  原文地址http://www.cnblogs.com/xrq730/p/5260294.html,转载请注明出处,谢谢! 前 ...

  8. 学习Python不得不关注和学习的国外大神博客

    注意 : 本文收集于网路 . 由于常常更新 , 有些链接打不开, 请自备梯子 在学习Python过程中,总会遇到各种各样的坑, 虽然Python是一门优美而简单易学的语言 . 但当学习后 , 总想着更 ...

  9. 大神为你分析 Go、Java、C 等主流编程语言(Go可以替代Java,而且最小化程序员的工作量,学习比较容易)

    本文主要分析 C.C++98.C++11.Java 与 Go,主要论述语言的关键能力.在论述的过程中会结合华为各语言编程专家和华为电信软件内部的骨干开发人员的交流,摒弃语言偏好或者语言教派之争,尽量以 ...

随机推荐

  1. 解决(Oracle)ORA-12528: TNS: 监听程序: 所有适用例程都无法建立新连接 问题

    解决(Oracle)ORA-12528: TNS: 监听程序: 所有适用例程都无法建立新连接 问题通过在CMD下用lsnrctl status 查看出的问题:发现BLOCKEDORACLE启动步骤:s ...

  2. 原生JavaScript 模拟alert对话框

    Window.prototype._alert = function() { //创建一个大盒子 var box = document.createElement("div"); ...

  3. 论一个PHP项目上线的注意点

    一.后端问题 服务器配置要跟上流量 预估QPS时要给足未知流量的空间 后端数据库设计要根据项目大小来相对应,小型流量单表就可以,但是中大型要分库分表 在处理执行修改的操作时一定要多一层判断(判断是否已 ...

  4. Struts2的Action访问

    ● 示例项目结构 ●  demo1.jsp <%@ page language="java" import="java.util.*" pageEncod ...

  5. 如何在有scoped不影响elementUI 的其他页面组件,进行单页面修改的几种方法。

    方式一:内联式css 内联式css , 优点:修改其他方便.缺点:造成页面臃肿,不利于后期维护. 方式二:外链css 外链css ,优点:对其他文件无影响,但会造成多个文件css  (缺点) @imp ...

  6. sublime推荐插件

    SyncedSidebarBg:侧边栏底色统一 Emmet:集合多种功能,大名鼎鼎的 Zen coding ==> 不过对于嵌入式的我没多大用啊 Sublime CodeIntel:代码提示 A ...

  7. SiteMesh 2.X 的使用(网页结构模板)

    SiteMesh是基于Servlet的filter的,即过滤流.它是通过截取reponse,并进行装饰后再交付给客户. 其中涉及到两个名词: 装饰页面(decorator page)和 "被 ...

  8. 算法竞赛模板 动态规划之背包DP

    ① 01背包 有n件物品和一个容量为v的背包.第i件物品的价值是c[i],体积是w[i].求解将哪些物品装入背包可使价值总和最大. 这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放. ...

  9. JAVA基础学习-多态 对象转型 final

    一.多态的产生条件 1:继承  存在继承的类之间 2:方法重装 3:父类继承子类重装的方法 子类的对象 也是属于父类的 二:对象的转型 1:向上转型:当子类转型成父类时 例如:Animal a = n ...

  10. 忘记mysql超户密码的解决方法

    本文章针对用yum安装的mariadb数据库,如果是tar包安装的mysql数据库,只是数据库命令的关闭启动方式不同而已. 方法一:[root@localhost ~]# killall -u mys ...