前面两篇文章提到

reactor模式:单线程的reactor模式

reactor模式:多线程的reactor模式

NIO的server模式只有5个阶段,但是,NIO的selectionkey里确实有个accept事件,所以,为了区别,衍生出了主reactor和从reactor

并且,从reactor可以根据服务器的负荷,新增多个从reactor进行请求处理

服务器架构如下图

这个就是完整版的reactor模式的架构图了,目前使用到了reactor模式的框架(如netty),基本用的模式就是这个

代码实现:

 1  // Reactor線程
2 package server;
3
4 import java.io.IOException;
5 import java.net.InetSocketAddress;
6 import java.nio.channels.SelectionKey;
7 import java.nio.channels.Selector;
8 import java.nio.channels.ServerSocketChannel;
9 import java.util.Iterator;
10 import java.util.Set;
11
12 public class TCPReactor implements Runnable {
13
14 private final ServerSocketChannel ssc;
15 private final Selector selector; // mainReactor用的selector
16
17 public TCPReactor(int port) throws IOException {
18 selector = Selector.open();
19 ssc = ServerSocketChannel.open();
20 InetSocketAddress addr = new InetSocketAddress(port);
21 ssc.socket().bind(addr); // 在ServerSocketChannel綁定監聽端口
22 ssc.configureBlocking(false); // 設置ServerSocketChannel為非阻塞
23 SelectionKey sk = ssc.register(selector, SelectionKey.OP_ACCEPT); // ServerSocketChannel向selector註冊一個OP_ACCEPT事件,然後返回該通道的key
24 sk.attach(new Acceptor(ssc)); // 給定key一個附加的Acceptor對象
25 }
26
27 @Override
28 public void run() {
29 while (!Thread.interrupted()) { // 在線程被中斷前持續運行
30 System.out.println("mainReactor waiting for new event on port: "
31 + ssc.socket().getLocalPort() + "...");
32 try {
33 if (selector.select() == 0) // 若沒有事件就緒則不往下執行
34 continue;
35 } catch (IOException e) {
36 e.printStackTrace();
37 }
38 Set<SelectionKey> selectedKeys = selector.selectedKeys(); // 取得所有已就緒事件的key集合
39 Iterator<SelectionKey> it = selectedKeys.iterator();
40 while (it.hasNext()) {
41 dispatch((SelectionKey) (it.next())); // 根據事件的key進行調度
42 it.remove();
43 }
44 }
45 }
46
47 /*
48 * name: dispatch(SelectionKey key)
49 * description: 調度方法,根據事件綁定的對象開新線程
50 */
51 private void dispatch(SelectionKey key) {
52 Runnable r = (Runnable) (key.attachment()); // 根據事件之key綁定的對象開新線程
53 if (r != null)
54 r.run();
55 }
56
57 }
 1  // 接受連線請求線程
2 package server;
3
4 import java.io.IOException;
5 import java.nio.channels.SelectionKey;
6 import java.nio.channels.Selector;
7 import java.nio.channels.ServerSocketChannel;
8 import java.nio.channels.SocketChannel;
9
10 public class Acceptor implements Runnable {
11
12 private final ServerSocketChannel ssc; // mainReactor監聽的socket通道
13 private final int cores = Runtime.getRuntime().availableProcessors(); // 取得CPU核心數
14 private final Selector[] selectors = new Selector[cores]; // 創建核心數個selector給subReactor用
15 private int selIdx = 0; // 當前可使用的subReactor索引
16 private TCPSubReactor[] r = new TCPSubReactor[cores]; // subReactor線程
17 private Thread[] t = new Thread[cores]; // subReactor線程
18
19 public Acceptor(ServerSocketChannel ssc) throws IOException {
20 this.ssc = ssc;
21 // 創建多個selector以及多個subReactor線程
22 for (int i = 0; i < cores; i++) {
23 selectors[i] = Selector.open();
24 r[i] = new TCPSubReactor(selectors[i], ssc, i);
25 t[i] = new Thread(r[i]);
26 t[i].start();
27 }
28 }
29
30 @Override
31 public synchronized void run() {
32 try {
33 SocketChannel sc = ssc.accept(); // 接受client連線請求
34 System.out.println(sc.socket().getRemoteSocketAddress().toString()
35 + " is connected.");
36
37 if (sc != null) {
38 sc.configureBlocking(false); // 設置為非阻塞
39 r[selIdx].setRestart(true); // 暫停線程
40 selectors[selIdx].wakeup(); // 使一個阻塞住的selector操作立即返回
41 SelectionKey sk = sc.register(selectors[selIdx],
42 SelectionKey.OP_READ); // SocketChannel向selector[selIdx]註冊一個OP_READ事件,然後返回該通道的key
43 selectors[selIdx].wakeup(); // 使一個阻塞住的selector操作立即返回
44 r[selIdx].setRestart(false); // 重啟線程
45 sk.attach(new TCPHandler(sk, sc)); // 給定key一個附加的TCPHandler對象
46 if (++selIdx == selectors.length)
47 selIdx = 0;
48 }
49 } catch (IOException e) {
50 e.printStackTrace();
51 }
52 }
53
54 }
 1  package server;
2
3 import java.io.IOException;
4 import java.nio.channels.SelectionKey;
5 import java.nio.channels.Selector;
6 import java.nio.channels.ServerSocketChannel;
7 import java.util.Iterator;
8 import java.util.Set;
9
10 public class TCPSubReactor implements Runnable {
11
12 private final ServerSocketChannel ssc;
13 private final Selector selector;
14 private boolean restart = false;
15 int num;
16
17 public TCPSubReactor(Selector selector, ServerSocketChannel ssc, int num) {
18 this.ssc = ssc;
19 this.selector = selector;
20 this.num = num;
21 }
22
23 @Override
24 public void run() {
25 while (!Thread.interrupted()) { // 在線程被中斷前持續運行
26 //System.out.println("ID:" + num
27 // + " subReactor waiting for new event on port: "
28 // + ssc.socket().getLocalPort() + "...");
29 System.out.println("waiting for restart");
30 while (!Thread.interrupted() && !restart) { // 在線程被中斷前以及被指定重啟前持續運行
31 try {
32 if (selector.select() == 0)
33 continue; // 若沒有事件就緒則不往下執行
34 } catch (IOException e) {
35 e.printStackTrace();
36 }
37 Set<SelectionKey> selectedKeys = selector.selectedKeys(); // 取得所有已就緒事件的key集合
38 Iterator<SelectionKey> it = selectedKeys.iterator();
39 while (it.hasNext()) {
40 dispatch((SelectionKey) (it.next())); // 根據事件的key進行調度
41 it.remove();
42 }
43 }
44 }
45 }
46
47 /*
48 * name: dispatch(SelectionKey key) description: 調度方法,根據事件綁定的對象開新線程
49 */
50 private void dispatch(SelectionKey key) {
51 Runnable r = (Runnable) (key.attachment()); // 根據事件之key綁定的對象開新線程
52 if (r != null)
53 r.run();
54 }
55
56 public void setRestart(boolean restart) {
57 this.restart = restart;
58 }
59 }
 1   // Handler線程
2 package server;
3
4 import java.io.IOException;
5 import java.nio.channels.SelectionKey;
6 import java.nio.channels.SocketChannel;
7 import java.util.concurrent.LinkedBlockingQueue;
8 import java.util.concurrent.ThreadPoolExecutor;
9 import java.util.concurrent.TimeUnit;
10
11 public class TCPHandler implements Runnable {
12
13 private final SelectionKey sk;
14 private final SocketChannel sc;
15 private static final int THREAD_COUNTING = 10;
16 private static ThreadPoolExecutor pool = new ThreadPoolExecutor(
17 THREAD_COUNTING, THREAD_COUNTING, 10, TimeUnit.SECONDS,
18 new LinkedBlockingQueue<Runnable>()); // 線程池
19
20 HandlerState state; // 以狀態模式實現Handler
21
22 public TCPHandler(SelectionKey sk, SocketChannel sc) {
23 this.sk = sk;
24 this.sc = sc;
25 state = new ReadState(); // 初始狀態設定為READING
26 pool.setMaximumPoolSize(32); // 設置線程池最大線程數
27 }
28
29 @Override
30 public void run() {
31 try {
32 state.handle(this, sk, sc, pool);
33
34 } catch (IOException e) {
35 System.out.println("[Warning!] A client has been closed.");
36 closeChannel();
37 }
38 }
39
40 public void closeChannel() {
41 try {
42 sk.cancel();
43 sc.close();
44 } catch (IOException e1) {
45 e1.printStackTrace();
46 }
47 }
48
49 public void setState(HandlerState state) {
50 this.state = state;
51 }
52 }
 1  package server;
2
3 import java.io.IOException;
4 import java.nio.channels.SelectionKey;
5 import java.nio.channels.SocketChannel;
6 import java.util.concurrent.ThreadPoolExecutor;
7
8 public interface HandlerState {
9
10 public void changeState(TCPHandler h);
11
12 public void handle(TCPHandler h, SelectionKey sk, SocketChannel sc,
13 ThreadPoolExecutor pool) throws IOException ;
14 }
package server;  

    import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.concurrent.ThreadPoolExecutor; public class ReadState implements HandlerState{ private SelectionKey sk; public ReadState() {
} @Override
public void changeState(TCPHandler h) {
// TODO Auto-generated method stub
h.setState(new WorkState());
} @Override
public void handle(TCPHandler h, SelectionKey sk, SocketChannel sc,
ThreadPoolExecutor pool) throws IOException { // read()
this.sk = sk;
// non-blocking下不可用Readers,因為Readers不支援non-blocking
byte[] arr = new byte[1024];
ByteBuffer buf = ByteBuffer.wrap(arr); int numBytes = sc.read(buf); // 讀取字符串
if(numBytes == -1)
{
System.out.println("[Warning!] A client has been closed.");
h.closeChannel();
return;
}
String str = new String(arr); // 將讀取到的byte內容轉為字符串型態
if ((str != null) && !str.equals(" ")) {
h.setState(new WorkState()); // 改變狀態(READING->WORKING)
pool.execute(new WorkerThread(h, str)); // do process in worker thread
System.out.println(sc.socket().getRemoteSocketAddress().toString()
+ " > " + str);
} } /*
* 執行邏輯處理之函數
*/
synchronized void process(TCPHandler h, String str) {
// do process(decode, logically process, encode)..
// ..
h.setState(new WriteState()); // 改變狀態(WORKING->SENDING)
this.sk.interestOps(SelectionKey.OP_WRITE); // 通過key改變通道註冊的事件
this.sk.selector().wakeup(); // 使一個阻塞住的selector操作立即返回
} /*
* 工作者線程
*/
class WorkerThread implements Runnable { TCPHandler h;
String str; public WorkerThread(TCPHandler h, String str) {
this.h = h;
this.str=str;
} @Override
public void run() {
process(h, str);
} }
}
 1  package server;
2
3 import java.io.IOException;
4 import java.nio.channels.SelectionKey;
5 import java.nio.channels.SocketChannel;
6 import java.util.concurrent.ThreadPoolExecutor;
7
8 public class WorkState implements HandlerState {
9
10 public WorkState() {
11 }
12
13 @Override
14 public void changeState(TCPHandler h) {
15 // TODO Auto-generated method stub
16 h.setState(new WriteState());
17 }
18
19 @Override
20 public void handle(TCPHandler h, SelectionKey sk, SocketChannel sc,
21 ThreadPoolExecutor pool) throws IOException {
22 // TODO Auto-generated method stub
23
24 }
25
26 }
 package server;  

    import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.concurrent.ThreadPoolExecutor; public class WriteState implements HandlerState{ public WriteState() {
} @Override
public void changeState(TCPHandler h) {
// TODO Auto-generated method stub
h.setState(new ReadState());
} @Override
public void handle(TCPHandler h, SelectionKey sk, SocketChannel sc,
ThreadPoolExecutor pool) throws IOException { // send()
// get message from message queue String str = "Your message has sent to "
+ sc.socket().getLocalSocketAddress().toString() + "\r\n";
ByteBuffer buf = ByteBuffer.wrap(str.getBytes()); // wrap自動把buf的position設為0,所以不需要再flip() while (buf.hasRemaining()) {
sc.write(buf); // 回傳給client回應字符串,發送buf的position位置 到limit位置為止之間的內容
} h.setState(new ReadState()); // 改變狀態(SENDING->READING)
sk.interestOps(SelectionKey.OP_READ); // 通過key改變通道註冊的事件
sk.selector().wakeup(); // 使一個阻塞住的selector操作立即返回
}
}
 1 package server;
2
3 import java.io.IOException;
4
5 public class Main {
6
7
8 public static void main(String[] args) {
9 // TODO Auto-generated method stub
10 try {
11 TCPReactor reactor = new TCPReactor(1333);
12 new Thread(reactor).start();
13 } catch (IOException e) {
14 // TODO Auto-generated catch block
15 e.printStackTrace();
16 }
17 }
18
19 }
    

总的来说,主从式reactor比多线程的reactor先进的地方在于:

1.主reactor是一个线程,负责监听外部的连线请求,并派发给Acceptor处理。故Main Reactor中的selector只有注册OP_ACCEPT事件,也只能监听OP_ACCEPT事件。

而处理请求是其他N个不同的线程,即从reactor

2.可以根据请求的密集度来调控从reactor的个数

参考文章:

https://blog.csdn.net/yehjordan/article/details/51026045

reactor模式:主从式reactor的更多相关文章

  1. EDA风格与Reactor模式

    本文将探讨如下几个问题: Event-Driven架构风格的约束 EDA风格对架构属性的影响 Reactor架构模式 Reactor所解决的问题 redis中的EventDriven 从观察者模式到E ...

  2. nio的reactor模式

    转自:http://blog.csdn.net/it_man/article/details/38417761 线程状态转换图 就是非阻塞IO 采用多路分发方式举个例子吧,你服务器做一个聊天室,按照以 ...

  3. Netty(七):EventLoop学习前导——Reactor模式

    了解Netty的人多少都会知道Netty的高性能的一个原因就是它是基于事件驱动的,而这一事件的原型就是Reactor模式. 所以在学习EventLoop前,很有必要先搞懂Reactor模式. 本文目录 ...

  4. Java进阶(五)Java I/O模型从BIO到NIO和Reactor模式

    原创文章,同步发自作者个人博客,http://www.jasongj.com/java/nio_reactor/ Java I/O模型 同步 vs. 异步 同步I/O 每个请求必须逐个地被处理,一个请 ...

  5. Reactor模式详解

    转自:http://www.blogjava.net/DLevin/archive/2015/09/02/427045.html 前记 第一次听到Reactor模式是三年前的某个晚上,一个室友突然跑过 ...

  6. Reactor模式

    对象行为类的设计模式,对同步事件分拣和派发.别名Dispatcher(分发器) Reactor模式是处理并发I/O比较常见的一种模式,用于同步I/O,中心思想是将所有要处理的I/O事件注册到一个中心I ...

  7. 转一篇:Reactor模式

    转载自:http://www.blogjava.net/DLevin/archive/2015/09/02/427045.html 前记 第一次听到Reactor模式是三年前的某个晚上,一个室友突然跑 ...

  8. Reactor模式和NIO(转载二)

    本文可看成是对Doug Lea Scalable IO in Java一文的翻译. 当前分布式计算 Web Services盛行天下,这些网络服务的底层都离不开对socket的操作.他们都有一个共同的 ...

  9. (转)reactor模式

    转自: http://www.blogjava.net/DLevin/archive/2015/09/02/427045.html Reactor模式详解 前记 第一次听到Reactor模式是三年前的 ...

随机推荐

  1. eclispe中打点不会提示的解决方法,以及自动补全

    Eclipse中打点无提示的解决办法 建了个JAVA工程,然后发现输入代码后,在输入.后面不会弹出来我所要的函数.  alt+/      提示No Default Proposals 自己找了半天, ...

  2. 在Python中使用moviepy进行音视频剪辑混音合成时输出文件无声音问题

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 在使用moviepy进行音视频剪辑时发现输出成功但 ...

  3. PyQt学习随笔:Qt中tem Views(Model-Based)和Item Widgets(Item-Based)控件的用途和关系

    在界面程序开发中,数据的展示主要包括表格.简单列表.树状列表以及纯文本等多种方式,在Qt中将界面表格.简单列表.树状列表称为"表项视图类(item view class)",并提供 ...

  4. SZhe_Scan碎遮:一款基于Flask框架的web漏洞扫描神器

    SZhe_Scan碎遮:一款基于Flask框架的web漏洞扫描神器 天幕如遮,唯我一刀可碎千里华盖,纵横四海而无阻,是谓碎遮 --取自<有匪> 写在前面 这段时间很多时间都在忙着编写该项目 ...

  5. 1、tensorflow 框架理解

    2020/10/31 参考:https://blog.csdn.net/mzpmzk/article/details/78636127 1. 两大步骤:定义图define the graph, 进行计 ...

  6. spring框架半自动注解

    为了简便我们的开发,让我们一起来学习半自动注解吧. 让Spring管理某些类 1.在需要被SpringIOC容器管理的类上打上相应的注解 @Component:任意组件 @Controller:控制层 ...

  7. EF 查询外键对应的实例

    EF 查询外键对应的实例   1. 查询时易遇到的情况: 能查询到外键值,但对应的外键实例为null. 解决方法: (1) 使用EF的include // 我的应用如下 // SampleResult ...

  8. Codeforces Edu Round 50 A-D

    A. Function Height 由于只能提升\(x\)为奇数的点,每个三角形的底一定为\(2\), 则要求我们求: \(2 * (h_1 + h_2 + - + h_n) / 2 = k\),使 ...

  9. JAVA字符配置替换方案

    在JAVA中,很多时候,我们后台要对数据进行变量配置,希望可以在运行时再进行变量替换.我们今天给大空提供的是org.apache.commons.text方案. 1.首先,引用org.apache.c ...

  10. 一、less命令查看日志

    查看日志时,一般用less满足大部分的需求. 使用命令格式: less [要查看的文件名] 例如:less LOG.20201211 中间加参数命令格式 less 参数 [要查看的文件名] 例如:查看 ...