在n个线程之间分配传入连接是否有益,每个线程都有自己独立的NIO Selector
,其中n是服务器中的核心数量?假设我正在编写一个服务器,它应该处理许多客户端连接。我可以有类似的东西:
selector.select();
Iterator<SelectionKey> i = selector.selectedKeys().iterator();
while (i.hasNext()) {
SelectionKey key = i.next();
i.remove();
if (!key.isValid())
continue;
if (key.isAcceptable()) {
// get one of the n selectors (I'd have one per core)
Selector chosenSelector = getRandomSelector();
// delegate the new connection to the chosen selector
SocketChannel newChannel = key.channel.accept();
newChannel.configureBlocking(false);
newChannel.register(chosenSelector, SelectionKey.OP_READ);
}
}
你们认为这有道理吗?我的意思是,运行n个线程,每个线程都有不同的选择器?或者我应该坚持使用一个单一的选择器线程来处理所有连接的OP_READ?或者别的什么?
不,它没有益处,因为需要处理的代码与IO操作所花费的时间之间的关系可以忽略不计。特别是如果您考虑分段数据同步所需的额外时间。然而,在单独的线程中完成接收数据的处理是有益的。
所以基本上:有一个单线程选择器循环,它将数据从一个缓冲区复制到任务缓冲区,以便在单独的线程中进一步处理,然后在Executor中启动带有该任务缓冲区的Runnable来处理复制的数据。
在n个线程之间分配传入连接是否有益,每个线程都有自己独立的NIO选择器,其中n是服务器中的核心数量?
所有主要框架都以这种方式运作; Mina,Netty,Nginx等都在多个选择器之间分配套接字。 Nginx字面上将文件描述符传递给具有自己的选择器的分叉进程。
你在这里描述的是基本的选择器 - 工人模型。每个核心通常有2名工人。每个工人都有一个Selector
。这个模型在Windows上是绝对必要的,因为每个Selector只能处理1024个套接字,而JDK所做的解决方案对于性能来说是灾难性的。
这是缩放反应器模式的基本方法。
重要的是单个SocketChannel
只附加到单个Selector
。因此,不要将SocketChannel.register()
调用到一堆Selectors
并在所有这些上面听OP_READ因为你不会获得任何好处。
http://man7.org/linux/man-pages/man7/epoll.7.html
Q2两个epoll实例可以等待相同的文件描述符吗?如果是这样,是否向两个epoll文件描述符报告了事件?
A2是的,会向两者报告事件。但是,可能需要仔细编程才能正确执行此操作。
我只有一个选择器,让它通过无锁环缓冲区在固定数量的线程上分发消息。然后,您可以完全无锁,超快速地进行流动。流程将是这样的:
Critical Selector => DEMUX =>工作线程=> MUX =>临界选择器
您只需要确保您的工作线程数量足够(并且您有足够的可用内核)来快速处理您的消息,否则您最终可能会使用完整的DEMUX,并且选择器必须阻止或丢弃消息。
我建议你阅读this article,了解异步,单线程,非阻塞网络框架的工作原理。当然你也可以查看Netty或Mina。有关延迟的概念,您必须使用demux和mux(纳秒)来支付,请参阅this benchmark。