我有一个生产者 - 消费者场景,生产者的生产速度比消费者消费的快得多。通常,解决方案是使生产者阻止,因为生产者/消费者场景的运行速度与最慢的组件一样快。限制或阻止生产者不是一个很好的解决方案,因为我们的应用程序为消费者提供了足够的时间来赶上。
这里的图表描绘了一个完整的阶段"在我们的应用程序中,与更常见的情况相比:
Our Application Common Scenario
2N +--------+--------+
|PPPPPPPP|oooooooo| P = Producer
|PPPPPPPP|oooooooo| C = Consumer
N +--------+--------+ N +--------+--------+--------+ o = Other Work
|CPCPCPCP|CCCCCCCC| |CPCPCPCP|CPCPCPCP|oooooooo| N = number of tasks
|CPCPCPCP|CCCCCCCC| |CPCPCPCP|CPCPCPCP|oooooooo|
------------------- ----------------------------
0 T/2 T 0 T/2 T 3T/2
这个想法是通过不抑制生产者来最大化吞吐量。
我们的任务运行的数据很容易被序列化,因此我计划实施一个文件系统解决方案,以便溢出所有无法立即满足的任务。
我使用Java ThreadPoolExecutor
和BlockingQueue
最大容量,以确保我们不会耗尽内存。问题在于实施这样一个"分层"队列,其中可以在内存中排队的任务立即完成,否则数据将在磁盘上排队。
我提出了两种可能的解决方案:
BlockingQueue
或LinkedBlockingQueue
实施作为参考,从头开始实施ArrayBlockingQueue
。这可能就像在标准库中复制实现并添加文件系统读/写一样简单。BlockingQueue
实现,实现单独的FilesystemQueue
来存储我的数据,并使用一个或多个线程将文件出列,创建Runnable
并使用{将其排队{1}}。这些是否合理,是否有更好的方法?
答案 0 :(得分:4)
在寻求更复杂的解决方案之前,您是否真的相信使用有界BlockingQueue
对您来说是一个交易破坏者?事实证明,增加堆大小并预先分配足够的容量仍然可以。它可以让您避免复杂性和性能不确定性,因为GC暂停的价格在您的舒适区内。
尽管如此,如果你的工作负载不平衡,它可以利用持久化大量内存不适合的消息(与经过验证的MPMC阻塞队列相比),听起来你需要一个更简单,更小版本的{{ 3}}或其ActiveMQ off-shoot。根据您的应用程序,您可能会发现ActiveMQ的其他功能非常有用,在这种情况下您可以直接使用它。如果没有,你可能会更好地搜索JMS空间,正如 bowmore 所暗示的那样。
答案 1 :(得分:2)
这听起来像是使用JMS队列而不是文件系统的理想情况。
不是使用阻塞队列,而是在永久JMS队列上发布消息。您仍然可以尝试分层方法,将JMS队列与BlockingQueue
并行组合,在BlockingQueue已满时发布到JMS队列,但我确信纯JMS方法本身可以正常工作。
答案 2 :(得分:2)
第一个选项是增加可用的堆空间大小, Dimitar建议的 Dimitrov ,使用记忆标志-Xmx
,例如java -Xmx2048m
来自Oracle's Documentation:请注意,JVM使用的内存多于堆。例如 Java方法,线程堆栈和本机句柄在内存中分配 与堆分开,以及JVM内部数据结构。
这里还有一个java 堆内存如何分类的图表。
第二个选项是使用实现所请求功能的库。为此,您可以使用ashes-queue
来自项目概述:这是Java中的简单FIFO实现 持续支持。也就是说,如果队列已满,那么 溢出的消息将被保留,并在有可用时 插槽,它们将被放回内存中。
第三个选项是创建您自己的实现。就此而言,您可以预览this thread,以指导您实现此目的。
您的建议包含在最后一个第三个选项中。两者都是合理的。从实现的角度来看,您应该使用第一个选项,因为它将保证更容易实现和清洁设计。