zoukankan      html  css  js  c++  java
  • IO学习笔记5

    2.2 NIO

    由于上面BIO的弊端,以及为了解决C10K的问题,出现了NIO模型(NonBlockingIO)。java中的nio指new io,而linux中的nio指NonblockingIO。

    NIO是同步非阻塞模型。

    代码如下:

    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    import java.util.ArrayList;
    
    /**
     * @author shuai.zhao@going-link.com
     * @date 2021/6/2
     */
    public class SocketNIO {
        public static void main(String[] args) throws IOException {
    
            ArrayList<SocketChannel> clients = new ArrayList<>();
    
            ServerSocketChannel ssc = ServerSocketChannel.open();
            ssc.bind(new InetSocketAddress(9000));
    
            ssc.configureBlocking(false);
    
            while (true) {
              // 如果没有客户端连接进来则返回null
                SocketChannel newClient = ssc.accept();
                if (newClient != null) {
                    System.out.println("client " + newClient.getRemoteAddress() + " is in");
                    newClient.configureBlocking(false);
                    clients.add(newClient);
                }
    
                ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
                for (SocketChannel client : clients) {
                    int read = client.read(byteBuffer);
                    if (read > 0) {
                        byteBuffer.flip();
                        byte[] bytes = new byte[byteBuffer.limit()];
                        byteBuffer.get(bytes);
                        System.out.println("client:" + client.socket().getPort() + " write: " + new String(bytes));
                        byteBuffer.clear();
                    }
                }
            }
        }
    }
    
    

    用一个线程去处理10K个客户端连接。

    再次使用strace去追踪启动服务端,查看主线程系统调用:

    ........
    accept(4, 0x7f2fbc0d21a0, [28])         = -1 EAGAIN (资源暂时不可用)
    accept(4, 0x7f2fbc0d4d50, [28])         = -1 EAGAIN (资源暂时不可用)
    accept(4, 0x7f2fbc72f070, [28])         = -1 EAGAIN (资源暂时不可用)
    accept(4, 0x7f2fbc0d19e0, [28])         = -1 EAGAIN (资源暂时不可用)
    accept(4, 0x7f2fbc0d21a0, [28])         = -1 EAGAIN (资源暂时不可用)
    accept(4, 0x7f2fbc0d4d50, [28])         = -1 EAGAIN (资源暂时不可用)
    accept(4, 0x7f2fbc72f070, [28])         = -1 EAGAIN (资源暂时不可用)
    accept(4, 0x7f2fbc0d19e0, [28])         = -1 EAGAIN (资源暂时不可用)
    

    可以看到服务端是不再阻塞的,每次循环accept()方法如果没有接收到客户端连接,就返回-1,对应java里就返回null,然后继续下一次循环。使用nc连接服务端,发送数据,然后可以在主线程的系统调用文件out.13101中找到发送的数据,而且后续调用为:

    accept(4, 0x7f2fbc0d19e0, [28])         = -1 EAGAIN (资源暂时不可用)
    read(5, 0x7f2fbc0ff6a0, 1024)           = -1 EAGAIN (资源暂时不可用)
    accept(4, 0x7f2fbcea0cb0, [28])         = -1 EAGAIN (资源暂时不可用)
    read(5, 0x7f2fbc0ffab0, 1024)           = -1 EAGAIN (资源暂时不可用)
    accept(4, 0x7f2fbc0d4d50, [28])         = -1 EAGAIN (资源暂时不可用)
    read(5, 0x7f2fbc0ffec0, 1024)           = -1 EAGAIN (资源暂时不可用)
    accept(4, 0x7f2fbc0d21a0, [28])         = -1 EAGAIN (资源暂时不可用)
    read(5, 0x7f2fbc1002d0, 1024)           = -1 EAGAIN (资源暂时不可用)
    accept(4, 0x7f2fbc0d19e0, [28])         = -1 EAGAIN (资源暂时不可用)
    read(5, 0x7f2fbc1006e0, 1024)           = -1 EAGAIN (资源暂时不可用)
    

    同一个线程同时accept和从客户端read数据,没有时都返回null。

    使用C10K客户顿端调用,服务端控制台输出结果如下:

    // 服务端
    client /127.0.0.1:20210 is in
    client /127.0.0.1:20211 is in
    client /127.0.0.1:20212 is in
    Exception in thread "main" java.io.IOException: Too many open files
    	at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
    	at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:424)
    // 客户端
    connection time consuming:52739
    clients = 10214
    

    (ps: 因为我是本地调用,所以可用fd不够,可以在linux下通过cat /proc/sys/fs/file-max命令查看当前用户可打开的fd数量,使用root用户时这个数量非常大,可以正常连接完这10w个连接数的。但是NIO仍无法避免的问题一个问题就是 fd文件不够,因为在生产环境不可能使用root用户来启动服务)

    虽然本地无法启动建立完所有的连接,但是使用在linux上是可以建立的。我们使用c10K问题主要是为了看IO效率,至于fd不够的问题,通过扩容来解决(一台不够就再加一台),但是在使用NIO的时候,瓶颈并不在连接数上,因为只从fd考虑,NIO单台服务器就可以支持十万。从效率上看,我本地建立10214个连接耗时 52739毫秒。这里记一下下面我们会有更高效的方法

  • 相关阅读:
    java多线程:并发包中ConcurrentHashMap和jdk的HashMap的对比
    java编程之:Unsafe类
    mave之:java的web项目必须要的三个jar的pom形式
    java多线程:并发包中ReentrantReadWriteLock读写锁的锁降级模板
    java多线程:并发包中ReentrantReadWriteLock读写锁的原理
    java编程之:按位与运算,等运算规则
    java多线程:jdk并发包的总结(转载)
    liunx之:wps for liunx的安装经验
    mysql中enum类型理解
    MySQL类型float double decimal的区别
  • 原文地址:https://www.cnblogs.com/Zs-book1/p/14889521.html
Copyright © 2011-2022 走看看