理解Netty模型 (1) 基本实现

248 阅读3分钟

先来看看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);
    }
}