BIO, NIO threading model

BIO(Blocking IO)

BIO (Blocking IO): Synchronous blocking model, one client connection corresponds to one processing thread, that is: one thread processes one client request.
    Single-threaded version: The server cannot provide services to other clients until all events of the first client are processed.
    Multi-threaded version: If there are a large number of connections that only connect but do not send data, threads will always be occupied and server resources will be wasted.
    Thread pool version: Using the thread pool cannot fundamentally solve the problem. If we have 500 threads to form a thread pool, we use these 500 threads to serve the client. Then, assuming that the first 500 client requests come in, these 500 threads are occupied, and these 500 client connections only connect and do not send data, then our server will collapse? Because our server can only handle up to 500 client connections, and no matter how many you come in later, it will be rejected by our server, so using a thread pool cannot solve this problem.

public class SocketServer {
    public static void main(String[] args) throws Exception {
        //创建了服务端,绑定到了9001端口
        ServerSocket serverSocket = new ServerSocket(9001);
        while (true) {
            System.out.println("等待连接..");
            //阻塞方法
            Socket clientSocket = serverSocket.accept(); //监听客户端的连接,有客户端的连接代码才会往下走,没有连接就会阻塞在这里
            System.out.println("有客户端连接了..");

                        //1.单线程版本
            //handler(clientSocket);

            //2.多线程版本
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        handler(clientSocket);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();

            //3.线程池版本
            ExecutorService executorService = Executors.newFixedThreadPool(100);
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        handler(clientSocket);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    //handler方法是处理客户端事件的方法,比如客户端发来数据,在这里我们就做打印处理
    private static void handler(Socket clientSocket) throws Exception {
            byte[] bytes = new byte[1024];
            System.out.println("准备read..");
            //接收客户端的数据,阻塞方法,没有数据可读时就阻塞
            int read = clientSocket.getInputStream().read(bytes);
            System.out.println("read完毕。。");
            if (read != -1) {
                System.out.println("接收到客户端的数据:" + new String(bytes, 0, read));
            }
    }
}

NIO(Non Blocking IO)

NIO model version 1.0 (early version)

A thread infinitely loops to the list (storing the client connection) to check whether there is a read or write request, if there is, it will be processed, if not skipped. If there are too many connections in this version, there will be a large number of invalid traversals. If there are 1000 client connections, only 100 of them have read and write events, then it will still loop 1000 times, and 900 of them will be invalid.

public class NioServer {

    // 保存客户端连接
    static List<SocketChannel> channelList = new ArrayList<>();

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

        // 创建NIO ServerSocketChannel,与BIO的serverSocket类似,即创建了服务端并绑定了9001端口
        ServerSocketChannel serverSocket = ServerSocketChannel.open();
        serverSocket.socket().bind(new InetSocketAddress(9001));
        // 设置ServerSocketChannel为非阻塞
        serverSocket.configureBlocking(false);
        System.out.println("服务启动成功");

        while (true) {
            // 非阻塞模式accept方法不会阻塞,否则会阻塞
            // NIO的非阻塞是由操作系统内部实现的,底层调用了linux内核的accept函数
            SocketChannel socketChannel = serverSocket.accept();
            if (socketChannel != null) { // 如果有客户端进行连接
                System.out.println("连接成功");
                // 设置SocketChannel为非阻塞
                socketChannel.configureBlocking(false);
                // 保存客户端连接在List中
                channelList.add(socketChannel);
            }
            // 遍历连接进行数据读取  读写事件
            Iterator<SocketChannel> iterator = channelList.iterator();
            while (iterator.hasNext()) {
                SocketChannel sc = iterator.next();
                ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                // 非阻塞模式read方法不会阻塞,否则会阻塞
                int len = sc.read(byteBuffer);
                // 如果有数据,把数据打印出来,整个过程是一个main线程在处理
                if (len > 0) {
                    System.out.println(Thread.currentThread().getName() + " 接收到消息:" + new String(byteBuffer.array()));
                } else if (len == -1) { // 如果客户端断开,把socket从集合中去掉
                    iterator.remove();
                    System.out.println("客户端断开连接");
                }
            }
        }
    }
}

NIO model version 2.0 (multiplexer enabled, above jdk1.4)

The connection request sent by the client will be registered on the multiplexer selector, and the multiplexer will process the IO request when it polls the connection. JDK1.4 has been introduced. This version has been greatly optimized. When the client connects, if there is a read or write event, it will be added to a queue, and the event processing thread will block and wait for new elements in the queue to process the event.

step:

  1. Create a ServerSocketChannel server
  2. Create a multiplexer Selector (each operating system creates different ones, Centos creates EPollSelectorProviderImpl, Windows creates WindowsSelectorImpl, which is actually the Linux Epoll instance EPollArrayWrapper)
  3. ServerSocketChannel registers the connection establishment event to the Selector (the register method adds elements to the EPollArrayWrapper)
  4. Handle events
    1. If it is a connection event, the client's read and write requests are also registered in the Selector
    2. If it is a read and write event, it is processed according to business
public class NioSelectorServer {

    public static void main(String[] args) throws IOException {
        //指定感兴趣的操作类型为 OP_ACCEPT
        int OP_ACCEPT = 1 << 4;
        System.out.println(OP_ACCEPT);

        // 创建NIO ServerSocketChannel
        ServerSocketChannel serverSocket = ServerSocketChannel.open();
        serverSocket.socket().bind(new InetSocketAddress(9001));
        // 设置ServerSocketChannel为非阻塞
        serverSocket.configureBlocking(false);
        // 打开Selector处理Channel,即创建epoll,这里启用了多路复用器是与NIO1.0版本最大的区别
        Selector selector = Selector.open();
        // 把ServerSocketChannel注册到selector上,并且selector对客户端accept连接操作感兴趣(把连接事件注册到多路复用器里面)
        SelectionKey selectionKey = serverSocket.register(selector, SelectionKey.OP_ACCEPT);//通过 SelectionKey.OP_ACCEPT 来识别和处理接受连接事件。
        System.out.println("服务启动成功");

        while (true) {
            // 阻塞等待需要处理的事件发生 已注册事件发生后,会执行后面逻辑
            selector.select();

            // 获取selector中注册的全部事件的 SelectionKey 实例
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();

            // 遍历SelectionKey对事件进行处理
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                // 如果是OP_ACCEPT事件(连接事件),则进行连接获取和事件注册
                if (key.isAcceptable()) {
                    ServerSocketChannel server = (ServerSocketChannel) key.channel();
                    SocketChannel socketChannel = server.accept();
                    socketChannel.configureBlocking(false);
                    // 这里只注册了读事件,如果需要给客户端发送数据可以注册写事件
                    SelectionKey selKey = socketChannel.register(selector, SelectionKey.OP_READ);
                    System.out.println("客户端连接成功");
                } else if (key.isReadable()) {  // 如果是OP_READ事件(读事件),则进行读取和打印
                    SocketChannel socketChannel = (SocketChannel) key.channel();
                    ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                    int len = socketChannel.read(byteBuffer);
                    // 如果有数据,把数据打印出来
                    if (len > 0) {
                        System.out.println(Thread.currentThread().getName() +  "接收到消息:" + new String(byteBuffer.array()));
                    } else if (len == -1) { // 如果客户端断开连接,关闭Socket
                        System.out.println("客户端断开连接");
                        socketChannel.close();
                    }
                }
                //从事件集合里删除本次处理的key,防止下次select重复处理
                iterator.remove();
            }
        }
    }
}

Redis threading model

Redis is a typical epoll-based NIO thread model (nginx is also), the epoll instance collects all events (connection and read and write events), and a server thread processes all event commands continuously.

Guess you like

Origin blog.csdn.net/crazy_xieyi/article/details/131314611