Java 爪哇及;RabbitMQ-排队&;多线程或Couchbase作为作业队列
我有一个Java 爪哇及;RabbitMQ-排队&;多线程或Couchbase作为作业队列,java,multithreading,mongodb,rabbitmq,couchbase,Java,Multithreading,Mongodb,Rabbitmq,Couchbase,我有一个工作分发者,他在不同的频道上发布消息 此外,我希望有两个(将来还会有更多)的消费者,他们处理不同的任务,在不同的机器上运行。(目前我只有一个,需要扩展) 让我们命名这些任务(仅举示例): FIBONACCI(生成FIBONACCI数) RANDOMBOOKS(生成随机句子来写一本书) 这些任务最多运行2-3小时,应平均分配给每个消费者 每个使用者都可以有x并行线程来执行这些任务。 所以我说:(这些数字只是例子,将被变量所取代) 机器1可以为FIBONACCI消耗3个parallel
工作分发者
,他在不同的频道上发布消息
此外,我希望有两个(将来还会有更多)的消费者
,他们处理不同的任务,在不同的机器上运行。(目前我只有一个,需要扩展)
让我们命名这些任务(仅举示例):
(生成FIBONACCI数)FIBONACCI
(生成随机句子来写一本书)RANDOMBOOKS
消费者
每个使用者都可以有x
并行线程来执行这些任务。
所以我说:(这些数字只是例子,将被变量所取代)
- 机器1可以为
消耗3个parallel作业,为FIBONACCI
消耗5个parallel作业RANDOMBOOKS
- 机器2可以为
消耗7个parallel作业,为FIBONACCI
消耗3个parallel作业RANDOMBOOKS
频道启动x
线程才能在每个消费者上收听
我什么时候才能确认
我目前只对一个使用者
使用的方法是:为每个任务启动x个
线程-每个线程都是一个默认使用者,实现可运行
。在handleDelivery
方法中,我调用basicAck(deliveryTag,false)
,然后执行工作
进一步:我想向特殊消费者发送一些任务。我如何结合上述公平分配实现这一目标
这是我的发布代码
String QUEUE_NAME = "FIBONACCI";
Channel channel = this.clientManager.getRabbitMQConnection().createChannel();
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
channel.basicPublish("", QUEUE_NAME,
MessageProperties.BASIC,
Control.getBytes(this.getArgument()));
channel.close();
这是我的消费者代码
public final class Worker extends DefaultConsumer implements Runnable {
@Override
public void run() {
try {
this.getChannel().queueDeclare(this.jobType.toString(), true, false, false, null);
this.getChannel().basicConsume(this.jobType.toString(), this);
this.getChannel().basicQos(1);
} catch (IOException e) {
// catch something
}
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Control.getLogger().error("Exception!", e);
}
}
}
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] bytes) throws IOException {
String routingKey = envelope.getRoutingKey();
String contentType = properties.getContentType();
this.getChannel().basicAck(deliveryTag, false); // Is this right?
// Start new Thread for this task with my own ExecutorService
}
}
在这种情况下,类Worker
启动两次:一次用于fibuncai
,一次用于RANDOMBOOKS
更新
正如答案所述,RabbitMQ并不是解决这一问题的最佳解决方案,但Couchbase或MongoDB pull方法将是最好的解决方案。我是这些系统的新手,是否有人可以向我解释,这将如何实现?如果您正在使用spring或愿意使用spring,那么您可以使用spring侦听器容器支持来实现它。这将为您提供一种您正在寻找的类似回调类型的编程模型
来自
首先让我说,我没有使用Java与RabbitMQ通信,因此我无法提供代码示例。但这不应该是个问题,因为这不是你要问的。这个问题更多的是关于应用程序的一般设计
让我们把它分解一下,因为这里有很多问题
将任务分配给不同的消费者
一种方法是使用循环制,但这相当粗糙,没有考虑到不同的任务可能需要不同的时间来完成。那该怎么办呢。一种方法是将预取设置为1
。预取意味着消费者在本地缓存消息(注意:消息尚未被消费)。通过将此值设置为1,将不会发生预取。这意味着您的消费者将只知道并且只在内存中存储当前正在处理的消息。这使得只有在工作线程空闲时才可以接收消息
何时承认
通过上面描述的设置,可以从队列中读取消息,将其传递给一个线程,然后确认消息。对所有可用线程执行此操作-1。您不想确认最后一条消息,因为这意味着您将打开接收另一条消息的窗口,而您还无法将该消息传递给您的某个员工。当其中一个线程完成时,就是您确认该消息的时候,这样您将始终让您的线程处理某些内容
传递特别信息
这取决于你不想做什么,但总的来说,我认为你的制作人应该知道他们在传递什么。这意味着您可以将其发送到某个特定的交换,或者更确切地说,使用某个路由密钥,该密钥将此消息传递到适当的队列,该队列将有一个侦听它的消费者知道如何处理该消息
我建议您仔细阅读AMQP和RabbitMQ,这可能是一个好方法
警告
在我的提案和您的设计中有一个主要缺陷,那就是我们在实际处理消息之前就确认了它。这意味着当(不是如果)我们的应用程序崩溃时,我们无法重新创建ACKed
消息。如果您事先知道要启动多少线程,就可以解决这个问题。我不知道是否可以动态更改预取计数,但不知何故我对此表示怀疑
一些想法
根据我使用RabbitMQ的经验(尽管有限),您不应该害怕创建交换和队列,如果操作正确,这些可以极大地改进和简化您的应用程序设计。也许你不应该让一个应用程序启动一堆消费者线程。相反,您可能希望使用某种包装器,根据系统中的可用内存或类似内容启动使用者。如果您这样做,您可以确保在应用程序崩溃时不会丢失任何消息,因为如果您这样做,您当然会在处理完消息后确认消息
推荐阅读
如果有什么不清楚的地方或者我没有抓住你的重点,请告诉我,如果可以的话,我会尝试扩大我的答案或改进它。以下是我对你问题的看法。正如@Daniel在他的回答中提到的,我认为这更像是一个问题
@Configuration
public class ExampleAmqpConfiguration {
@Bean
public MessageListenerContainer messageListenerContainer() {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(rabbitConnectionFactory());
container.setQueueName("some.queue");
container.setMessageListener(exampleListener());
return container;
}
@Bean
public ConnectionFactory rabbitConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
return connectionFactory;
}
@Bean
public MessageListener exampleListener() {
return new MessageListener() {
public void onMessage(Message message) {
System.out.println("received: " + message);
}
};
}
}