rocketmq--特性之顺序消息

应用场景:
    网购的时候,我们需要下单,那么下单需要假如有三个顺序,第一、创建订单 ,第二:订单付款,第三:订单完成。也就是这个三个环节要有顺序,这个订单才有意义。RocketMQ可以保证顺序消费
 
实现原理:
    produce在发送消息的时候,把消息发到同一个队列(queue)中。发送消息的时候可以实现MessageQueueSelector类的select 方法,返回的就是queue。
    消费者注册消息监听器为MessageListenerOrderly,这个监听器是让消费者独占的,这样就可以保证消费端只有一个线程去消费消息。

代码:

public static void main(String[] args) throws Exception {
	//Instantiate with a producer group name.
	DefaultMQProducer producer = new DefaultMQProducer(Configure.BROKER_GROUP);
	producer.setNamesrvAddr(Configure.NAMESRV_ADDR);
	//Launch the instance.
	producer.start();

	String[] tags = new String[] {Configure.MESSAGE_TAG_ORDER_1,Configure.MESSAGE_TAG_ORDER_2};

	for (int i = 0; i < 100; i++) {
		int orderId = i % 10;

		log.info("orderId{}",orderId);

		//创建消息
		Message msg = new Message(Configure.MESSAGE_TOPIC, tags[i % tags.length], "KEY" + i,
				("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));

		//发送消息,重写选择MessageQueue 方法,把消息写到对应的ConsumerQueue 中
		// orderId 参数传递到内部方法 select arg 参数
		SendResult sendResult = producer.send(msg, new MessageQueueSelector() {

			@Override
			public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
			   log.info("arg{}",arg);
				Integer id = (Integer) arg;
				int index = id % mqs.size();
				//返回选中的队列
				return mqs.get(index);
			}
		}, orderId);

		System.out.printf("%s%n", sendResult);
	}
	//server shutdown
	producer.shutdown();
}

消费者:

public static void main(String[] args) throws Exception{
	DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(Configure.CONSUMER_GROUP);
	consumer.setNamesrvAddr(Configure.NAMESRV_ADDR);

	consumer.subscribe(Configure.MESSAGE_TOPIC, Configure.MESSAGE_TAG_ORDER_1+" || "+Configure.MESSAGE_TAG_ORDER_2);

	consumer.registerMessageListener(new MessageListenerOrderly() {
		@Override
		public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs,
												   ConsumeOrderlyContext context) {
			//手动确认
			context.setAutoCommit(false);
			msgs.forEach(m->{
				System.out.print("host:"+m.getBornHost()+"--");
				System.out.print("key:"+m.getKeys()+"--");
				System.out.print("Topic:"+m.getTopic()+"--");
				System.out.print("QueueId:"+m.getQueueId()+"--");
				System.out.print("tags:"+m.getTags()+"--");
				System.out.print("msg:"+new String(m.getBody()));
				System.out.println();
			});
			return ConsumeOrderlyStatus.SUCCESS;

		}
	});

	consumer.start();

	log.info("Consumer Started.%n");
}

注:代码复制多份,启用使用多个消费者(容易看日志)。

测试结果(启用了三个消费者):

消费者1,消费日志

消费者2,消费日志

消费者3,消费日志

测试总结:

消费者1,消费了queue0,和queue1,通过msg tag 和queue id,可以确定在一个队列上的msg tag,消息是被顺序消费的。

消费者2和消费者3分别消费queue3和queue2 上的消息,很容易看出来是顺序消费的。也可以把消息只发送到一个队列,进行测试。

实现:

    1、发送消息时把业务相关的消息发送到同一个consumer queue

    2、消费消息时使用 MessageListenerOrderly,消费消息时要获取consumer queue的锁后才能消费消息(消费消息内部使用的是线程池来消费的)。

猜你喜欢

转载自blog.csdn.net/convict_eva/article/details/82627225