多Reactor模型
多Reactor模型是netty使用的模型,从图中可以直接看出,我们实现了一个主反应器(mainReactor)和多个次反应器(subReactor),主反应器中包含了一个ServerSocketChannel用来接收所有的请求,主反应器中的选择器通过轮询方式将新链接交由不同的次反应器,次反应器维护主反应器交付的SocketChannel,次反应器负责链接的读写监控和处理。
本来想着从网上copy一段代码,结果没有搜到我想要的实现了Reactor模型--多Reactor模型代码,本人花了一点功夫实现了,供大家参考。注意:代码可能产生jdk Epoll空轮询导致cpu 100%的著名jdk nio bug问题,解决方法:可以给select加一个超时时间,当select未在规定时间阻塞并且不是因为注册唤醒的次数达到阈值(例如:512次)可以认为发生了空轮询,此时需要重新open一个selector将旧selector上的channal全部转移注册到新selector上。
package com.nio.server3;
import java.io.EOFException;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.Iterator;
import java.util.concurrent.*;
/**
*
*/
public class NioServer3 {
private int port;
private Selector selector;
private SelectorProvider provider;
private int numCores = 3;
private int index = 0;
private ExecutorService service = Executors.newFixedThreadPool(numCores);
private SubReactor[] subReactors = new SubReactor[numCores];
public static void main(String[] args){
//启动mainReactor
new NioServer3(7001).start();
}
public NioServer3(int port) {
this.port = port;
}
public void init() {
ServerSocketChannel ssc = null;
try {
provider = SelectorProvider.provider();
ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress(port));
selector = provider.openSelector();
ssc.register(selector, SelectionKey.OP_ACCEPT);
for(int i = 0;i< subReactors.length;i++){
subReactors[i] = new SubReactor();
}
//启动subReactor线程
for(SubReactor subReactor : subReactors){
service.submit(subReactor);
}
System.out.println("NioServer started ......");
} catch (IOException e) {
e.printStackTrace();
}finally {
}
}
public void accept(SelectionKey key) {
try {
SubReactor handler = this.subReactors[index % numCores];
index ++;
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
//直接注册会阻塞,采用队列解决阻塞问题
handler.register(sc);
sc.write(ByteBuffer.wrap("Java NIO Reactor Copyright belongs to CSDN \r\nReactor> ".getBytes()));
} catch (IOException e) {
e.printStackTrace();
}
}
public void start() {
this.init();
while (true) {
try {
int events = selector.select();
if (events > 0) {
Iterator<SelectionKey> selectionKeys = selector.selectedKeys().iterator();
while (selectionKeys.hasNext()) {
SelectionKey key = selectionKeys.next();
selectionKeys.remove();
if (key.isAcceptable()) {
accept(key);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static class SubReactor implements Runnable{
private Selector selector;
private ConcurrentLinkedQueue<SocketChannel> event = new ConcurrentLinkedQueue<>();
private ByteBuffer inBuffer = ByteBuffer.allocateDirect(1024); // 创建读取缓冲区
private ByteBuffer outBuffer = ByteBuffer.allocateDirect(1024); // 创建写缓冲区
private StringBuilder request = new StringBuilder();
private String READING = "READING",CLOSED = "CLOSED";
private String STATE = READING;
public SubReactor() throws IOException {
this.selector = Selector.open();
}
public void register(SocketChannel sc){
event.offer(sc);
selector.wakeup();
}
protected boolean inputIsComplete() throws IOException {
while (inBuffer.hasRemaining()) {
byte ch = inBuffer.get();
if (ch == 3) { // ctrl+c 关闭连接
STATE = CLOSED;
return true;
} else if (ch == '\r') { // continue
} else if (ch == '\n') {
// 读取到了 \r\n 读取结束
return true;
} else {
request.append((char)ch);
}
}
return false;
}
public void readAndReply(SelectionKey key) {
try{
SocketChannel channel = (SocketChannel) key.channel(); // 服务器可读取消息:得到事件发生的Socket通道(其实就是这个请求对应的通道)
inBuffer.clear();
int read = channel.read(inBuffer);
if (read > 0) {
inBuffer.flip();
if(inputIsComplete()){
if(STATE == CLOSED){
key.channel().close();
STATE = READING;
return;
}
System.out.println("收到消息:" + request.toString()+"\r\n");
//让线程睡眠一会模拟业务处理
// Thread.sleep(5000);
outBuffer.clear();
outBuffer.put((request.toString()+"\r\nReactor> ").getBytes());
outBuffer.flip();
channel.write(outBuffer); // 将消息回送给客户端
request.delete(0,request.length());
}
} else if (read == -1){
// -1 客户端关闭了连接
throw new EOFException();
}else{
//read = 0 不处理
}
} catch (IOException e) {
try {
key.channel().close();
} catch (IOException ex) {
}
}
}
@Override
public void run() {
try {
while (true) {
SocketChannel sc = null;
while( (sc = event.poll()) != null){
sc.register(selector,SelectionKey.OP_READ);
System.out.println(sc.socket().getRemoteSocketAddress() +" 注册到了线程:"+Thread.currentThread().getName());
}
selector.select();
Iterator<?> ite = this.selector.selectedKeys().iterator();
while (ite.hasNext()) {
SelectionKey key = (SelectionKey) ite.next();
ite.remove(); // 删除已选的key,以防重复处理
if(key.isReadable()){
readAndReply(key);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}