我有一个客户端套接字列表,通常大小约为2000。这些客户端是动态的,它们来来往往。
我有一个ExecutorService
,带有一个固定的线程池,其中包含处理这些线程的32个线程。该执行程序服务负责解码并发送要发送到这2000个客户端的消息。
我想防止执行程序服务的两个(或多个)线程同时处理同一客户端。
一种方法可能是引入另一个簿记线程(因此我最终得到32 + 1个线程),该线程负责在对应于同一客户端的先前消息为ExecutorService.submit(mesage)
时调用完成。但是我不确定这是否会带来瓶颈,这意味着这个新引入的簿记线程无法跟上提交消息的时间。
理想情况下,我不希望预先将线程预先分配给一组客户端,因为消息负载在各个客户端之间分布不均。事先也不知道。
有哪些方法?它们是由java.util.concurrent
功能提供的吗?
更新
这是一个简短的摘要,因为评论指出存在一些误解:
我不希望每个客户端有一个线程,因为最终会有2000个线程。
理想情况下,我不想将线程预先分配给一组客户端,因为消息率不是在所有客户端之间平均分配的,而且事先也不知道。
必须保留消息顺序。
我认为线程A
等待线程B
并不好,因为B
已经在向同一客户端发送消息。换句话说,在任何时候,只有一个线程正在处理一个客户端。
答案 0 :(得分:1)
当线程(A)开始处理消息(#1)时,它需要向共享管理器对象注册客户端ID。对于每个注册客户,都有一个队列。
当另一个线程(B)开始为同一客户端处理消息(#2)时,注册将检测到线程A已经在处理,并将消息#2添加到客户端的队列中。然后,线程B将停止并处理下一条消息。
当线程A处理完消息#1后,它将尝试注销,但是由于消息#2处于队列中,线程A将开始处理该消息。之后,当它再次尝试注销时,没有排队的消息,线程将停止并处理下一条消息。
要由管理者代码正确地同步访问,因此第二条消息可以由线程B处理,也可以移交给线程A,而不会丢失。
以上逻辑确保线程B将不等待线程A,即没有空闲时间,并且尽快处理消息#2,即以最小的延迟,而不会为同一客户端处理两个消息。时间。
每个客户的消息顺序 被保留。在全球范围内,当然不会保留消息顺序,因为消息#2的处理会延迟。
请注意,每个线程只有一个队列,因此只有32个队列,并且只有“重复”消息是队列,因此所有队列通常都为空。
更新
示例:为了在此处进行标识,消息被命名为clientId.messageId
,其中messageId
是全局的。
消息按以下顺序提交给执行器(3个线程):
1.1, 2.2, 1.3, 2.4, 3.5, 1.6
线程A拾取1.1
并开始处理。
线程B拾取2.2
并开始处理。
线程C选取1.3
,将其添加到线程A的队列中,然后返回。
线程C选取2.4
,将其添加到线程B的队列中,然后返回。
线程C选取3.5
并开始处理。
线程A完成了消息1.1
,并开始处理1.3
。
线程C完成消息3.5
并返回。
线程C选取1.6
,将其添加到线程A的队列中,然后返回。
线程C现在处于空闲状态。
线程B完成了消息2.2
,并开始处理2.4
。
线程A完成了消息1.3
,并开始处理1.6
。
线程B用消息2.4
完成并返回。
线程B现在处于空闲状态。
线程A使用消息1.6
完成并返回。
线程A现在处于空闲状态。
答案 1 :(得分:0)
您要顺序处理每个客户端的消息,但同时又不想为每个客户端分配单独的线程。这是使用Actor model的确切用例。演员就像轻量级的线程。它们不像通常的线程那样强大,但是非常适合像您这样的可重复任务。
如果您发现java actor libraries
被Google发现太重了,则可以在我的Github存储库中使用most compact actor implementation,或者查看我的异步库df4j
中包含的扩展的actor实现。
答案 2 :(得分:-1)
使每个线程服务都有自己的队列。编号插座。将每个请求放入队列[socket num%线程数]。
这将确保按顺序和顺序处理来自特定套接字的请求。
不幸的是,您将无法通过这种方式实现负载平衡。
或者,使用并发哈希表存储正在提供的套接字。如果线程为当前正在处理的套接字请求提供服务,则只需将请求放回队列即可。