使用1个生产者生成消息,2个消费者来消费消息。保证一个消息只能被一个消费者获取
消费1:
package com.sky.study.work;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.QueueingConsumer.Delivery;
import com.sky.study.ConnectionUtil;
public class Recv {
private final static String QUEUE_NAME = "test_queue_work";
public static void main(String[] args) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//同一时刻服务器只会发一条消息给消费者
channel.basicQos(1);
//定义队列的消费者
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
//监听队列,false表示手动返回完成状态,true表示自动
channel.basicConsume(QUEUE_NAME, false, queueingConsumer);
//获取消息
while(true) {
Delivery nextDelivery = queueingConsumer.nextDelivery();
String massage = new String(nextDelivery.getBody());
System.out.println(massage);
//休眠
Thread.sleep(10);
// 返回确认状态,注释掉表示使用自动确认模式
channel.basicAck(nextDelivery.getEnvelope().getDeliveryTag(), false);
}
}
}
消费2:
package com.sky.study.work;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.QueueingConsumer.Delivery;
import com.sky.study.ConnectionUtil;
public class Recv2 {
private final static String QUEUE_NAME = "test_queue_work";
public static void main(String[] args) throws Exception {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
channel.basicQos(1);
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(QUEUE_NAME, false, consumer);
while (true) {
Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(message);
// 休眠1秒
Thread.sleep(1000);
//下面这行注释掉表示使用自动确认模式
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
生产者
package com.sky.study.work;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.sky.study.ConnectionUtil;
public class Send {
private final static String QUEUE_NAME = "test_queue_work";
public static void main(String[] args) throws Exception {
//获取连接
Connection connection = ConnectionUtil.getConnection();
//创建通道
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//发送消息
for (int i = 0; i < 100; i++) {
// 消息内容
String message = "" + i;
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
Thread.sleep(i * 10);
}
channel.close();
connection.close();
}
}
工具类
package com.sky.study;
import java.io.IOException;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class ConnectionUtil {
public static Connection getConnection() throws IOException {
ConnectionFactory factory = new ConnectionFactory();
factory.setVirtualHost("testHello");
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setUsername("sky");
factory.setPassword("123456");
Connection connection = factory.newConnection();
return connection;
}
}
- 轮询分发 :使用任务队列的优点之一就是可以轻易的并行工作。如果我们积压了好多工作,我们可以通过增加工作者(消费者)来解决这一问题,使得系统的伸缩性更加容易。在默认情况下,RabbitMQ将逐个发送消息到在序列中的下一个消费者(而不考虑每个任务的时长等等,且是提前一次性分配,并非一个一个分配)。平均每个消费者获得相同数量的消息。这种方式分发消息机制称为Round-Robin(轮询)。
- 公平分发 :虽然上面的分配法方式也还行,但是有个问题就是:比如:现在有2个消费者,所有的奇数的消息都是繁忙的,而偶数则是轻松的。按照轮询的方式,奇数的任务交给了第一个消费者,所以一直在忙个不停。偶数的任务交给另一个消费者,则立即完成任务,然后闲得不行。而RabbitMQ则是不了解这些的。这是因为当消息进入队列,RabbitMQ就会分派消息。它不看消费者为应答的数目,只是盲目的将消息发给轮询指定的消费者
为了保证公平分发,我们使用basicQos( prefetchCount = 1)方法,来限制RabbitMQ只发不超过1条的消息给同一个消费者。当消息处理完毕后,有了反馈,才会进行第二次发送。
还有一点需要注意,使用公平分发,必须关闭自动应答,改为手动应答。