2013-10-02 98 views
3

服务器本质上是一个项目队列,而客户端充当这些项目的生产者和/或消费者。Java中的多线程服务器

服务器必须:

  • 倾听放/取请求,并相应地处理它们 - 这通常不会花太长时间,它包括:
    1. 解析短串;
    2. HashMap.get;
    3. 获取锁定;
    4. A PriorityQueue.pollPriorityQueue.offer;
  • 通知所有项目活动的每一个客户端,只要有可能,让每一个客户有一个实时鉴于这是怎么回事的。

设置此功能的最简单的方法,是由具有螺纹accept荷兰国际集团的客户,然后为每个客户端创建两个线程:

  • 一个处理该InputStream,哪些块等待请求;
  • 而另一个处理OutputStream,它监听队列上的事件,并将信息发送到客户端。

当然,这是不可扩展的,并且为每个客户端拥有两个线程似乎是浪费。

我也想过使用一个线程,这将

  • 设置套接字超时约1read秒;
  • 继续发送每个新事件给客户端,如果read超时或处理请求后;
  • 循环这两个动作。

但是,轮询请求和事件也是浪费。

另一种方法是使用线程池,并将上述两个操作中的每一个放在它们各自的Runnable中。然后这些可运行的对象将在Executor中彼此排队。 这似乎也是浪费,如果不是更多。

我一直在读somequestions,我现在好奇NIO,因为非阻塞操作和事件驱动服务器似乎是正确的方法。

以上任何一种设计是否适合这项任务,还是应该使用NIO来解决?

就数字而言,这更是一个锻炼比真正的系统,所以它与数千客户不必交易,但是,理想情况下,它应该能够执行和规模好。

回答

1

每个客户端的两个线程绝对不可伸缩。

如果服务器上有M个核心,那么实际上并不比运行M个线程要好。任何更高级别的暴露都会导致抖动,并会减少每秒执行的操作次数。

更好的设计分区可用核成ù更新器和L听众,其中 U + L型== M.

每个客户端被分配(理想地与负载平衡,但是这是一个点缀),以更新器线程。每个更新事件都会多播到所有更新器线程,然后每个更新器线程都会更新其所有分配的客户端。更新程序列表末尾的客户端比最初更新的时间更晚,但没有任何帮助:您只有很多硬件。

同样,每个客户端都分配给一个侦听器线程,该线程处理多个侦听器。客户端输入被转储到一个FIFO队列中,并在侦听器线程处理完成后立即处理。

然后,每个线程可以保持活动状态并存储在内存中,而客户端数据通过系统移动。设计优雅地降级,因为太多的客户端意味着所有更新都变慢,作为客户端数量的线性函数。你提出的设计会降低速度。

现代(例如比2002年晚)的web服务器将这一切深深隐藏在实现中,因此开发人员无需对其进行管理。但它仍然是一个有用的练习。

+0

这是有道理的。每个客户端想要线程的主要原因之一是,如果连接速度很慢,那么只有该线程/客户端会因此而受到影响。有一组更新线程将事件广播到所有客户端会遭受连接速度缓慢的影响,但我想这是无法解决的。 – afsantos

2

那么您需要继续使用ThreadPool,因为您需要管理Runners !,我建议您在业务中使用impl MOA结构,因为此客户端连接到服务器并且服务器等待客户端请求(数据),然后服务器将作业排队(如果没有可用的线程进行处理)并且直接响应指向服务器上的客户端进程ID的长值并关闭套接字。 现在如果客户请求已处理并准备好采取行动,该怎么办?所以这里有两种方法,好的一个是服务器向客户端发出信号(因此客户端需要监听关于服务器响应的[ServerSocket])关于完成的请求。 OR客户端会以一定的时间间隔检查服务器并检查进程的状态。

+0

*“服务器信号客户端”* - 你在这里提出的建议,基本上是角色的倒置,不是吗?让客户端监听服务器响应。我怀疑这些要求需要很长时间才能实现。每隔一段时间就不是三路握手而不是保持插座打开的成本? – afsantos

+0

保持连接打开比在端口上侦听更糟糕。当然是。当服务器在端口x上侦听客户端请求时,客户端在端口y上侦听响应。正如我所说,这只是一个建议。但请记住,如果客户端打开一个端口来获取响应,那么客户端应该可以访问服务器。 – 2013-10-02 19:43:57

1

上述设计完全没问题。这正是网络服务器在引入nio之前的工作原理。

你有多少客户?如果不是那么多,不要担心可扩展性,并尽量避免复杂的nio api。如果您确实需要扩展,请考虑使用某种抽象,例如netty。使用nio可能相当复杂,可能在不同的操作系统上工作的方式不同(一旦我遇到了可以在特定操作系统上复制的奇怪错误)。

使用NIO您可以处理所有的客户端请求的线程1-4

有时IO能胜过NIO /响应流程。

看到这个答案 - Old I/O thread per client model or NIO reactor pattern?

+0

链接的答案及其链接的文章都非常有见地。 *“上述设计是完全正确的”* - 使用此语句,您是指每个客户端线程模型,使用定期轮询还是'Runnable'方法,尽可能调度任务,以及(可能)限制线程 - 池大小? – afsantos

+0

如果您正在编写ping/pong应用程序,例如web服务器,其中响应将紧跟在请求后,一个线程与SO_TIMEOUT将正常工作。或者根本不需要超时。读取请求直到完成(请求长度可以在前几个字节中指定),发回响应。如果您需要同时向两个方向发送数据或从服务器推送通知 - 请为每个客户端提供2个线程。 – Anton

1

我设计并实施了一些生产水平的实时系统(谈论毫秒级的延迟或更低,但客户屈指可数)。恕我直言,你应该采取NIO方法。

NIO的核心基本上是select(),它允许您同时处理来自不同套接字/客户端的输入。之后,将事件放入适当的队列中并/或在整个系统中广播。那么如何处理队列和分配线程完全独​​立于IO任务,并且取决于您自己的调整。另外看看ZeroMQ,它基本上应用了相同的想法;通过多个Socket模型查看他们的Poller。我相信大多数现代消息传递框架,包括JMS/EMS,TibcoRV,29 West LBM等(现在在Informatica下)都采用类似的方法。

+0

对于一个带有4个线程的小系统,例如,这将通过'select'通过'select'来监听传入连接和客户端输入,然后有三个工作线程调度请求并向所有客户端广播,是吗?在广播时,如果客户端连接速度特别慢,是否不会以负面方式影响所有后续客户端,具体取决于客户端迭代的顺序? – afsantos

+0

当套接字准备好读取时,它不会导致选择器甚至会触发SelectionKey.OP_READ。 – Anton