對NIO的瞭解請看我上篇博客,此處不作過多敘述
https://blog.csdn.net/CSDNzhangtao5/article/details/103023972
Reactor模式是一種事件處理模式,單個或多個事件(Event)併發地投遞到事件處理服務(Service Handler),事件處理服務將事件進行分離,同步的將他們分發到對應的事件處理器中去處理。把事件的產生於事件的處理分離,且完全異步,能最大性能的提升事件的響應與事件的處理能力
Reactor模式:
request都放在一個列隊裏,由對應的handler去各自消費。可以理解爲基於事件驅動型的消息訂閱
大名鼎鼎的netty通信框架就是基於Reactor模型做的線程模型,能較好的支持高併發的通信請求。
netty線程模型:
簡單的自己動手實現一個基於Reactor模型的通信服務端:基於阻塞隊列,將連接的channel放入工作線程的隊列裏,由工作線程各自去消費自己的隊列裏的channel然後註冊到系統內核,由各自工作線程處理channel的事件
import org.springframework.core.NestedExceptionUtils;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
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.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
public class NIOTest {
//指定ByteBuffer緩衝區大小
static ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//儲存io事件處理線程
private static final Set<WorkThread> selectorThreads = new HashSet<>();
static int defaultThreads;
static {
defaultThreads = Runtime.getRuntime().availableProcessors() * 2;
}
public static void main(String[] args) throws Exception {
//開放服務端4003端口
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(new InetSocketAddress(4003));
//創建事件處理線程池,專門處理IO事件的讀、寫
buildWorkThread(defaultThreads);
start();
//開闢一個線程專門處理線程的連接事件
new Thread(() -> {
Collection<WorkThread> workThreads = Collections.unmodifiableCollection(selectorThreads);
Iterator<WorkThread> workThreadIterator = workThreads.iterator();
try {
//創建一個selector 查詢器
Selector selector = Selector.open();
//將通道設置爲非阻塞
serverSocketChannel.configureBlocking(false);
///將端口的channel連接事件註冊給selector
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
//向系統內核查詢有連接事件的註冊channel,若沒有則阻塞
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
//若工作事件池遍歷完成,重頭開始遍歷工作線程池
if (!workThreadIterator.hasNext()) {
workThreadIterator = workThreads.iterator();
}
WorkThread workThread = workThreadIterator.next();
//遍歷系統內核返回的連接事件
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
iterator.next();
iterator.remove();
//拿到連接的channel,放到工作線程池裏的隊列
SocketChannel socket = serverSocketChannel.accept();
workThread.offer(socket);
}
workThread.wakeup();
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
}).start();
}
//創建若干工作線程
static void buildWorkThread(int workNum) throws Exception {
int i = 0;
while (i++ < workNum) {
selectorThreads.add(new WorkThread());
}
}
//啓動工作線程
static void start() {
Iterator<WorkThread> iterator = selectorThreads.iterator();
while (iterator.hasNext()) {
iterator.next().start();
}
}
static class WorkThread extends Thread {
private final Selector selector;
private final BlockingQueue<SocketChannel> blockingQueue = new LinkedBlockingDeque<>();
//對象實例化時,創建一個查詢器
WorkThread() throws Exception {
selector = Selector.open();
}
public void wakeup() {
selector.wakeup();
}
public void offer(SocketChannel socketChannel) {
blockingQueue.offer(socketChannel);
}
@Override
public void run() {
try {
while (true) {
//從隊列裏取出連接channel
SocketChannel socket = blockingQueue.poll();
//向系統內核註冊讀事件
if (null != socket) {
socket.configureBlocking(false);
socket.register(selector, SelectionKey.OP_READ);
}
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
selectionKeys.remove(selectionKey);
//獲取有讀事件的channel
if (selectionKey.isReadable()) {
SocketChannel channel = (SocketChannel) selectionKey.channel();
if (null != channel) {
byteBuffer.clear();
//將劉數據讀取到用戶緩衝區
channel.read(byteBuffer);
//將byteBuffer讀取位置設置爲0
byteBuffer.flip();
//取出字節
int limit = byteBuffer.limit();
byte[] bytes = new byte[limit];
byteBuffer.get(bytes);
System.out.println(new String(bytes, "utf-8"));
//將關注事件設爲可寫狀態
selectionKey.interestOps(SelectionKey.OP_WRITE);
}
}
//開始向channel寫入回覆消息
if (selectionKey.isWritable()) {
SocketChannel channel = (SocketChannel) selectionKey.channel();
byteBuffer.clear();
byteBuffer.put("hello".getBytes());
byteBuffer.flip();
channel.write(byteBuffer);
selectionKey.interestOps(SelectionKey.OP_READ);
}
}
}
} catch (Exception e) {
System.out.println(NestedExceptionUtils.getRootCause(e));
}
}
}
}