Multiple Reactor model
The multi-reactor model is the model used by netty. It can be seen directly from the figure that we have implemented a main reactor (mainReactor) and multiple sub-reactors (subReactor). The main reactor contains a ServerSocketChannel to receive all Request, the selector in the main reactor passes the new link to different sub-reactors through polling. The sub-reactor maintains the SocketChannel delivered by the main reactor, and the sub-reactor is responsible for the read-write monitoring and processing of the link.
I was thinking about copying a piece of code from the Internet, but I didn’t find the code that I wanted to implement the Reactor model--multi-Reactor model. I spent a little effort to implement it for your reference. Note: The code may generate the famous jdk nio bug problem of jdk Epoll empty polling which leads to cpu 100%. Solution: You can add a timeout to select. When select is not blocked at the specified time and it is not because the number of registered wake-ups reaches the threshold (for example, : 512 times) It can be considered that empty polling has occurred. At this time, it is necessary to reopen a selector to transfer all the channals on the old selector to the new 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();
}
}
}
}