多个线程中的多个选择器

时间:2014-03-03 13:18:11

标签: java multithreading selector nio

在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?或者别的什么?

4 个答案:

答案 0 :(得分:3)

不是没有益处,因为需要处理的代码与IO操作所花费的时间的关系可以忽略不计。特别是如果您考虑分段数据同步所需的额外时间。然而,在单独的线程中完成接收数据的处理是有益的。

所以基本上:有一个单线程选择器循环,它将数据从一个缓冲区复制到任务缓冲区,以便在单独的线程中进一步处理,然后在Executor中启动带有该任务缓冲区的Runnable来处理复制的数据

答案 1 :(得分:1)

这正是Netty所做的。它使用N个线程调用Type:1BANKFROMBANK1TO2 -> Type:2BANKFROMBANK1TO2 Type:2BANKFROMBANK1TO2 -> Type:2BANKFROMBANK1TO2 Type:3BANKFROMBANK1TO2 -> Type:2BANKFROMBANK1TO2 Type:4BANKFROMBANK1TO2 -> Type:2BANKFROMBANK1TO2 ,其中N =(可用CPU数* 2)由于超线程而乘以2。这是父EventLoop,它被视为接受套接字连接的“接受者事件循环”。 然后是线程工作者的子EventLoop

建议您看看$Replace = Get-ChildItem "C:\temp\" -Filter "xfil*" Foreach ($file in $Replace) { (Get-Content $File.fullname).replace((Get-Content $File.fullname)[5],'x') | Set-Content -Path $file.FullName }

答案 2 :(得分:-1)

我只有一个选择器,并通过无锁环缓冲区将消息分布在固定数量的线程上。然后,您可以完全无锁,超快速地进行流动。流程就像那样:

关键选择器=&gt; DEMUX =&gt;工人线程=&gt; MUX =&gt;关键选择器

您只需要确保您的工作线程数量足够(并且您有足够的可用内核)来快速处理您的消息,否则您最终可能会使用完整的DEMUX并且选择器必须阻止或丢弃消息。

我建议您阅读this article,了解异步,单线程,非阻塞网络框架的工作原理。当然你也可以查看Netty或Mina。有关延迟的概念,您必须使用demux和mux(纳秒)来支付,请参阅this benchmark

答案 3 :(得分:-1)

  

在n个线程之间分配传入连接是否有益,每个线程都有自己独立的NIO选择器,其中n是服务器中的核心数量?

所有主要框架都以这种方式运作; Mina,Netty,Nginx等都在多个选择器之间分配套接字。 Nginx直接将文件描述符传递给具有自己的选择器的分叉进程。

您在这里描述的是基本的选择器 - 工作者模型。每个核心通常有2名工人。每个工人都有一个Selector。这个模型在Windows上是绝对必要的,因为每个Selector只能处理1024个套接字,而JDK所做的解决方案对性能来说是灾难性的。

http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b27/sun/nio/ch/WindowsSelectorImpl.java

  • 核心0 - 线程 - &gt;选择器 - &gt; OP_READ / OP_WRITE - &gt;做生意逻辑
  • 核心1 - 线程 - &gt;选择器 - &gt; OP_READ / OP_WRITE - &gt;做生意逻辑
  • Core 2 - Thread - &gt;选择器 - &gt; OP_READ / OP_WRITE - &gt;做生意逻辑
  • Core 3 - Thread - &gt;选择器 - &gt; OP_READ / OP_WRITE - &gt;做生意逻辑

这是缩放反应器模式的基本方法。

单个SocketChannel仅附加到单个Selector非常重要。因此,请不要将SocketChannel.register()致电Selectors给所有人{strong> OP_READ ,因为您无法获得任何好处。

http://man7.org/linux/man-pages/man7/epoll.7.html

  

Q2 两个epoll实例可以等待相同的文件描述符吗?如果              那么,是否向两个epoll文件描述符报告了事件?

     

A2 是的,并且会向两者报告事件。但是,小心              可能需要编程来正确执行此操作。