rabbitMq默认是公平分发的,即轮询分发,有多少个消费者,多少个消费者平均消费消费。具体例子请看我之前写的文章《rabbitMq消息应答–ack机制》。但是在工作中,我们希望消费速度快的机器就消费更多的消息。这时候我们可以在消费者设置channel.basicQos(1);不公平分发,来让消费速度快的消费者消费更多的消息。意思就是如果这个任务我还没有处理完或者我还没有应答你,你先别分配给我,我目前只能处理一个任务,然后 rabbitmq 就会把该任务分配给没有那么忙的那个空闲消费者。
不公平分发的缺点:如果所有的消费者都没有完成手上任务,队列还在不停的添加新任务,队列有可能就会遇到队列被撑满的情况,这个时候就只能添加新的 worker 或者改变其他存储任务的策略。
具体例子如下:
消费者一
public class WorkAck {
public static final String QUEUE_NAME = "ack_queue";
/**
* 消息手动应答代码示例
* @param args
* @throws IOException
* @throws TimeoutException
*/
public static void main(String[] args) throws IOException, TimeoutException {
Channel channel = RabbitUtils.getChannel();
System.out.println("线程1较快,在等待消息:");
DeliverCallback deliverCallback = new DeliverCallback() {
@Override
public void handle(String consumerTag, Delivery message) throws IOException {
System.out.println(Thread.currentThread().getName() + "接收到了消息:" + new String(message.getBody()));
// 消息手动应答
channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
}
};
CancelCallback cancelCallback = new CancelCallback() {
@Override
public void handle(String consumerTag) throws IOException {
System.out.println(Thread.currentThread().getName()+"取消发送了");
}
};
System.out.println(Thread.currentThread().getId() + "消费者正在等待消息----");
/**
* 第一个参数是队列名称
* 第二个参数表示是否自动应答,如果是true,则无需手动应答,如果是false,则需要手动应答;
* 手动应答是消费者处理完本条消息,然后会向mq服务器进行消息确认,允许mq服务器在队列中把消息删除;一旦消费者没有向mq服务器进行确认,即使消费者断开后;
* mq服务器也会把消息重新入到mq队列,给其他的消费者消费。
* 生产上一般都开启手动应答;自动应答存在消费者还没处理完消息时,队列清空了消息,导致消息无法重新入队,有消息丢失的风险;
*/
// 设置不公平分发,默认是0,0表示公平分发。
channel.basicQos(1);
channel.basicConsume(QUEUE_NAME, false, deliverCallback, cancelCallback);
}
}
消费者二:
public class WorkAck2 {
public static final String QUEUE_NAME = "ack_queue";
/**
* 消息手动应答代码示例
* @param args
* @throws IOException
* @throws TimeoutException
*/
public static void main(String[] args) throws IOException, TimeoutException {
Channel channel = RabbitUtils.getChannel();
System.out.println("线程2非常慢,在等待消息:");
DeliverCallback deliverCallback = new DeliverCallback() {
@Override
public void handle(String consumerTag, Delivery message) throws IOException {
System.out.println(Thread.currentThread().getName() + "接收到了消息:" + new String(message.getBody()));
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "在处理消息:" + new String(message.getBody()));
// 消息手动应答
channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
}
};
CancelCallback cancelCallback = new CancelCallback() {
@Override
public void handle(String consumerTag) throws IOException {
System.out.println(Thread.currentThread().getName()+"取消发送了");
}
};
System.out.println(Thread.currentThread().getId() + "消费者正在等待消息----");
// 设置不公平分发,默认是0,0表示公平分发。
channel.basicQos(1);
/**
* 第一个参数是队列名称
* 第二个参数表示是否自动应答,如果是true,则无需手动应答,如果是false,则需要手动应答;
* 手动应答是消费者处理完本条消息,然后会向mq服务器进行消息确认,允许mq服务器在队列中把消息删除;一旦消费者没有向mq服务器进行确认,即使消费者断开后;
* mq服务器也会把消息重新入到mq队列,给其他的消费者消费。
* 生产上一般都开启手动应答;自动应答存在消费者还没处理完消息时,队列清空了消息,导致消息无法重新入队,有消息丢失的风险;
*/
channel.basicConsume(QUEUE_NAME, false, deliverCallback, cancelCallback);
}
}
测试结果:
Connected to the target VM, address: '127.0.0.1:8969', transport: 'socket'
线程1较快,在等待消息:
1消费者正在等待消息----
pool-1-thread-4接收到了消息:AA
pool-1-thread-5接收到了消息:CC
pool-1-thread-6接收到了消息:DD
线程2非常慢,在等待消息:
1消费者正在等待消息----
pool-1-thread-4接收到了消息:BB
pool-1-thread-4在处理消息:BB
从测试结果来看,消费者一消费的速度比较快能够消费3条消息,消费者二消费速度比较慢,只能消费1条消息。