Java中的多线程服务器

Java中的多线程服务器,java,multithreading,concurrency,client-server,nio,Java,Multithreading,Concurrency,Client Server,Nio,服务器本质上是一个项目队列,而客户机充当这些项目的生产者和/或消费者 服务器必须: 侦听put/take请求并相应地处理它们-这通常不会花费太长时间,它包括: 解析短字符串 一个HashMap.get 获取锁 APriorityQueue.poll或PriorityQueue.offer 尽快将所有项目活动通知每个客户,以便每个客户都能实时查看正在发生的事情 最简单的设置方法是让一个线程accepting客户端,然后为每个客户端创建两个线程: 处理输入流,阻止等待请求的程序 另一个用于处

服务器本质上是一个项目队列,而客户机充当这些项目的生产者和/或消费者

服务器必须:

  • 侦听put/take请求并相应地处理它们-这通常不会花费太长时间,它包括:
  • 解析短字符串
  • 一个
    HashMap.get
  • 获取锁
  • A
    PriorityQueue.poll
    PriorityQueue.offer
  • 尽快将所有项目活动通知每个客户,以便每个客户都能实时查看正在发生的事情
最简单的设置方法是让一个线程
accept
ing客户端,然后为每个客户端创建两个线程:

  • 处理
    输入流
    ,阻止等待请求的程序
  • 另一个用于处理
    OutputStream
    ,它侦听队列上的事件,并将信息发送给客户端
当然,这是不可伸缩的,而且为每个客户机提供两个线程似乎是浪费

我还考虑使用一个线程,这将

  • 读取设置约1秒的套接字超时
    
  • 如果
    读取超时,或在处理请求后,继续向客户端发送每个新事件
    
  • 循环这两个动作
但是,对请求和事件进行轮询也是浪费

另一种方法是使用线程池,将上述两个操作分别放在各自的
Runnable
中。然后,这些可运行程序将在
执行器中彼此排队。
这似乎同样浪费,如果不是更多的话

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

上述设计是否适合此任务,或者我是否应该使用NIO解决此问题?


就数量而言,这更像是一个练习,而不是一个真正的系统,因此它不必处理数千个客户端,但理想情况下,它应该能够很好地执行和扩展。

每个客户端两个线程肯定是不可伸缩的

如果服务器上有M个内核,那么实际上没有比运行M个线程更好的了。任何更高的值都会使您遭受打击,并会减少每秒执行的操作数

更好的设计将可用的内核划分为U更新程序和L侦听器,其中 U+L==M

每个客户机都被分配给一个更新程序线程(理想情况下是负载平衡,但这是一种修饰)。每个更新事件都多播到所有更新程序线程,然后每个更新程序线程更新其分配的所有客户端。更新程序列表末尾的客户机更新要晚于开始时的客户机更新,但没有任何帮助:您只有这么多硬件

类似地,每个客户机都分配给一个侦听器线程,该线程处理多个侦听器。客户机输入被转储到FIFO队列中,侦听器线程一找到它就进行处理

然后,当客户端数据在系统中移动时,每个线程都可以保持活动状态并保存在内存中。这种设计会优雅地退化,因为太多的客户端意味着所有更新都会变慢,这是客户端数量的线性函数。你提出的设计将比这更快地退化


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

你需要继续使用线程池,因为你需要管理跑步者!,我建议您在业务中采用MOA结构,因为此客户端连接到服务器,服务器等待客户端请求(数据),然后服务器将作业排队(如果没有可处理的线程),并立即响应指向服务器上客户端进程id的长值,然后关闭套接字。
现在,如果客户端请求已处理并准备好执行操作,该怎么办?这里有两种方法,好的方法是服务器向客户机发出信号(因此客户机需要监听服务器响应[ServerSocket])关于完成的请求。或者客户端定期检查服务器并检查进程的状态。

上述设计完全正确。这正是在引入nio之前web服务器的工作方式

你有多少客户?如果不是很多,不要担心可伸缩性,尽量避免复杂的NIOAPI。如果你真的需要缩放,考虑使用某种抽象,例如NETY。使用nio可能相当复杂,在不同的操作系统上可能无法以相同的方式工作(一旦我遇到奇怪的bug,它只能在特定的操作系统上重现)

使用nio,您可以在1-4个线程中处理所有客户机的请求/响应流

有时IO可能会胜过NIO


请看这个答案-

我已经设计并实现了几个生产级实时系统(谈论毫秒级或更少的延迟,但只有少数客户端)。我认为你绝对应该采取NIO方法

NIO的核心基本上是select(),它允许您同时处理来自不同套接字/客户端的输入。之后,将事件放入适当的队列和/或在整个系统中广播。那么,如何处理队列和分配线程完全独立于IO任务,这取决于您自己的调整


还要看一看ZeroMQ,它本质上应用了相同的思想;看看他们的多插槽模型轮询器。我相信大多数现代消息传递框架,包括JMS/EMS、TibcoRV、29 West LBM等(现在在Informatica下)都采用了类似的方法。

这是有道理的。每个客户机都需要一个线程的主要原因之一是,如果连接速度慢,那么只有该线程/客户机会因此受到影响。如果有一组更新程序线程将事件广播给所有客户端,那么