我们都知道 TCP 连接建立是需要三次握手
同一个端口的两个 socket:默认不能同时绑定监听,因此无法各自建立连接(特殊配置下的复用不改变 “四元组唯一” 的核心规则)。 端口的作用是 “区分服务”,而连接的唯一性由 “四元组” 保证,因此多个端口自然支持更多独立连接,这也是服务器通常会开放多个端口(如 80、443、3306 等)提供不同服务的原因。
我们都知道 TCP 连接建立是需要三次握手
同一个端口的两个 socket:默认不能同时绑定监听,因此无法各自建立连接(特殊配置下的复用不改变 “四元组唯一” 的核心规则)。 端口的作用是 “区分服务”,而连接的唯一性由 “四元组” 保证,因此多个端口自然支持更多独立连接,这也是服务器通常会开放多个端口(如 80、443、3306 等)提供不同服务的原因。
分布式 ID 常见解决方案
incr
自增操作是 原子性的,在高并发环境下也不会出现竞争条件
Netty 的 I/O 模型是基于非阻塞 I/O 实现的,底层依赖的是 NIO 框架的多路复用器 Selector。采用 epoll 模式后,只需要一个线程负责 Selector 的轮询。当有数据处于就绪状态后,需要一个事件分发器(Event Dispather),它负责将读写事件分发给对应的读写事件处理器(Event Handler)。事件分发器有两种设计模式:Reactor 和 Proactor,Reactor 采用同步 I/O, Proactor 采用异步 I/O。
在 Java NIO 和操作系统 I/O 模型里,epoll
、select
、poll
都是 I/O 多路复用机制,主要用来监控多个文件描述符(socket 等)是否可读/可写。它们的区别可以从以下几个方面理解:
select
限制:
最大监控 fd 数量有限(一般 1024,依赖编译时 FD_SETSIZE
)。
poll
改进:
没有 select
的 1024 限制,可以监听更多 fd。
缺点:
每次调用依然需要 线性扫描整个数组,性能随 fd 数量线性下降。
epoll
特点:
注册事件时(epoll_ctl
)只需要传一次,内核会保存,不必像 select/poll
那样每次传整个 fd 集合。
epoll_wait
时直接返回活跃的 fd。复杂度:
添加/删除 fd 是 O(log n)(红黑树)。
| 特性 | select | poll | epoll | | ------- | ------------------ | ------------- | ------------------- | | 数据结构 | 位图 | 数组 | 红黑树 + 就绪链表 | | fd 数量限制 | 1024 (FD_SETSIZE) | 无限制 | 无限制 | | 拷贝开销 | 每次调用都拷贝 fd 集合 | 每次调用都拷贝 fd 数组 | 注册时一次,之后无需重复拷贝 | | 事件检测方式 | 轮询(线性扫描) | 轮询(线性扫描) | 事件驱动(就绪队列) | | 性能 | O(n),低效 | O(n),低效 | O(1),高效 | | 适用场景 | 小规模连接 | 中等规模连接 | 大规模高并发(如 NIO、Netty) |
Java 的 Selector
底层在不同操作系统上会使用不同实现:
Linux:优先用 epoll
(高性能)。
kqueue
。select
或 IOCP。Selector.select()
,在 Linux 下实际上就是基于 epoll
。