在执行器服务RabbitMQ中只有一个线程同时运行

时间:2017-08-01 03:44:30

标签: java multithreading rabbitmq

我已经创建了一个具有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()后添加到完成块中的读取队列。如果我错了,请纠正我。

这是令人困惑的,因为使用者绑定到一个通道,并且在最后一个任务块完成之前,通道不会释放到队列,这意味着线程池始终只为该使用者提供一个线程。

问题:

  1. 为什么RabbitMQ设计此模型
  2. 我们如何优化此问题
  3. 将任务提交到handleDelivery中的独立线程池以使用消息和ack(仅在任务完成后确保消息确认)是否合适

1 个答案:

答案 0 :(得分:0)

&GT; 1.为什么RabbitMQ设计这个模型

我想知道自己的原因。但这一事实清楚地反映在他们的documentation

  

每个频道都有自己的调度线程。对于最常见的用例   每个渠道一个消费者,这意味着消费者不会阻止其他消费者   消费者。如果每个频道有多个消费者,请注意a   长期运行的消费者可能会阻止向其他人发送回调   该频道的消费者。

&GT; 2.我们如何优化此问题

通过将实际工作提交到另一个线程池,您可以拥有多个渠道或从处理中解除消息消费。您可以在this article中找到更多详细信息。

&GT; 3.将任务提交到handleDelivery中的独立线程池以使用消息和ack(仅在任务完成后确保消息确认)是否合适

来自docs

的引用
  

使用手动确认时,重要的是要考虑   什么线程确认。如果它不同于   收到交付的线程(例如Consumer#handleDelivery   委托交付处理到另一个线程),承认   多个参数设置为true是不安全的,将导致   双重确认,因此是一个通道级协议   关闭频道的例外。确认单个消息   时间可以安全。