reactor模式:单线程的reactor模式
reactor模式称之为响应器模式,常用于nio的网络通信框架,其服务架构图如下
不同于传统IO的串行调度方式,NIO把整个服务请求分为五个阶段
read:接收到请求,读取数据
decode:解码数据
compute:业务逻辑处理
encode:返回数据编码
send:发送数据
其中,以read和send阶段IO最为频繁
代码实现
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;
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(selector, ssc)); // 給定key一個附加的Acceptor對象
25 }
26
27 @Override
28 public void run() {
29 while (!Thread.interrupted()) { // 在線程被中斷前持續運行
30 System.out.println("Waiting for new event on port: " + ssc.socket().getLocalPort() + "...");
31 try {
32 if (selector.select() == 0) // 若沒有事件就緒則不往下執行
33 continue;
34 } catch (IOException e) {
35 // TODO Auto-generated catch block
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;
13 private final Selector selector;
14
15 public Acceptor(Selector selector, ServerSocketChannel ssc) {
16 this.ssc=ssc;
17 this.selector=selector;
18 }
19
20 @Override
21 public void run() {
22 try {
23 SocketChannel sc= ssc.accept(); // 接受client連線請求
24 System.out.println(sc.socket().getRemoteSocketAddress().toString() + " is connected.");
25
26 if(sc!=null) {
27 sc.configureBlocking(false); // 設置為非阻塞
28 SelectionKey sk = sc.register(selector, SelectionKey.OP_READ); // SocketChannel向selector註冊一個OP_READ事件,然後返回該通道的key
29 selector.wakeup(); // 使一個阻塞住的selector操作立即返回
30 sk.attach(new TCPHandler(sk, sc)); // 給定key一個附加的TCPHandler對象
31 }
32
33 } catch (IOException e) {
34 // TODO Auto-generated catch block
35 e.printStackTrace();
36 }
37 }
38
39
40 }
1 // Handler線程
2 package server;
3
4 import java.io.IOException;
5 import java.nio.ByteBuffer;
6 import java.nio.channels.SelectionKey;
7 import java.nio.channels.SocketChannel;
8 import java.util.concurrent.LinkedBlockingQueue;
9 import java.util.concurrent.ThreadPoolExecutor;
10 import java.util.concurrent.TimeUnit;
11
12 public class TCPHandler implements Runnable {
13
14 private final SelectionKey sk;
15 private final SocketChannel sc;
16
17 int state;
18
19 public TCPHandler(SelectionKey sk, SocketChannel sc) {
20 this.sk = sk;
21 this.sc = sc;
22 state = 0; // 初始狀態設定為READING
23 }
24
25 @Override
26 public void run() {
27 try {
28 if (state == 0)
29 read(); // 讀取網絡數據
30 else
31 send(); // 發送網絡數據
32
33 } catch (IOException e) {
34 System.out.println("[Warning!] A client has been closed.");
35 closeChannel();
36 }
37 }
38
39 private void closeChannel() {
40 try {
41 sk.cancel();
42 sc.close();
43 } catch (IOException e1) {
44 e1.printStackTrace();
45 }
46 }
47
48 private synchronized void read() throws IOException {
49 // non-blocking下不可用Readers,因為Readers不支援non-blocking
50 byte[] arr = new byte[1024];
51 ByteBuffer buf = ByteBuffer.wrap(arr);
52
53 int numBytes = sc.read(buf); // 讀取字符串
54 if(numBytes == -1)
55 {
56 System.out.println("[Warning!] A client has been closed.");
57 closeChannel();
58 return;
59 }
60 String str = new String(arr); // 將讀取到的byte內容轉為字符串型態
61 if ((str != null) && !str.equals(" ")) {
62 process(str); // 邏輯處理
63 System.out.println(sc.socket().getRemoteSocketAddress().toString()
64 + " > " + str);
65 state = 1; // 改變狀態
66 sk.interestOps(SelectionKey.OP_WRITE); // 通過key改變通道註冊的事件
67 sk.selector().wakeup(); // 使一個阻塞住的selector操作立即返回
68 }
69 }
70
71 private void send() throws IOException {
72 // get message from message queue
73
74 String str = "Your message has sent to "
75 + sc.socket().getLocalSocketAddress().toString() + "\r\n";
76 ByteBuffer buf = ByteBuffer.wrap(str.getBytes()); // wrap自動把buf的position設為0,所以不需要再flip()
77
78 while (buf.hasRemaining()) {
79 sc.write(buf); // 回傳給client回應字符串,發送buf的position位置 到limit位置為止之間的內容
80 }
81
82 state = 0; // 改變狀態
83 sk.interestOps(SelectionKey.OP_READ); // 通過key改變通道註冊的事件
84 sk.selector().wakeup(); // 使一個阻塞住的selector操作立即返回
85 }
86
87 void process(String str) {
88 // do process(decode, logically process, encode)..
89 // ..
90 }
91 }
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 reactor.run();
13 } catch (IOException e) {
14 // TODO Auto-generated catch block
15 e.printStackTrace();
16 }
17 }
18
19 }
客户端代码
1 package main.pkg;
2
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.io.InputStreamReader;
6 import java.io.PrintWriter;
7 import java.net.Socket;
8 import java.net.UnknownHostException;
9
10 public class Client {
11
12 /**
13 * @param args
14 */
15 public static void main(String[] args) {
16 // TODO Auto-generated method stub
17 String hostname=args[0];
18 int port = Integer.parseInt(args[1]);
19 //String hostname="127.0.0.1";
20 //int port=1333;
21
22 System.out.println("Connecting to "+ hostname +":"+port);
23 try {
24 Socket client = new Socket(hostname, port); // 連接至目的地
25 System.out.println("Connected to "+ hostname);
26
27 PrintWriter out = new PrintWriter(client.getOutputStream());
28 BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
29 BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
30 String input;
31
32 while((input=stdIn.readLine()) != null) { // 讀取輸入
33 out.println(input); // 發送輸入的字符串
34 out.flush(); // 強制將緩衝區內的數據輸出
35 if(input.equals("exit"))
36 {
37 break;
38 }
39 System.out.println("server: "+in.readLine());
40 }
41 client.close();
42 System.out.println("client stop.");
43 } catch (UnknownHostException e) {
44 // TODO Auto-generated catch block
45 System.err.println("Don't know about host: " + hostname);
46 } catch (IOException e) {
47 // TODO Auto-generated catch block
48 System.err.println("Couldn't get I/O for the socket connection");
49 }
50
51 }
52
53 }
代码解读:
1.创建TCPReactor 类的实例,启动端口监听
2.Acceptor 类只用于处理接受请求的时候,后续的读写跟其无任何关系
3.TCPReactor.run( )一直在进行,后续selectionkey有变动,会监听到,一直执行dispatch方法
最后提醒一点,从性能来说,单线程的reactor没过多的提升,因为IO和CPU的速度还是严重不匹配
参考文章:
https://blog.csdn.net/yehjordan/article/details/51012833
reactor模式:单线程的reactor模式的更多相关文章
- 两种高效的事件处理模式(Proactor和Reactor)
典型的多线程服务器的线程模型 1. 每个请求创建一个线程,使用阻塞式 I/O 操作 这是最简单的线程模型,1个线程处理1个连接的全部生命周期.该模型的优点在于:这个模型足够简单,它可以实现复杂的业务场 ...
- 【转】Reactor与Proactor两种模式区别
转自:http://www.cnblogs.com/cbscan/articles/2107494.html 两种IO多路复用方案:Reactor and Proactor 一般情况下,I/O 复用机 ...
- ACE_linux:Reactor与Proactor两种模式的区别
一.概念: Reactor与Proactor两种模式的区别.这里我们只关注read操作,因为write操作也是差不多的.下面是Reactor的做法: 某个事件处理器宣称它对某个socket上的读事件很 ...
- java 高性能Server —— Reactor模型单线程版
NIO模型 NIO模型示例如下: Acceptor注册Selector,监听accept事件 当客户端连接后,触发accept事件 服务器构建对应的Channel,并在其上注册Selector,监听读 ...
- 选择目录,选择文件夹的COM组件问题。在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式。请确保您的 Main 函数带有 STAThreadAttribute 标记。 只有将调试器附加到该进程才会引发此异常。
异常: 在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式.请确保您的 Main 函数带有 STAThreadAttribute 标记. 只有将调试器附加到该进程才会引发此异常. ...
- 在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式
在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式 转载自:http://blog.163.com/smhily_min/blog/static/75206226201092011 ...
- Reactor模型-单线程版
Reactor模型是典型的事件驱动模型.在网络编程中,所谓的事件当然就是read.write.bind.connect.close等这些动作了.Reactor模型的实现有很多种,下面介绍最基本的三种: ...
- c# Clipboard.SetDataObject(bmp1) 在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式。请确保您的 Main 函数带有 STAThreadAttribute 标记。 只有将调试器附加到该进程才会引发此异常
c# Clipboard.SetDataObject(bmp1) 在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式.请确保您的 Main 函数带有 STAThreadAttri ...
- 异常错误:在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式
最近做一个蛋疼的东西就是C#调用windows API 来操作一个摄像头,自动处理一些东西.要用到剪切板复制 粘贴功能,即 Clipboard.SetDataObject(filedic, true) ...
随机推荐
- moviepy音视频剪辑:TextClip.list(font)和search搜索字体报错UnicodeDecodeError:utf-8 codec cannott decode byte 问题
☞ ░ 前往老猿Python博文目录 ░ 在moviepy2.0.0.Dev版本中,执行如下语句: from moviepy.editor import * TextClip.search('gb', ...
- PyQt(Python+Qt)学习随笔:怎么在QScrollArea滚动区域中展示子部件的超长内容?
老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 滚动区域可以针对部署在其上的子部件在不可见时进行滚动展示,但这种滚动展示仅只能展示内容层可见范围的子 ...
- 第二十四章、containers容器类部件QScrollArea滚动区域详解
老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.概述 容器部件就是可以在部件内放置其他部件的部件,在Qt Designer中可以使用的容器部件有 ...
- PyQt(Python+Qt)学习随笔:Qt Designer中QAbstractButton派生按钮部件的text属性
text属性保存按钮上显示的文字,如果按钮未设置文字则为空字符串.如果文字中包含有与符号('&'),则该按钮会自动设置一个快捷键,快捷键就是'&'后第一个字符,显示时会在该字符下加下划 ...
- APP软件系统测试
1.功能模块测试 2.交叉事件测试 3.压力测试 存储压力测试 边界压力测试 响应能力压力测试 网络流量测试 4.容量测试 5.安装卸载测试 6.易用性.用户体验测试 7.UI界面测试
- GBK宽字节注入SQL
SQL注入:宽字节注入(GBK双字节绕过) 2015-06-09lyiang 宽字节注入的作用是非常大的,实际上在代码层的防御一般不外乎两种,一是反斜杠转义,而就是replace替换成空字节,之前的替 ...
- 哀悼疫情,全站灰色如何实现,CSS滤镜一行代码实现
庚子清明,以国家之名哀悼,以国家之名哀悼在新冠肺炎疫情中牺牲的烈士和逝世的同胞! 向抗疫英雄致敬! 今日打开各样的app,各大电商,爱奇艺都是灰色的 这里我也一直很好奇该功能,前端如何实现,了解过后发 ...
- P4317 花神的数论题,关于luogu题解粉兔做法的理解
link 题意 设 \(\text{sum}(i)\) 表示 \(i\) 的二进制表示中 \(1\) 的个数.给出一个正整数 \(N\) ,求 \(\prod_{i=1}^{N}\text{sum}( ...
- 升级jenkins之后无法启动 报错Unable to read /var/lib/jenkins/config.xml
故障记录 点击jenkins升级后再点击回滚到之前版本,jenkins就起不来了. 欲哭无泪,报错如下 hudson.util.HudsonFailedToLoad: org.jvnet.hudson ...
- 精尽Spring MVC源码分析 - HandlerMapping 组件(三)之 AbstractHandlerMethodMapping
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...