Reactor单线程模型

看这篇博客建议先看下netty(一)中的几种网络IO模型

Netty是典型的Reactor模型结构,Reactor模式也叫反应器模式,大多数IO相关组件如Netty、Redis在使用的IO模式。接下来我们看下原始多线程IO模型实现,以及单线程Reactor模型的优缺点以及实现

 

最原始的多线程IO模型,服务器用一个while循环,不断监听端口是否有新的Socket连接,当有连接时处理对应的Socket,在处理的过程中,没有办法继续接收其他的连接,所有的请求执行都是串行的,可想而知这种模型的处理效率是相当低的,如果某个请求操作是比较耗时的将导致所有的请求都阻塞。这个模型的简单实现是这样的

while(true){
//接收请求
socket = accept();
//执行请求,所有的操作都是单线程
handle(socket)
//处理完后继续接收等待
}

这种方法的最大问题是无法并发,效率太低,如果当前的请求没有处理完,那么后面的请求只能被阻塞,服务器的吞吐量太低,因此想到了使用多线程来改进,当接收到请求后,开启一个子线程来处理具体的事物,主线程继续监听端口,代码实现如下

//ServerSocket监听连接请求,将连接后的Socket交给新的线程处理
public class Main {

    public static void main(String[] args) throws IOException {

        //端口
        int port = 8080;
        if (args != null && args.length > 0){
            try {
                port = Integer.valueOf(args[0]);
            }catch (NumberFormatException e){
                ;
            }
        }


        ServerSocket serverSocket = null;
        Socket socket = null;
        try {
            //初始化ServerSocket
            serverSocket = new ServerSocket(port);
            //循环监听
            while (true){
                socket = serverSocket.accept();
                //交给子线程处理事物
                new Thread(new Handler(socket)).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (serverSocket != null){
                serverSocket.close();
            }
        }
    }


}
//处理事件的Handler
public class Handler implements Runnable {

    private Socket socket = null;

    public Handler(Socket socket) {
        //接收Socket
        this.socket = socket;
    }

    @Override
    public void run() {
        //读
        BufferedReader in = null;
        //写
        PrintWriter out = null;

        try {
            in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
            out = new PrintWriter(socket.getOutputStream(),true);
            String body = null;
            while (true){
                body  = in.readLine();
                if (body == null){
                    break;
                }
                System.out.println(body);
                out.print(2);
            }
        } catch (IOException e) {
            if (in != null){
                try {
                    in.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            if (out != null){
                out.close();
            }
            if (socket != null){
                try {
                    socket.close();
                    socket = null;
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            e.printStackTrace();
        }
    }
}

 

这种模型在一定程度上提高了系统的吞吐量,但是每个请求都要新建一个线程,当请求量较大时,服务将会收到极大的影响,Java中的线程直接对应到操作系统,大量的线程创建销毁需要巨大的成本,为了对这种模型进行改进,很多人想到了用线程池或者队列来避免大量的线程创建,也确实这么做了,但是这都是治标不治本的做法,那么IO复用模型就出来了,通过这种模型可以达到使用少量线程处理多个Socket客户端的能力,如下图所示,Reactor线程循环监听请求事件,遇到请求时将事件注册到Selector上,然后处理Selector上准备好的事件,整个流程都是一个线程在处理。

 

 



class Reactor implements Runnable
{
    final Selector selector;
    final ServerSocketChannel serverSocket;

    Reactor(int port) throws IOException
    { //Reactor初始化
        selector = Selector.open();
        serverSocket = ServerSocketChannel.open();
        serverSocket.socket().bind(new InetSocketAddress(port));
        //非阻塞
        serverSocket.configureBlocking(false);

        //分步处理,第一步,接收accept事件
        SelectionKey sk =
                serverSocket.register(selector, SelectionKey.OP_ACCEPT);
        //attach callback object, Acceptor
        sk.attach(new Acceptor());
    }

    public void run()
    {
        try
        {
            while (!Thread.interrupted())
            {
                selector.select();
                Set selected = selector.selectedKeys();
                Iterator it = selected.iterator();
                while (it.hasNext())
                {
                    //Reactor负责dispatch收到的事件
                    dispatch((SelectionKey) (it.next()));
                }
                selected.clear();
            }
        } catch (IOException ex)
        { /* ... */ }
    }

    void dispatch(SelectionKey k)
    {
        Runnable r = (Runnable) (k.attachment());
        //调用之前注册的callback对象
        if (r != null)
        {
            r.run();
        }
    }

    // inner class
    class Acceptor implements Runnable
    {
        public void run()
        {
            try
            {
                SocketChannel channel = serverSocket.accept();
                if (channel != null)
                    new Handler(selector, channel);
            } catch (IOException ex)
            { /* ... */ }
        }
    }
}

 

Handler的实现

class Handler implements Runnable
{
    final SocketChannel channel;
    final SelectionKey sk;
    ByteBuffer input = ByteBuffer.allocate(SystemConfig.INPUT_SIZE);
    ByteBuffer output = ByteBuffer.allocate(SystemConfig.SEND_SIZE);
    static final int READING = 0, SENDING = 1;
    int state = READING;

    Handler(Selector selector, SocketChannel c) throws IOException
    {
        channel = c;
        c.configureBlocking(false);
        // Optionally try first read now
        sk = channel.register(selector, 0);

        //将Handler作为callback对象
        sk.attach(this);

        //第二步,注册Read就绪事件
        sk.interestOps(SelectionKey.OP_READ);
        selector.wakeup();
    }

    boolean inputIsComplete()
    {
        /* ... */
        return false;
    }

    boolean outputIsComplete()
    {

        /* ... */
        return false;
    }

    void process()
    {
        /* ... */
        return;
    }

    public void run()
    {
        try
        {
            if (state == READING)
            {
                read();
            }
            else if (state == SENDING)
            {
                send();
            }
        } catch (IOException ex)
        { /* ... */ }
    }

    void read() throws IOException
    {
        channel.read(input);
        if (inputIsComplete())
        {

            process();

            state = SENDING;
            // Normally also do first write now

            //第三步,接收write就绪事件
            sk.interestOps(SelectionKey.OP_WRITE);
        }
    }

    void send() throws IOException
    {
        channel.write(output);

        //write完就结束了, 关闭select key
        if (outputIsComplete())
        {
            sk.cancel();
        }
    }
}

 

当其中某个 handler 阻塞时, 会导致其他所有的 client 的 handler 都得不到执行, 并且更严重的是, handler 的阻塞也会导致整个服务不能接收新的 client 请求(因为 acceptor 也被阻塞了)。 因为有这么多的缺陷, 因此单线程Reactor 模型用的比较少。这种单线程模型不能充分利用多核资源,所以才有了之后的改进,将事件处理扔给线程池,主线程负责接收连接,以及读写,再之后优化到专门用一个线程池来接收连接,之后将连接的socket通过负载均衡分配给不同的selector,不同的selector通过拥有的worker threadpool去处理具体的事物,从而达到高并发,高可用,高吞吐量,在下面博客我会贴上多线程模型的实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值