rocketMQ(六) rocketMQ顺序投递并消费-附demo
目标:电商订单生产并消费
1.准备知识一览
1.1 MessageQueueSelector了解
生产消息使用MessageQueueSelector投递到Topic下指定的queue
producer.send(message, new MessageQueueSelector(){
select(List<MessageQueue> mqs, Message msg, Object arg){
Integer queueNum = (Integer)arg;
return mqs.get(queueNum);
}
},0)
-
应用场景:顺序消息,分摊负载
-
默认topic下的queue数量是4,可以配置
-
支持同步,异步发送指定的MessageQueue(不建议使用异步)。
-
选择的queue数量必须小于配置的,否则会出错
1.2 顺序消息介绍
1.2.1什么是顺序消息:消息的生产和消费顺序一致
-
全局顺序:topic下面全部消息都要有序(少用)
(1)性能要求不高,所有的消息严格按照 FIFO 原则进行消息发布和消费的场景,并行度成为消息系统的瓶颈, 吞吐量不够.
(2)在证券处理中,以人民币兑换美元为例子,在价格相同的情况下,先出价者优先处理,则可以通过全局顺序的方式按照 FIFO 的方式进行发布和消费
-
局部顺序:只要保证一组消息被顺序消费即可
(1)性能要求高(2)电商的订单创建,同一个订单相关的创建订单消息、订单支付消息、订单退款消息、订单物流消息、订单交易成功消息 都会按照先后顺序来发布和消费
(阿里巴巴集团内部电商系统均使用局部顺序消息,既保证业务的顺序,同时又能保证业务的高性能)
1.2.2 顺序发布:对于指定的一个 Topic,客户端将按照一定的先后顺序发送消息
1.2.3 顺序消费:对于指定的一个 Topic,按照一定的先后顺序接收消息,即先发送的消息一定会先被客户端接收到。
注意: 1.顺序消息暂不支持广播模式 2.顺序消息不支持异步发送方式,否则将无法严格保证顺序
1.3 生产端发送消息有序
生产端保证发送消息有序,且发送到同一个Topic的同个queue里面,RocketMQ的确是能保证FIFO的,以订单orderId为例
Message msg = new Message("orderServer", "", order.getOrderId(),
order.toString().getBytes());
SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
// arg 即从外部传递进来的 order.getOrderId()
// 对orderId 进行取余 计算, 保证同一订单的信息 发送到同一个 topic 下的同一个queue 里面,保证生产有序
Integer id = (Integer) arg;
int index = id % mqs.size();
return mqs.get(index);
}
}, order.getOrderId());
1.4 消费端消费有序
MessageListenerConcurrently:并发消费
MessageListenerOrderly:
-
Consumer会平均分配queue的数量
-
并不是简单禁止并发处理,而是为每个Consumer Quene加个锁,消费每个
消息前,需要获得这个消息所在的Queue的锁,这样同个时间,同个Queue的
消息不被并发消费,但是不同Queue的消息可以并发处理
代码
/**
* 顺序消息消费
*/
public class Consumer {
public static void main(String[] args) throws MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("orderServer");
consumer.setNamesrvAddr("192.168.159.128:9876;192.168.159.129:9876");
/**
* 设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费<br>
* 如果非第一次启动,那么按照上次消费的位置继续消费
*/
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
consumer.subscribe("orderServer", "TagA || TagC || TagD");
consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
MessageExt message = msgs.get(0);
try {
//模拟业务逻辑处理中...
return ConsumeOrderlyStatus.SUCCESS;
} catch (Exception e) {
e.printStackTrace();
//补偿机制
return ConsumeOrderlyStatus.SYSPEND_CURRENT_QUEUE_A_MOMENT;
}
}
});
consumer.start();
System.out.println("Consumer Started.");
}
}