我已经创建了一个具有20个内核的指定线程池的连接。
ConnectionFactory factory = new ConnectionFactory();
....
//specified es
ExecutorService consumerExecutor = Executors.newFixedThreadPool(threadNum, threadFactory);
con = factory.newConnection(consumerExecutor, addresses);
然后从此连接创建一个频道:
final Channel channel = connection.createChannel();
并使用它来创建DefaultConsumer。
虽然我发现虽然线程可以用来消费消息,但总是只有一个线程消耗消息,即使消息在服务器中大量累积。
我查看源代码并找到:
private final class WorkPoolRunnable implements Runnable {
@Override
public void run() {
int size = MAX_RUNNABLE_BLOCK_SIZE;
List<Runnable> block = new ArrayList<Runnable>(size);
try {
Channel key = ConsumerWorkService.this.workPool.nextWorkBlock(block, size);
if (key == null) return; // nothing ready to run
try {
for (Runnable runnable : block) {
runnable.run();
}
} finally {
if (ConsumerWorkService.this.workPool.finishWorkBlock(key)) {
ConsumerWorkService.this.executor.execute(new WorkPoolRunnable());
}
}
} catch (RuntimeException e) {
Thread.currentThread().interrupt();
}
}
}
/* Basic work selector and state transition step */
private K readyToInProgress() {
K key = this.ready.poll();
if (key != null) {
this.inProgress.add(key);
}
return key;
}
/**
* Return the next <i>ready</i> client,
* and transfer a collection of that client's items to process.
* Mark client <i>in progress</i>.
* If there is no <i>ready</i> client, return <code><b>null</b></code>.
* @param to collection object in which to transfer items
* @param size max number of items to transfer
* @return key of client to whom items belong, or <code><b>null</b></code> if there is none.
*/
public K nextWorkBlock(Collection<W> to, int size) {
synchronized (this) {
K nextKey = readyToInProgress();
if (nextKey != null) {
VariableLinkedBlockingQueue<W> queue = this.pool.get(nextKey);
drainTo(queue, to, size);
}
return nextKey;
}
}
技巧应该在ConsumerWorkService.this.workPool.nextWorkBlock
中,它从就绪队列轮询通道,并在运行回调run()
后添加到完成块中的读取队列。如果我错了,请纠正我。
这是令人困惑的,因为使用者绑定到一个通道,并且在最后一个任务块完成之前,通道不会释放到队列,这意味着线程池始终只为该使用者提供一个线程。
问题:
handleDelivery
中的独立线程池以使用消息和ack(仅在任务完成后确保消息确认)是否合适答案 0 :(得分:0)
&GT; 1.为什么RabbitMQ设计这个模型
我想知道自己的原因。但这一事实清楚地反映在他们的documentation:
中每个频道都有自己的调度线程。对于最常见的用例 每个渠道一个消费者,这意味着消费者不会阻止其他消费者 消费者。如果每个频道有多个消费者,请注意a 长期运行的消费者可能会阻止向其他人发送回调 该频道的消费者。
&GT; 2.我们如何优化此问题
通过将实际工作提交到另一个线程池,您可以拥有多个渠道或从处理中解除消息消费。您可以在this article中找到更多详细信息。
&GT; 3.将任务提交到handleDelivery中的独立线程池以使用消息和ack(仅在任务完成后确保消息确认)是否合适
来自docs:
的引用使用手动确认时,重要的是要考虑 什么线程确认。如果它不同于 收到交付的线程(例如Consumer#handleDelivery 委托交付处理到另一个线程),承认 多个参数设置为true是不安全的,将导致 双重确认,因此是一个通道级协议 关闭频道的例外。确认单个消息 时间可以安全。