网络编程 -- RPC实现原理 -- 目录

  啦啦啦

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的更多相关文章

  1. 网络编程 -- RPC实现原理 -- NIO多线程 -- 迭代版本V1

    网络编程 -- RPC实现原理 -- 目录 啦啦啦 V1——设置标识变量selectionKey.attach(true);只处理一次(会一直循环遍历selectionKeys,占用CPU资源). ( ...

  2. 网络编程 -- RPC实现原理 -- NIO单线程

    网络编程 -- RPC实现原理 -- 目录 啦啦啦 Class : Service package lime.pri.limeNio.optimize.socket; import java.io.B ...

  3. 网络编程 -- RPC实现原理 -- 目录

    -- 啦啦啦 -- 网络编程 -- RPC实现原理 -- NIO单线程 网络编程 -- RPC实现原理 -- NIO多线程 -- 迭代版本V1 网络编程 -- RPC实现原理 -- NIO多线程 -- ...

  4. 网络编程 -- RPC实现原理 -- Netty -- 迭代版本V1 -- 入门应用

    网络编程 -- RPC实现原理 -- 目录 啦啦啦 V1——Netty入门应用 Class : NIOServerBootStrap package lime.pri.limeNio.netty.ne ...

  5. 网络编程 -- RPC实现原理 -- Netty -- 迭代版本V2 -- 对象传输

    网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2——Netty -- 使用序列化和反序列化在网络上传输对象:需要实现 java.io.Serializable 接口 只能传输( ByteBuf ...

  6. 网络编程 -- RPC实现原理 -- Netty -- 迭代版本V3 -- 编码解码

    网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2——Netty -- pipeline.addLast(io.netty.handler.codec.MessageToMessageCodec ...

  7. 网络编程 -- RPC实现原理 -- Netty -- 迭代版本V4 -- 粘包拆包

    网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2——Netty -- new LengthFieldPrepender(2) : 设置数据包 2 字节的特征码 new LengthFieldB ...

  8. 网络编程 -- RPC实现原理 -- RPC -- 迭代版本V3 -- 远程方法调用 整合 Spring

    网络编程 -- RPC实现原理 -- 目录 啦啦啦 V3——RPC -- 远程方法调用 及 null的传输 + Spring 服务提供商: 1. 配置 rpc03_server.xml 注入 服务提供 ...

  9. 网络编程 -- RPC实现原理 -- RPC -- 迭代版本V4 -- 远程方法调用 整合 Spring 自动注册

    网络编程 -- RPC实现原理 -- 目录 啦啦啦 V4——RPC -- 远程方法调用 + Spring 自动注册 服务提供商: 1. 配置 rpc04_server.xml 注入 服务提供商 rpc ...

随机推荐

  1. protobuf 测试使用

    1       介绍 Protocol buffers are a flexible, efficient, automated mechanism for serializing structure ...

  2. iOS Application Extension

    链接: iOS App Extension入门 iOS10通知及通知拓展Extension使用详解(附Demo) iOS10 通知extension之 Content Extension

  3. mxnet安装及NDArray初体验

    一.mxnet安装 (以下均为mac环境) 有二种方式: 1.1 用conda安装 #创建gluon目录 mkdir gluon-tutorials && cd gluon-tutor ...

  4. copy unicode HTML to clipboard

    How to copy unicode HTML code to the clipboard in html format, so it can be pasted into Writer, Word ...

  5. Android 获得手机屏幕真实的宽高

    http://stackoverflow.com/questions/1016896/get-screen-dimensions-in-pixels WindowManager w = activit ...

  6. Java int转string 长度不足左补0

    最近项目中用到这个需求,我试了两个方法都可以实现 方法一:(推荐) String s=String.format("%010d", 123)//123为int类型,0代表前面要补的 ...

  7. Unity Alpha融合参数(便查)

    Alpha Blending,中文译作Alpha混合 Blending就是控制透明的.处于光栅化的最后阶段. 这里例如我们给一个模型贴一个材质,那么在某个点计算出来颜色值称为源,而该点之前累积的颜色值 ...

  8. Android 自定义TextView 实现文本间距

    Android系统中TextView默认显示中文时会比较紧凑,不是很美观.为了让每行保持一定的行间距,可以设置属性android:lineSpacingExtra或android:lineSpacin ...

  9. 屌丝也用按位与(&),按位或(|) (二)

    上篇讲到了,用按位操作进行配置,下边来讲一个大神也在用的例子 linux的权限系统 我们知道一个文件,如果是777,那么是最高的权限,那这个七是咋构成的,我会告诉你是 4+2+1么? 我会告诉你,是1 ...

  10. 使用VirtualBox在Ubuntu下虚拟Windows XP共享文件夹设置方法

    1.首先保证虚拟的Windows XP有虚拟光驱(正常安装的都是有的,因为在ubuntu下一般都是用硬盘虚拟安装的),然后在已经运行起来的Windows XP菜单栏上选择“设备-安装增强功能“,如果不 ...