Netty (15)-three implementations of threading model

One, single Reactor single thread

1.1 Working principle

Single Reactor single thread

1.2 NIO group chat system

/**
 * @desc 群聊服务器端
 * @author yxs
 * @date 2021-02-06 16:50
 */
public class GroupChatServer {

    // 定义属性
    private Selector selector;
    private ServerSocketChannel listenChannel;
    private static final int PORT = 6667;

    // 构造器,完成初始化操作
    public GroupChatServer(){
        try {

            // 得到选择器
            selector = Selector.open();
            // 初始化 ServerSocketChannel
            listenChannel = ServerSocketChannel.open();
            // 绑定端口
            listenChannel.socket().bind(new InetSocketAddress(PORT));
            // 设置非阻塞模式
            listenChannel.configureBlocking(false);
            // 将 listenChannel 注册到 selector
            listenChannel.register(selector, SelectionKey.OP_ACCEPT);

        }catch (IOException e){
            e.printStackTrace();
        }
    }

    // 监听
    public void listen(){

        System.out.println("监听线程:" + Thread.currentThread().getName());
        try {
            // 循环处理
            while (true){
                int count = selector.select();
                if(count > 0){ // 有事件处理

                    // 遍历得到的 selectionKey 集合
                    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                    while (iterator.hasNext()){
                        // 取出 selectionKey
                        SelectionKey key = iterator.next();

                        // 监听到 accept 事件
                        if(key.isAcceptable()){
                            SocketChannel sc = listenChannel.accept();
                            sc.configureBlocking(false);
                            // 将 sc 注册到 selector 上
                            sc.register(selector,SelectionKey.OP_READ);
                            // 提示
                            System.out.println(sc.getRemoteAddress() + " 上线");
                        }

                        // 监听到 read 事件,即通道是可读的状态
                        if(key.isReadable()){
                            // 处理读(专门写方法...)
                            readData(key);
                        }

                        // 删除当前 key,防止重复处理
                        iterator.remove();
                    }
                }else{
                    System.out.println("等待....");
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {

        }
    }

    // 读取客户端消息
    private void readData(SelectionKey key){
        // 定义一个 SocketChannel
        SocketChannel channel = null;
        try {
            // 获取到关联的 channel
            channel = (SocketChannel)key.channel();
            // 创建 buffer
            ByteBuffer buffer = ByteBuffer.allocate(1024);

            int count = channel.read(buffer);
            // 根据 count 的值做处理
            if(count > 0){
                // 把缓冲区的数据转成字符串
                String msg = new String(buffer.array());
                // 输出该消息
                System.out.println("from client:" + msg);
                // 向其它的客户端转发消息(去掉自己),专门写一个方法来处理
                sendInfoToOtherClients(msg,channel);
            }
        }catch (IOException e){
            if(channel != null){
                try {
                    System.out.println(channel.getRemoteAddress() + " 离线了");
                    // 取消注册
                    key.cancel();
                    // 关闭通道
                    channel.close();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }
    }

    // 转发消息给其它的客户端(通道)
    private void sendInfoToOtherClients(String msg,SocketChannel self) throws IOException {

        System.out.println("服务器转发消息中...");
        System.out.println("服务器转发数据给客户端线程:" + Thread.currentThread().getName());
        // 遍历所有注册到 selector 上的 socketChannel,并排除 self
        for(SelectionKey key:selector.keys()){
            // 通过 key 取出对应的 SocketChannel
            Channel targetChannel = key.channel();

            // 排除自己,TODO
            if(targetChannel instanceof SocketChannel && targetChannel != self){
                // 转型
                SocketChannel dest = (SocketChannel) targetChannel;
                // 将 msg 存储到 buffer
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                // 将 buffer 的数据写入通道
                dest.write(buffer);
            }
        }

    }

    public static void main(String[] args) {

        // 创建服务器对象
        GroupChatServer chatServer = new GroupChatServer();
        chatServer.listen();
    }

}

Results of the

1.3 Program description

1) Select is the standard network API of the I/O multiplexing model, which can realize that the application monitors multiple connection requests through a blocking object.
2) The Reactor object listens to the client request event through Select, and distributes it through Dispatch after receiving the event
3) If it is a connection request event, the Acceptor will process the connection request through Accept, and then create a Handler object to handle the subsequent business processing after the connection is completed.
4) If it is not a connection event, Reactor will dispatch and call the corresponding Handler for the connection To respond to
5), Handler will complete the complete business process of Read->Business Processing->Send

Combined case: The server uses one thread to handle all IO operations (connection, read, write, etc.) through multiplexing. The coding is simple and clear, but if the number of client connections is large, it will not be able to support.

1.4 Advantages and disadvantages

1) Advantages: The model is simple, there are no problems of multi-threading, process communication, and competition. All are completed in one thread.
2) Disadvantages: Performance problems. There is only one thread, which cannot fully utilize the performance of multi-core CPUs. When the Handler is processing the business on a certain connection, the entire process cannot handle other connection events, which can easily lead to performance bottlenecks
3). Disadvantages: reliability problems, unexpected termination of threads, or infinite loops, which will cause the communication module of the entire system to be unavailable , Unable to receive and process external messages, causing node failure
4) Applicable scenarios: The number of clients is limited, and the business processing is very fast, such as the time complexity of Redis in business processing O(1).

Two, single Reactor multi-threaded

2.1 Working principle

Single Reactor multi-threaded

2.2 Program description

1) The Reactor object listens to the client request event through Select, and distributes it through Dispatch after receiving the event.
2) If it is a connection request event, the Acceptor handles the connection request through Accept, and then creates a Handler object to handle the connection after the completion of the connection. Subsequent business processing
3). If it is not a connection event, Reactor will dispatch and call the corresponding Handler to handle the connection.
4). Handler is only responsible for responding to the event and does not perform specific business processing. After reading the data through read, it will be distributed to the back A thread of the worker thread pool handles the business
5), the worker thread pool will allocate an independent thread to complete the real business, and return the result to the Handler
6), the Handler returns the result to the client through send after receiving the response

2.3 Advantages and disadvantages

1) Advantages: can make full use of the processing power of multi-core CPU (multi-threading)
2) Disadvantages: multi-threaded data sharing and access are more complicated, Reactor handles the monitoring and response of all events, and Reactor runs in a single thread, high Performance bottlenecks are prone to occur in concurrent scenarios

Three, master-slave Reactor multithreading

3.1 Working principle

Master-slave Reactor multithreading

3.2 Program description

1) The MainReactor object of the main Reactor thread listens to connection events through select. After receiving the event, it processes the connection event through Acceptor.
2) When the Acceptor processes the connection event, MainReactor assigns the connection to SubReactor
3). SubReactor adds the connection to the connection queue Monitor and create a Handler for various event processing
4) When a new event occurs, SubReactor will call the corresponding Handler for processing
5) After the Handler reads the data through read, it will be distributed to the subsequent worker thread pool A certain thread handles the business
6), the worker thread pool will allocate an independent thread to complete the real business, and return the result to the Handler
7), after the Handler receives the response, the result is returned to the client through send
8), the Reactor main thread can Corresponding to multiple Reactor sub-threads, that is, MainReactor can be associated with multiple SubReactor

3.3 Recommended references

"Scalable IO In Java" illustrates the principle of Multiple Reactors:
Scalable IO In Java
Doug Lea

3.4 Advantages and disadvantages

1) Advantages: The data interaction between the parent thread and the child thread is simple and the responsibilities are clear. The parent thread only needs to receive new connections, and the child thread completes the subsequent business processing.
2) Advantages: The data interaction between the parent thread and the child thread is simple, and the Reactor main thread Only need to pass the new connection to the child thread, the child thread does not need to return data
3), disadvantage: higher programming complexity

Combined example: This model is widely used in many projects, including Nginx master-slave Reactor multi-process model, Memcached master-slave multi-threading, Netty master-slave multi-threading model, etc.

Fourth, the Reactor model summary

3 modes are understood by life cases:
1) Single Reactor single thread, the receptionist and waiter at the front desk are the same person, serving customers throughout the process
2) Single Reactor multi-threaded, 1 receptionist at the front desk, multiple waiters, receptionists Responsible for reception only
3), master-slave Reactor multi-thread, multiple receptionists, multiple waiters

Advantages of Reactor mode:
1), fast response, no need to be blocked by a single synchronization time, although Reactor itself is synchronized
2), can avoid complex multi-threading and synchronization problems to the greatest extent, and avoid multi-thread/process switching overhead
3) Good scalability, which can easily make full use of CPU resources by increasing the number of Reactor instances.
4) Good reusability. The Reactor model itself has nothing to do with specific event processing logic and has high reusability.

Guess you like

Origin blog.csdn.net/yangxshn/article/details/113871183