前几篇我们介绍了如果通过RabbitMQ发布一个简单的消息,再到工作队列,多个消费者进行消费,最后再到工作队列的分发与消息的应答机制(ACK);
之前我们分享的这几种模式,都是被消费之后就从队列中被删除了,理想状态下不会被重复消费,试想我们另外一种场景,比如我之前做的小说业务,用户在登录成功后,需要将临时账户的金币和书架的书籍信息同步到正式账户。
如果我们跟登录融合在一块,登录成功之后,如果用户账户或者书架同步失败,那么势必影响我们整个登录的体验。为了更好地做到用户无感知,不需要用户做更多的操作,那么我们就使用消息队列的方式,来进行异步同步。
发布订阅模式
这就是我们一个用户数据同步的流程图,也是RabbitMQ发布订阅的流程图,大家可能注意到了中间怎么多了一个交换机。
这里要注意,使用发布订阅模式,这里必须将交换机与队列进行绑定,如果不绑定,直接发送消息,这个消息是不会发送到任何队列的,更不会被消费。
交换机种类
交换机总共分四种类型:分别是direct、topic、headers、fanout。这次我们主要讲fanout,因为这是我们本次需要用到的交换机类型。
fanout顾名思义就是广播模式。它会把消息推送给所有订阅它的队列。
代码
生产者
public class Send {
/**
* 交换机名称
*/
private final static String EXCHANGE_NAME = "test_exchange_fanout";
public static void main(String[] args) throws IOException, TimeoutException {
// 获取连接
Connection connection = MQConnectUtil.getConnection();
// 创建通道
Channel channel = connection.createChannel();
// 声明交换机 fanout:分发模式,分裂
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
// 消息内容
String msg = "我是一个登录成功的消息";
// 发送消息
channel.basicPublish(EXCHANGE_NAME, "", null, msg.getBytes());
System.out.println("消息发送成功:" + msg);
channel.close();
connection.close();
}
}
消费者-同步账户
public class Consumer1 {
/**
* 交换机名称
*/
private final static String EXCHANGE_NAME = "test_exchange_fanout";
private final static String QUEUE_NAME = "test_topic_publish_account";
public static void main(String[] args) throws IOException, TimeoutException {
// 获取连接
Connection connection = MQConnectUtil.getConnection();
// 创建通道
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 将队列绑定到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
// 保证一次只接收一个消息,保证rabbitMQ每次将消息发送给闲置的消费者
channel.basicQos(1);
DefaultConsumer consumer = new DefaultConsumer(channel) {
@SneakyThrows
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
throws IOException {
String msg = new String(body, StandardCharsets.UTF_8);
System.out.println("同步账户[1]:" + msg);
Thread.sleep(1000);
// 手动应答
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
// 监听队列
channel.basicConsume(QUEUE_NAME, false, consumer);
}
}
消费者-同步书架
public class Consumer2 {
/**
* 交换机名称
*/
private final static String EXCHANGE_NAME = "test_exchange_fanout";
private final static String QUEUE_NAME = "test_topic_publish_book_case";
public static void main(String[] args) throws IOException, TimeoutException {
// 获取连接
Connection connection = MQConnectUtil.getConnection();
// 创建通道
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 将队列绑定到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
// 保证一次只接收一个消息,保证rabbitMQ每次将消息发送给闲置的消费者
channel.basicQos(1);
DefaultConsumer consumer = new DefaultConsumer(channel) {
@SneakyThrows
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
throws IOException {
String msg = new String(body, StandardCharsets.UTF_8);
System.out.println("同步书架[2]:" + msg);
Thread.sleep(1000);
// 手动应答
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
// 监听队列
channel.basicConsume(QUEUE_NAME, false, consumer);
}
}
总结
那么基于这样的需要同步用户数据的需求,那么为了保证各数据同步之间互不影响,降低耦合性,那么我们就可以使用多个队列,进行用户数据的同步。提升整个系统的高可用。
<p style="text-align:center;font-weight:bold;color:#0e88eb;font-size:20px">日拱一卒,功不唐捐</p>
<p style="text-align:center;font-weight:bold;color:#773098;font-size:16px">更多内容请关注:</p>
来源:oschina
链接:https://my.oschina.net/u/3178270/blog/4283636