服务器本质上是一个项目队列,而客户端充当这些项目的生产者和/或消费者。
服务器必须:
HashMap.get
; PriorityQueue.poll
或PriorityQueue.offer
; 最简单的设置方法是通过一个线程accept
客户端,然后为每个客户端创建两个线程:
InputStream
的人; OutputStream
,并将信息发送给客户端。当然这不可扩展,每个客户端都有两个线程似乎很浪费。
我还考虑过使用单个线程,这将是
read
的套接字超时约1秒; read
超时,或者在处理完请求后,继续将每个新事件发送给客户; 但是,对请求和事件进行轮询也是浪费。
另一种方法是使用线程池,并将上述两个操作中的每一个放在各自的Runnable
中。然后,这些可运行的队列将在Executor
中相互排队。
如果不是更多的话,这似乎也是浪费。
我一直在阅读some questions,我现在对NIO感到好奇,因为非阻塞操作和事件驱动的服务器似乎是正确的方法。
以上任何设计是否适合此项任务,还是应该使用NIO解决?
就数字而言,这不仅仅是一个练习而是一个真实的系统,所以它不必处理成千上万的客户,但理想情况下,应该> em>能够很好地执行和扩展。
答案 0 :(得分:2)
你需要继续使用ThreadPool因为你需要管理Runners!我建议你在你的业务中建立MOA结构,因为这个客户端连接到服务器并且服务器等待客户端请求(数据),然后服务器队列作业(如果没有可用于处理的线程)并且imediatly响应一个长值,该值指向服务器上的客户端进程ID并关闭套接字。 现在如果客户请求已处理并准备采取行动怎么办?所以这里有两种方法,好的一种是服务器发信号通知客户端(因此客户端需要监听服务器响应[ServerSocket])关于完成的请求。 OR客户端定期检查服务器并检查进程的状态。
答案 1 :(得分:1)
每个客户端的两个线程绝对不可扩展。
如果服务器上有M个核心,那么实际上你不可能比运行M个线程更好。任何更高的东西都会让你震惊,并且会减少每秒执行的操作次数。
更好的设计将可用内核划分为U更新器和L侦听器,其中 U + L == M.
为更新程序线程分配每个客户端(理想情况下具有负载平衡,但这是一个装饰)。每个更新事件都会多播到所有更新程序线程,每个更新程序线程都会更新其所有已分配的客户端更新程序列表末尾的客户端比一开始时更新的客户端更新,但没有任何帮助:您只有这么多硬件。
类似地,每个客户端都分配给一个侦听器线程,该线程处理多个侦听器。客户端输入被转储到FIFO队列中,并在侦听器线程到达时自动处理。
然后,每个线程都可以在内存中保持活动状态,同时客户端数据通过系统移动。设计优雅地降级,因为太多客户端意味着所有更新都会变慢,因为客户端数量的线性函数。你提出的设计会比这更快地降低。
现代(例如,比2002年更晚)网络服务器将这一切埋在实施的深处,因此开发人员不需要管理它。但它仍然是一项有用的练习。
答案 2 :(得分:1)
以上设计完全没问题。这正是在引入nio之前Web服务器的工作方式。
您有多少客户?如果不是那么多,不要担心可扩展性并尽量避免复杂的nio api。如果你真的需要扩展,可以考虑使用某种抽象,例如netty。使用nio可能相当复杂,在不同的操作系统上可能无法使用相同的方式(一旦遇到奇怪的错误,只能在特定的操作系统上重现)。
使用nio,您可以处理1-4个线程中的所有客户端请求/响应流
有时IO可能胜过NIO。
请参阅此回答 - Old I/O thread per client model or NIO reactor pattern?
答案 3 :(得分:1)
我设计并实现了几个生产级别的实时系统(谈论毫秒级或更少的延迟,但少数客户端)。恕我直言,你应该采取NIO方法。
NIO的核心基本上是select(),它允许您同时处理来自不同套接字/客户端的输入。之后,将事件放入适当的队列和/或在整个系统中广播。然后,如何处理队列和分配线程完全独立于IO任务,并由您自己调整。
另请参阅ZeroMQ,它基本上应用了相同的想法;通过多个Socket模型看看他们的Poller。我相信大多数现代消息框架,包括JMS / EMS,TibcoRV,29 West LBM等(现在在Informatica下)都采用了类似的方法。