简体   繁体   English

执行器服务RabbitMQ中只有一个线程并发运行

[英]Only one thread is running concurrently in executor service, RabbitMQ

I have create a connection with a specified thread pool with 20 cores.我已经创建了一个与具有 20 个内核的指定线程池的连接。

        ConnectionFactory factory = new ConnectionFactory();
        ....
        //specified es
        ExecutorService consumerExecutor = Executors.newFixedThreadPool(threadNum, threadFactory);
        con = factory.newConnection(consumerExecutor, addresses);

then create a channel from this connection:然后从此连接创建一个通道:

        final Channel channel = connection.createChannel();

and use this to create a DefaultConsumer.并使用它来创建一个 DefaultConsumer。

While I find that though the threads can be used to consume messages, always, only one thread is consuming messages even though messages are massive accumulated in servers.虽然我发现虽然线程可以用来消费消息,但总是只有一个线程在消费消息,即使消息在服务器中堆积如山。

I look into the source code and find:我查看源代码并发现:

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;
    }
}

The trick should be in ConsumerWorkService.this.workPool.nextWorkBlock , it poll the channel from the ready queue, and add to the read queue in the finish block after running the callback run() .诀窍应该在ConsumerWorkService.this.workPool.nextWorkBlock中,它从就绪队列中轮询通道,并在运行回调run()后添加到完成块中的读取队列。 Please correct me if I am wrong.如果我错了,请纠正我。

This is confusing since a consumer is bound to one channel, and the channel is not released to the queue until last task block is finished, which means the thread pool is always offering only one thread for that consumer.这是令人困惑的,因为消费者绑定到一个通道,并且直到最后一个任务块完成后通道才会被释放到队列中,这意味着线程池始终只为该消费者提供一个线程。

Questions:问题:

  1. Why RabbitMQ designs this model为什么 RabbitMQ 设计这个模型
  2. How do we optimize this issue我们如何优化这个问题
  3. Is it good to submit the task to a standalone thread pool in handleDelivery to consume messages as well as ack(to ensure message ack only after task finishes)将任务提交到handleDelivery中的独立线程池以消费消息和 ack 是否很好(以确保仅在任务完成后才确认消息)

> 1. Why RabbitMQ designs this model > 1. 为什么RabbitMQ设计这个模型

I would like to know the reason myself.我想自己知道原因。 But this fact is clearly reflected in their documentation :但是这个事实清楚地反映在他们的文档中:

Each Channel has its own dispatch thread.每个 Channel 都有自己的调度线程。 For the most common use case of one Consumer per Channel, this means Consumers do not hold up other Consumers.对于每个通道一个消费者的最常见用例,这意味着消费者不会阻止其他消费者。 If you have multiple Consumers per Channel be aware that a long-running Consumer may hold up dispatch of callbacks to other Consumers on that Channel.如果每个通道有多个消费者,请注意长时间运行的消费者可能会阻止向该通道上的其他消费者发送回调。

> 2. How do we optimize this issue > 2.我们如何优化这个问题

You can either have multiple channels or decouple message consumption from processing by submitting the actual work to another thread pool.您可以拥有多个通道,也可以通过将实际工作提交给另一个线程池来将消息消耗处理分离。 You can find more details in this article .您可以在本文中找到更多详细信息。

> 3. Is it good to submit the task to a standalone thread pool in handleDelivery to consume messages as well as ack(to ensure message ack only after task finishes) > 3. 把任务提交到handleDelivery中的独立线程池消费消息和ack是否好(确保任务完成后才确认消息)

Quote from the docs :引用文档

When manual acknowledgements are used, it is important to consider what thread does the acknowledgement.使用手动确认时,重要的是要考虑哪个线程执行确认。 If it's different from the thread that received the delivery (eg Consumer#handleDelivery delegated delivery handling to a different thread), acknowledging with the multiple parameter set to true is unsafe and will result in double-acknowledgements, and therefore a channel-level protocol exception that closes the channel.如果它与接收交付的线程不同(例如,Consumer#handleDelivery 将交付处理委托给不同的线程),将多个参数设置为 true 进行确认是不安全的,并且会导致双重确认,因此会导致通道级协议异常关闭通道。 Acknowledging a single message at a time can be safe.一次确认一条消息可能是安全的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM