Selector 为什么能用一个线程管成千上万个连接?这篇用同样的 3000 个连接对比 Selector 与每连接一线程的资源占用,再把水平触发和 OP_WRITE 两个常见的空转陷阱量出来(Netty 的粘包拆包、Selector.wakeup 在后面的篇目)。
3000 个连接,每个发 64 字节并收到回显:(每批最多 100 个在途,连接全部保持打开)总耗时 134 ms;服务端只用了 1 个线程(selector-thread);accept 了 3000 个 服务端 select() 返回了 4511 次,每次平均处理 1.1 个就绪的通道,最多一次 45 此时 JVM 里的 Java 线程数 7(含 main 与 JVM 自己的几个系统线程),进程 RSS 53 MB(3000 个连接都还开着;客户端也在这个进程里)
3000 个连接(同样每批最多 100 个在途,全部保持打开):总耗时 275 ms;服务端创建了 3000 个处理线程(accept 了 3000 个);创建线程失败:没有 此时 JVM 里的 Java 线程数 3007,进程 RSS 287 MB(3000 个连接都还开着;客户端也在这个进程里)
不读数据:200 ms 内 select 返回了「有就绪通道」的次数 = 640163 读掉数据:200 ms 内 select 返回了「有就绪通道」的次数 = 1 对已读完的连接再 select:200 ms 内 select 返回了「有就绪通道」的次数 = 0 注册了 OP_WRITE 却没有东西要写:200 ms 内 select 返回「就绪」的次数 = 636177(socket 发送缓冲一直有空位,所以一直就绪)