先来看看Netty 的Reactor 模型
我们可以看出在Netty有 BossGroup 和 WorkerGroup
BossGroup 是用来接收 连接。
WorkerGroup 是用来处理连接
首先我们要知道 为了提高多核处理的利用我们 可以不再只用一个selector ,而是根据核数来设置多个selector让多个线程来进行处理。
- 每个线程对应一个selector
- 多线程下,并发客户端被分配到多个selector上
- 每个连接只绑定到其中一个selector
而netty 中其实也是这样做的。
我们来看看我们的实现, 首先就是SelectorThread
public class SelectorThread implements Runnable{
Selector selector = null;
//这个是用来接收Channel的ServerScoektChannel SocketChannel
//当有需要注册到当前select的连接进行来时会被wakeup并且放Channel入队列
LinkedBlockingQueue<Channel> queue = new LinkedBlockingQueue<Channel>();
//来管理当前selector 的group 可能是 boss 或 worker
SelectorThreadGroup group ;
//当前 selector 是一个 worker 要给 workergroup来管理
public void setWorker(SelectorThreadGroup group){
this.group = group;
}
public void run() {
while(true){
//注意 如果没有事件发生 select 就会阻塞,可以使用wakeup()来唤醒
int nums =selector.select();
//如果有事件发生 就进行相应处理 比如 accept 读/写
if(nums>0){
....
}
//被wakeup 时 说明有连接 进来,从Channel中取出并注册
if (!queue.isEmpty()){
Channel c = queue.take();
if (c instanceof ServerSocketChannel){
server.register(selector,SelectionKey.OP_ACCEPT);
}else if (c instanceof SocketChannel ){
SocketChannel client = (SocketChannel) c;
ByteBuffer byteBuffer = ByteBuffer.allocate(4096);
client.register(selector,SelectionKey.OP_READ,byteBuffer);
}
}
}
}
}
再来看workergroup 的实现
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.Channel;
import java.nio.channels.ServerSocketChannel;
import java.util.concurrent.atomic.AtomicInteger;
/**
* SelectorThreadGroup 来进行对 SelectorThread 进行管理
* 并决定每一次 有事件时 选择哪一个 线程selector 来处理
*/
public class SelectorThreadGroup {
//当前group所管理的所有selector
SelectorThread[] sts ;
ServerSocketChannel server = null;
AtomicInteger xid = new AtomicInteger(0);//用来进行轮询selector 轮流放入连接
SelectorThreadGroup workGroup = this;
//num 线程数
SelectorThreadGroup( int num ){
sts = new SelectorThread[num];
for (int i =0;i<num;i++){
sts[i] = new SelectorThread(this);
new Thread(sts[i]).start();
}
}
public void setWorkGroup(SelectorThreadGroup work){
this.workGroup = work;
}
public void bind(int port) {
try {
server = ServerSocketChannel.open();
server.configureBlocking(false);
server.bind(new InetSocketAddress(port));
//注册到哪个selector 上呢 ?
//把自己注册进selector
nextSelectorV2(server);
} catch (IOException e) {
e.printStackTrace();
}
}
public void nextSelector(Channel c ){
// c 可能事 server 也可能事 client
SelectorThread st = next();
//1 通过队列传递消息
st.queue.add(c);
//2 通过打断阻塞让对应线程自己去完成注册
st.selector.wakeup();
}
public void nextSelectorV2(Channel c){
if (c instanceof ServerSocketChannel ){
SelectorThread next = next();
next.queue.add(c);
next.setWorker(workGroup);
next.selector.wakeup();
}else {
SelectorThread worker = nextWorker();
worker.queue.add(c);
worker.selector.wakeup();
}
}
//轮询出来的结果
public SelectorThread next() {
int index = xid.incrementAndGet()%sts.length;
return sts[index];
}
public SelectorThread nextWorker() {
int index = xid.incrementAndGet()%sts.length;
return workGroup.sts[index];
}
}
SelectThread 具体实现
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
public class SelectorThread implements Runnable{
//每个线程对应一个selector
//多线程下,并发客户端被分配到多个selector上
//每个连接只绑定到其中一个selector
Selector selector = null;
LinkedBlockingQueue<Channel> queue = new LinkedBlockingQueue<Channel>();
SelectorThreadGroup group ;
public void setWorker(SelectorThreadGroup group){
this.group = group;
}
SelectorThread(SelectorThreadGroup group ){
try {
this.group = group;
selector = Selector.open();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
//true
while (true){
try {
//如果不设置超时时间 会阻塞 select(50)
//wakeup 会跳出阻塞
//1 select
int nums = selector.select();
//2 处理selectkeys
if (nums>0){
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iter = keys.iterator();
while (iter.hasNext()){
SelectionKey key = iter.next();
iter.remove();//通过调用remove将这个键key从已选择键的集合中删除
if (key.isAcceptable()){
acceptHandler(key);
}else if (key.isReadable()){
readHandler(key);
}else if (key.isWritable()){
}
}
}
//3 处理一些 Task
//把队列里的消息 就是 Channel 注册到selector 中并做一些操作
if (!queue.isEmpty()){
Channel c = queue.take();
if (c instanceof ServerSocketChannel){
ServerSocketChannel server = (ServerSocketChannel) c;
server.register(selector,SelectionKey.OP_ACCEPT);
System.out.println(Thread.currentThread().getName()+" register listen");
}else if (c instanceof SocketChannel ){
SocketChannel client = (SocketChannel) c;
ByteBuffer byteBuffer = ByteBuffer.allocate(4096);
client.register(selector,SelectionKey.OP_READ,byteBuffer);
System.out.println(Thread.currentThread().getName()+"client register/"+client.getRemoteAddress());
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void readHandler(SelectionKey key) {
ByteBuffer buffer = (ByteBuffer)key.attachment();
SocketChannel client = (SocketChannel) key.channel();
buffer.clear();
while (true){
try {
int num = client.read(buffer);
if (num>0){
buffer.flip();//将读到的内容翻转 并写回
while (buffer.hasRemaining()){
client.write(buffer);
}
buffer.clear();
}else if (num==0){
break;
}else if (num<0){
//客户端断开了
System.out.println(client.getRemoteAddress()+"断开连接");
key.cancel();//从selector 中取消注册
// client.close();
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void acceptHandler(SelectionKey key) {
ServerSocketChannel server = (ServerSocketChannel)key.channel();
try {
SocketChannel client = server.accept();
client.configureBlocking(false);
//选择一个多路复用器 并注册进去
group.nextSelector(client);
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用
public class MainThread {
public static void main(String[] args) {
//不做关于IO 和 业务的事
//1 创建IO Thread 一个或多个
SelectorThreadGroup bossgroup = new SelectorThreadGroup(3);
SelectorThreadGroup workgroup = new SelectorThreadGroup(3);
bossgroup.setWorkGroup(workgroup);
//2 把监听的 9090 的 server 注册到某个selector 上
bossgroup.bind(9090);
}
}