网络编程 -- RPC实现原理 -- NIO多线程 -- 迭代版本V2
啦啦啦
V2——增加WriteQueue队列,存放selectionKey。addWriteEventToQueue()添加selectionKey并唤醒阻塞的selector。等selector唤醒之后再注册OP_WRITE事件。
(
selectionKey.cancel();清除key对应事件之后,由于多线程 main线程和对应的IO线程会抢夺selector资源。
在selector.select()和sc.register(selectionKey.selector(), SelectionKey.OP_WRITE);处会发生死锁,因此将需要注册IO操作的selectionKey放入队列并唤醒selector,之后在注册IO操作。
)
NIO 多线程
1.打开Selector
2.打开ServerSocketChannel
3.获取与此Channel关联的ServerSocket并绑定地址
4.设置Channel为非阻塞
5.将Channel注册到Selector并指定操作位
6.阻塞select()返回具有操作的通道个数。如果唤醒之后就绪通道数小于1,则将WriteQueue、ReadQueue队列中的所有sc注册到selector中并分别设置写操作位、读操作位。
7.获取SelectionKeys遍历,最后移除
8.根据key判断操作:isAcceptable、isConnectable、isReadable、isWritable
9.1.isAcceptable:根据key获取Channel,accept()获取SocketChannel并设置为非阻塞模式,注册到Selector并指定读操作位和缓冲区。
9.2.isConnectable:仅仅就是连接状态
9.3.isReadable:取消Read事件,启动Read线程处理IO读操作。添加selectionKey到WriteQueue中并唤醒selector。
9.4.isWritable:取消Write事件,启动Write线程处理IO写操作。添加selectionKey到ReadQueue中并唤醒selector。
private static Queue<SelectionKey> writeQueue = new LinkedBlockingQueue<SelectionKey>();
public static void addWriteEventToQueue(SelectionKey selectionKey) {
System.out.println(Thread.currentThread() + " -- -- -- 添加selectionKey到队列,并唤醒selector");
writeQueue.add(selectionKey);
selectionKey.selector().wakeup();
}
int n = selector.select();
System.out.println(Thread.currentThread() + " 事件就绪通道个数 : " + n);
if(n < 1){
System.out.println(Thread.currentThread() + " -- -- -- selector被唤醒,注册队列中所有socketChannel为OP_WRITE操作");
Iterator<SelectionKey> iterator = writeQueue.iterator();
while(iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
SocketChannel sc = (SocketChannel) selectionKey.channel();
sc.register(selectionKey.selector(), SelectionKey.OP_WRITE);
}
}
Class : Service
package lime.pri.limeNio.optimize.socket4; import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue; public class Service { public static void main(String[] args) throws IOException, InterruptedException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(9999));
serverSocketChannel.configureBlocking(false);
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
System.out.println(Thread.currentThread() + " 监听端口@9999,等待客户端连接...");
int n = selector.select();
System.out.println(Thread.currentThread() + " 事件就绪通道个数 : " + n);
if(n < 1){
System.out.println(Thread.currentThread() + " -- -- -- selector被唤醒,注册队列中所有socketChannel为OP_WRITE操作");
Iterator<SelectionKey> iterator = writeQueue.iterator();
while(iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
SocketChannel sc = (SocketChannel) selectionKey.channel();
sc.register(selectionKey.selector(), SelectionKey.OP_WRITE);
}
}
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
iterator.remove();
if (selectionKey.isValid() && selectionKey.isAcceptable()) {
System.out.println(Thread.currentThread() + " -- -- -- 处理Acceptable事件");
ServerSocketChannel ssc = (ServerSocketChannel) selectionKey.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
}
if (selectionKey.isValid() && selectionKey.isReadable()) {
System.out.println(Thread.currentThread() + " -- -- -- 处理Readable事件");
selectionKey.cancel();
RequestProcessor.proceess(selectionKey, selector);
}
if (selectionKey.isValid() && selectionKey.isWritable()) {
System.out.println(Thread.currentThread() + " -- -- -- 处理Writable事件");
selectionKey.cancel();
ResponseProcessor.proceess(selectionKey, selector);
}
}
}
} private static Queue<SelectionKey> writeQueue = new LinkedBlockingQueue<SelectionKey>();
public static void addWriteEventToQueue(SelectionKey selectionKey) {
System.out.println(Thread.currentThread() + " -- -- -- 添加selectionKey到队列,并唤醒selector");
writeQueue.add(selectionKey);
selectionKey.selector().wakeup();
}
}
Class : RequestProcessor
package lime.pri.limeNio.optimize.socket4; import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class RequestProcessor { private static ExecutorService exec = Executors.newFixedThreadPool(2); public static void proceess(final SelectionKey selectionKey, final Selector selector) {
exec.submit(new Runnable() { public void run() {
try {
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byteBuffer.clear();
SocketChannel sc = (SocketChannel) selectionKey.channel();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while(sc.read(byteBuffer) != -1){
bos.write(byteBuffer.array());
byteBuffer.clear();
}
System.out.println(Thread.currentThread() + " 客户端( " + sc.getRemoteAddress() + " ) 请求 : " + bos.toString());
Service.addWriteEventToQueue(selectionKey);
} catch (Exception e) {
}
}
});
} }
Class : ResponseProcessor
package lime.pri.limeNio.optimize.socket4; import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class ResponseProcessor { private static ExecutorService exec = Executors.newFixedThreadPool(2); public static void proceess(final SelectionKey selectionKey, final Selector selector) {
exec.submit(new Runnable() { public void run() {
try {
String response = "服务端响应 : " + new Date().toString();
ByteBuffer byteBuffer = ByteBuffer.wrap(response.getBytes());
SocketChannel sc = (SocketChannel) selectionKey.channel();
sc.write(byteBuffer);
sc.close();
} catch (IOException e) {
}
}
});
} }
Class : Client
package lime.pri.limeNio.optimize.socket4; import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel; public class Client { public static void main(String[] args) throws IOException {
for (int i = 0; i < 10; i++) {
new Thread() {
@Override
public void run() {
try {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999)); socketChannel.write(ByteBuffer.wrap("Query Date".getBytes()));
socketChannel.shutdownOutput(); ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
socketChannel.read(byteBuffer);
System.out.println(new String(byteBuffer.array()));
socketChannel.close();
} catch (IOException e) {
}
} }.start();
}
}
}
Console: Server
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Thread[main,5,main] 事件就绪通道个数 : 1
Thread[main,5,main] -- -- -- 处理Acceptable事件
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Thread[main,5,main] 事件就绪通道个数 : 1
Thread[main,5,main] -- -- -- 处理Acceptable事件
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Thread[main,5,main] 事件就绪通道个数 : 3
Thread[main,5,main] -- -- -- 处理Acceptable事件
Thread[main,5,main] -- -- -- 处理Readable事件
Thread[main,5,main] -- -- -- 处理Readable事件
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Thread[main,5,main] 事件就绪通道个数 : 2
Thread[main,5,main] -- -- -- 处理Acceptable事件
Thread[main,5,main] -- -- -- 处理Readable事件
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Thread[main,5,main] 事件就绪通道个数 : 2
Thread[main,5,main] -- -- -- 处理Readable事件
Thread[main,5,main] -- -- -- 处理Acceptable事件
Thread[pool-1-thread-2,5,main] 客户端( /127.0.0.1:4643 ) 请求 : Query Date
Thread[pool-1-thread-2,5,main] -- -- -- 添加selectionKey到队列,并唤醒selector
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Thread[main,5,main] 事件就绪通道个数 : 2
Thread[main,5,main] -- -- -- 处理Readable事件
Thread[main,5,main] -- -- -- 处理Acceptable事件
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Thread[main,5,main] 事件就绪通道个数 : 2
Thread[main,5,main] -- -- -- 处理Readable事件
Thread[main,5,main] -- -- -- 处理Acceptable事件
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Thread[main,5,main] 事件就绪通道个数 : 2
Thread[main,5,main] -- -- -- 处理Acceptable事件
Thread[pool-1-thread-2,5,main] 客户端( /127.0.0.1:4645 ) 请求 : Query Date
Thread[main,5,main] -- -- -- 处理Readable事件
Thread[pool-1-thread-1,5,main] 客户端( /127.0.0.1:4644 ) 请求 : Query Date
Thread[pool-1-thread-1,5,main] -- -- -- 添加selectionKey到队列,并唤醒selector
Thread[pool-1-thread-2,5,main] -- -- -- 添加selectionKey到队列,并唤醒selector
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Thread[main,5,main] 事件就绪通道个数 : 0
Thread[pool-1-thread-2,5,main] 客户端( /127.0.0.1:4646 ) 请求 : Query Date
Thread[main,5,main] -- -- -- selector被唤醒,注册队列中所有socketChannel为OP_WRITE操作
Thread[pool-1-thread-2,5,main] -- -- -- 添加selectionKey到队列,并唤醒selector
Thread[pool-1-thread-1,5,main] 客户端( /127.0.0.1:4642 ) 请求 : Query Date
Thread[pool-1-thread-1,5,main] -- -- -- 添加selectionKey到队列,并唤醒selector
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Thread[pool-1-thread-2,5,main] 客户端( /127.0.0.1:4647 ) 请求 : Query Date
Thread[pool-1-thread-2,5,main] -- -- -- 添加selectionKey到队列,并唤醒selector
Thread[pool-1-thread-1,5,main] 客户端( /127.0.0.1:4648 ) 请求 : Query Date
Thread[main,5,main] 事件就绪通道个数 : 0
Thread[main,5,main] -- -- -- selector被唤醒,注册队列中所有socketChannel为OP_WRITE操作
Thread[pool-1-thread-1,5,main] -- -- -- 添加selectionKey到队列,并唤醒selector
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Thread[main,5,main] 事件就绪通道个数 : 0
Thread[main,5,main] -- -- -- selector被唤醒,注册队列中所有socketChannel为OP_WRITE操作
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Thread[main,5,main] 事件就绪通道个数 : 9
Thread[main,5,main] -- -- -- 处理Readable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[pool-1-thread-2,5,main] 客户端( /127.0.0.1:4649 ) 请求 : Query Date
Thread[pool-1-thread-2,5,main] -- -- -- 添加selectionKey到队列,并唤醒selector
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Acceptable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Thread[main,5,main] 事件就绪通道个数 : 0
Thread[main,5,main] -- -- -- selector被唤醒,注册队列中所有socketChannel为OP_WRITE操作
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Thread[main,5,main] 事件就绪通道个数 : 10
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Acceptable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Readable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Thread[main,5,main] 事件就绪通道个数 : 1
Thread[main,5,main] -- -- -- 处理Readable事件
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Thread[pool-1-thread-1,5,main] 客户端( /127.0.0.1:4650 ) 请求 : Query Date
Thread[pool-1-thread-1,5,main] -- -- -- 添加selectionKey到队列,并唤醒selector
Thread[pool-1-thread-2,5,main] 客户端( /127.0.0.1:4651 ) 请求 : Query Date
Thread[main,5,main] 事件就绪通道个数 : 0
Thread[main,5,main] -- -- -- selector被唤醒,注册队列中所有socketChannel为OP_WRITE操作
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Thread[pool-1-thread-2,5,main] -- -- -- 添加selectionKey到队列,并唤醒selector
Thread[main,5,main] 事件就绪通道个数 : 9
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Thread[main,5,main] 事件就绪通道个数 : 0
Thread[main,5,main] -- -- -- selector被唤醒,注册队列中所有socketChannel为OP_WRITE操作
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Thread[main,5,main] 事件就绪通道个数 : 10
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] -- -- -- 处理Writable事件
Thread[main,5,main] 监听端口@9999,等待客户端连接...
Class : Client
Thread[Thread-7,5,main] 服务端响应 : Sat Jun 24 16:00:41 CST 2017
Thread[Thread-0,5,main] 服务端响应 : Sat Jun 24 16:00:41 CST 2017
Thread[Thread-3,5,main] 服务端响应 : Sat Jun 24 16:00:41 CST 2017
Thread[Thread-9,5,main] 服务端响应 : Sat Jun 24 16:00:41 CST 2017
Thread[Thread-1,5,main] 服务端响应 : Sat Jun 24 16:00:41 CST 2017
Thread[Thread-4,5,main] 服务端响应 : Sat Jun 24 16:00:41 CST 2017
Thread[Thread-6,5,main] 服务端响应 : Sat Jun 24 16:00:41 CST 2017
Thread[Thread-5,5,main] 服务端响应 : Sat Jun 24 16:00:41 CST 2017
Thread[Thread-8,5,main] 服务端响应 : Sat Jun 24 16:00:41 CST 2017
Thread[Thread-2,5,main] 服务端响应 : Sat Jun 24 16:00:41 CST 2017
啦啦啦
网络编程 -- RPC实现原理 -- NIO多线程 -- 迭代版本V2的更多相关文章
- 网络编程 -- RPC实现原理 -- NIO多线程 -- 迭代版本V1
网络编程 -- RPC实现原理 -- 目录 啦啦啦 V1——设置标识变量selectionKey.attach(true);只处理一次(会一直循环遍历selectionKeys,占用CPU资源). ( ...
- 网络编程 -- RPC实现原理 -- NIO单线程
网络编程 -- RPC实现原理 -- 目录 啦啦啦 Class : Service package lime.pri.limeNio.optimize.socket; import java.io.B ...
- 网络编程 -- RPC实现原理 -- 目录
-- 啦啦啦 -- 网络编程 -- RPC实现原理 -- NIO单线程 网络编程 -- RPC实现原理 -- NIO多线程 -- 迭代版本V1 网络编程 -- RPC实现原理 -- NIO多线程 -- ...
- 网络编程 -- RPC实现原理 -- Netty -- 迭代版本V1 -- 入门应用
网络编程 -- RPC实现原理 -- 目录 啦啦啦 V1——Netty入门应用 Class : NIOServerBootStrap package lime.pri.limeNio.netty.ne ...
- 网络编程 -- RPC实现原理 -- Netty -- 迭代版本V2 -- 对象传输
网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2——Netty -- 使用序列化和反序列化在网络上传输对象:需要实现 java.io.Serializable 接口 只能传输( ByteBuf ...
- 网络编程 -- RPC实现原理 -- Netty -- 迭代版本V3 -- 编码解码
网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2——Netty -- pipeline.addLast(io.netty.handler.codec.MessageToMessageCodec ...
- 网络编程 -- RPC实现原理 -- Netty -- 迭代版本V4 -- 粘包拆包
网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2——Netty -- new LengthFieldPrepender(2) : 设置数据包 2 字节的特征码 new LengthFieldB ...
- 网络编程 -- RPC实现原理 -- RPC -- 迭代版本V3 -- 远程方法调用 整合 Spring
网络编程 -- RPC实现原理 -- 目录 啦啦啦 V3——RPC -- 远程方法调用 及 null的传输 + Spring 服务提供商: 1. 配置 rpc03_server.xml 注入 服务提供 ...
- 网络编程 -- RPC实现原理 -- RPC -- 迭代版本V4 -- 远程方法调用 整合 Spring 自动注册
网络编程 -- RPC实现原理 -- 目录 啦啦啦 V4——RPC -- 远程方法调用 + Spring 自动注册 服务提供商: 1. 配置 rpc04_server.xml 注入 服务提供商 rpc ...
随机推荐
- 使用uploadify上传图片时返回“Cannot read property 'queueData' of undefined”
在使用uploadify插件上传图片时,遇到一个比较坑的错误:上传时提示“Cannot read property 'queueData' of undefined”. 遇到这个问题有点无语,因为这个 ...
- 阿里云ecs 增加虚拟网卡
cd /etc/sysconfig/network-scripts/ #进入网卡目录 cp ifcfg-eth0 ifcfg-eth0:1 # 复制出ifcfg-eth0:1虚拟网卡 vi i ...
- ios mac 对照片进行JPEG压缩
ios mac 对照片进行JPEG压缩 1. 在iOS上可以使用 API UIImageJPEGRepresentation 对照片数据进行JPEG压缩: 我们知道iOS其实是MAC OS 的移植,那 ...
- shell基础语法以及监控进程不存在重新启动
转码 # dos2unix ./test.sh 权限 # chmod a+x ./test.sh 语法变量var="111"echo $varecho ${var} 运算no1=4 ...
- 【翻译自mos文章】在10g中,当发生ORA-00020时,sqlplus登陆会报“connected to an idle instance”
在10g中.当发生ORA-00020时,sqlplus登陆会报"connected to an idle instance" 来源于: Sqlplus Logon Reports ...
- VSCode配置TypeScript
网上教程一堆,记录下我认为的关键点: 1.创建tsconfig.json,使用命令行在项目文件夹下输入“tsc --init”即可: 2.创建tasks.json,在VSCode中,使用ctrl+sh ...
- N个降序数组,找到最大的K个数
问题定义 这个问题来自网上看到的百度算法题,感觉很不错,问题定义如下: 假定有20个有序数组,每个数组有500个数字,降序排列,数字类型32位uint数值,现在需要取出这10000个数字中最大的500 ...
- linux每日命令(9):cp命令
一.命令格式: cp [参数] source dest 或 cp [参数] source... directory 二.命令功能: 将源文件复制至目标文件,或将多个源文件复制至目标目录. 三. 命令参 ...
- RSA加密和解密工具类
import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher; import java.security.*; i ...
- GNU Binutils简介及基本用法
[时间:2017-06] [状态:Open] [关键词:GNU, binutils, as, ld, ar, 基础工具,linux,链接器,汇编器] 0 简介 GNU Binary Utilities ...