1 rocketMQ producer,consumer基本特征

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xhpscdx/article/details/77853031

1 rocketMQ producer,consumer基本特征

1. 介绍

  1. rocketMQ 有同步,异步,sendOneway发送消息机制。
  2. RocketMQ支持消费失败定时重试,每次重试间隔时间顺延。
  3. RocketMQ支持定时延迟发送机制。
  4. RocketMQ支持有序消息,及push,poll的不同消费机制。

2. 延迟特征

  1. RocketMQ 支持定时消息,但是不支持任意时间精度,仅支持系统默认间隔时间特定的 level,例如定时 5s, 10s, 1m 等。
  2. 其中,level=0 级表示不延时,level=1 表示1级延时 5s延迟 ,level=2 表示 2 级延时 10s延迟,以此类推
  3. 如果要支持任意的时间精度,在Broker层面,必须要做消息排序,如果再涉及到持久化,那么消息排序要不可避免的产生巨大性能开销。

3. 延迟配置

在服务器端(rocketmq-broker端)的属性配置文件中加入以下行:
messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
描述了各级别与延时时间的对应映射关系。


1. 这个配置项配置了从1级开始,各级延时的时间,可以修改这个指定级别的延时时间; 
2. 时间单位支持:s、m、h、d,分别表示秒、分、时、天; 
3. 默认值就是上面声明的,可手工调整; 
4. 默认值已够用,不建议修改这个值。

4. producer 发送延迟有序的消费


 /**
     * 有序消息
     */
    @Test
    public void test_sync_producer_order() {
        try {
            DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
            producer.setNamesrvAddr(NAMES_SERVER_ADDRESS);
            //发送失败后,重试几次r
            producer.setRetryTimesWhenSendFailed(2);
            //发送消息超时
            producer.setSendMsgTimeout(3000);
           // producer.set


            producer.start();
            CountDownLatch countDownLatch = new CountDownLatch(2);
            String[] tags = new String[]{"TagA", "TagB", "TagC", "TagD", "TagE"};
            long count = 0;
            for (int i = 0; i < 10; i++) {
                //  int orderId = i % 10;
                for (int j = 0; j < 5; j++) {
                    Message msg =
                            new Message("TopicTest_c", tags[i % tags.length], "KEY" + j,
                                    ("Hello RocketMQ " + i+j).getBytes(RemotingHelper.DEFAULT_CHARSET));
                    // This message will be delivered to consumer 10 seconds later.
                    msg.setDelayTimeLevel(3);

                    SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
                        @Override
                        public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                            //the are为orderId
                            Integer id = (Integer) arg;
//                        int index = id % mqs.size();
//                         // 根据key的hash值来选择

                            int index = RocketUtils.toPositive(RocketUtils.murmur2(id.toString().getBytes())) % mqs
                                    .size();
                            MessageQueue queue = mqs.get(index);
                            logger.info("input value {} get queue size:{},choose queue: {}", id, mqs.size(), queue
                                    .getBrokerName());
                            return queue;
                        }
                    }, j);
                    logger.info("return send result {}", sendResult);
                    count++;
                }
            }
            logger.info("total send count{}", count);
            producer.shutdown();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



1.发送延迟消息只需要setDelayTimeLevel,设置延迟level就可以。
2.发送有序消息,一般消息是通过轮询所有队列来发送的(负载均衡策略),顺序消息可以根据业务,比如说订单号相同的消息发送到同一个队列,上面的策略是通过参数的hash值与queue数取余来选择queue。
3.consumer端也有rebalance机制来分配queue给指定的consumer消费,类似kafka的顺序消费。

5. consumer采用push模式消费,


    @Test
    public void consumer_push_Test_delay() {
        try {
            DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name_three_test");
            // consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
            consumer.setNamesrvAddr(RocketUtils.NAMES_SERVER_ADDRESS);
            //"TagA || TagC || TagD"
            // RocketUtils.TOPIC_NAME_DELAY
            consumer.subscribe("TopicTest_test", "*");
            //指定consumeMessage的时候,获取消息的数量,
            consumer.setConsumeMessageBatchMaxSize(1);
            consumer.setPullBatchSize(1);
            //以前是多线程的消费
            consumer.setConsumeThreadMin(1);
            consumer.setConsumeThreadMax(1);
            // consumer.setConsumeThreadMin();
            //consumer.resetClientConfig();

            final AtomicLong consumeTimes = new AtomicLong(0);
            final CountDownLatch latch = new CountDownLatch(1);

            consumer.registerMessageListener(new MessageListenerConcurrently() {
                @Override
                public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext
                        context) {
                    // context.setAutoCommit(true);
                    logger.info("===========start ============" + consumeTimes.longValue());
                    for (MessageExt message : msgs) {
//                        logger.info(" Receive New Messages: " + ToStringBuilder.reflectionToString(message,
//                                ToStringStyle.SHORT_PREFIX_STYLE) + "%n:" + new String(message.getBody()));

                        logger.info(" Receive New Messages queue:{},queue offset:{},message:{} ",message
                                .getQueueId(),message.getQueueOffset(),new
                                String(message
                                .getBody()));


                        logger.info("Receive message[msgId=" + message.getMsgId() + "] "
                                + (System.currentTimeMillis() - message.getStoreTimestamp()) + "ms later");

                    }
                    logger.info("===========end ============");
                    consumeTimes.incrementAndGet();
                    //   context.setAckIndex();
                    if (consumeTimes.longValue() > 5) {
                       // latch.countDown();
                        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                    } else {
                        /**
                         * 2017-08-14 14:05:38 INFO main - messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m
                         // 7m 8m 9m 10m 20m 30m 1h 2h
                         */
                        context.setDelayLevelWhenNextConsume(3);
                        return ConsumeConcurrentlyStatus.RECONSUME_LATER;
                    }
                }
            });

            consumer.start();
            latch.await();

            Thread.sleep(60 * 1000);

            logger.info("Consumer Started.%n");
            consumer.shutdown();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

  1. 发送一条消息,发送反馈消息ID:C0A804A15F0014DAD5DC12B5CC860000

2017-09-04 15:11:43,706 NettyClientSelector_1 DEBUG [io.netty.util.internal.Cleaner0] - [java.nio.ByteBuffer.cleaner(): available] 
SendResult [sendStatus=SLAVE_NOT_AVAILABLE, msgId=C0A804A15F0014DAD5DC12B5CC860000, offsetMsgId=AC101E0D00002A9F00000000002159A1, messageQueue=MessageQueue [topic=TopicTest_test, brokerName=broker-a, queueId=0], queueOffset=424]

2.根据上面的消费的代码,设置消息消费失败后延迟10s再试,根据下面的日志可以发现消息在10s后被重复消费。


2017-09-04 15:12:14,378 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [===========start ============0] 
2017-09-04 15:12:14,378 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [ Receive New Messages queue:0,queue offset:1,message:Hello RocketMQ andex  ] 
2017-09-04 15:12:14,859 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [Receive message[msgId=C0A804A15F0014DAD5DC12B5CC860000] 839ms later] 
2017-09-04 15:12:14,859 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [===========end ============] 
2017-09-04 15:12:24,918 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [===========start ============1] 
2017-09-04 15:12:24,918 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [ Receive New Messages queue:0,queue offset:18,message:Hello RocketMQ andex  ] 
2017-09-04 15:12:24,918 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [Receive message[msgId=C0A804A15F0014DAD5DC12B5CC860000] 351ms later] 
2017-09-04 15:12:24,918 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [===========end ============] 
2017-09-04 15:12:34,970 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [===========start ============2] 
2017-09-04 15:12:34,970 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [ Receive New Messages queue:0,queue offset:19,message:Hello RocketMQ andex  ] 
2017-09-04 15:12:34,970 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [Receive message[msgId=C0A804A15F0014DAD5DC12B5CC860000] 349ms later] 
2017-09-04 15:12:34,970 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [===========end ============] 
2017-09-04 15:12:45,019 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [===========start ============3] 
2017-09-04 15:12:45,019 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [ Receive New Messages queue:0,queue offset:20,message:Hello RocketMQ andex  ] 
2017-09-04 15:12:45,019 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [Receive message[msgId=C0A804A15F0014DAD5DC12B5CC860000] 346ms later] 
2017-09-04 15:12:45,019 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [===========end ============] 
2017-09-04 15:12:55,070 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [===========start ============4] 
2017-09-04 15:12:55,070 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [ Receive New Messages queue:0,queue offset:21,message:Hello RocketMQ andex  ] 
2017-09-04 15:12:55,070 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [Receive message[msgId=C0A804A15F0014DAD5DC12B5CC860000] 348ms later] 
2017-09-04 15:12:55,070 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [===========end ============] 
2017-09-04 15:13:05,120 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [===========start ============5] 
2017-09-04 15:13:05,120 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [ Receive New Messages queue:0,queue offset:22,message:Hello RocketMQ andex  ] 
2017-09-04 15:13:05,120 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [Receive message[msgId=C0A804A15F0014DAD5DC12B5CC860000] 346ms later] 
2017-09-04 15:13:05,120 ConsumeMessageThread_1 INFO [com.keruyun.rocket.client.ApacheRocketProducerTest] - [===========end ============] 

6. 消费者push模式两种监听模式

1.同一批的消息只要根据MessageQueueSelector投递消息到同一个queue,同一个queue只能被某一个消费者实例所拉取,同一个消费者实例肯定是按照顺序拉取消息提交给业务处理线程池。这样就保证消费端顺序消费。
2.(这里假设触发了重排导致queue分配给了别人也没关系,由于queue的消息永远是FIFO,最多只是已经消费的消息重复而已,queue内顺序还是能保证)
3. 但的确会有一些异常场景会导致乱序。如master宕机,导致写入队列的数量上出现变化。还有调整queue数,如果还是沿用取模的seletor,就会一批订单号的消息前面散列到q0,后面的可能散到q1,这样就不能保证顺序了。

7. MessageListenerOrderly
  1. MessageListenerOrderly是自带的保证顺序消费的Listener,我们可以使用MessageListenerConcurrently设置为单线程来替代它,因为它有如下问题:
  2. 遇到消息失败的消息,无法跳过,当前队列消费暂停。
  3. 目前版本的RocketMQ的MessageListenerOrderly是不能从slave消费消息的。
8. MessageListenerConcurrently
  1. 主要流程是PullMessageService单线程轮询pull数据到ConsumeMessageConcurrentlyService去消费。
  2. 消息集合按consumeBatchSize切分后放到consumeExecutor里去消费,所有消息被并行的执行。
12. 参考资料
  1. RocketMQ_example 例子代码
  2. 更多RocketMQ使用请下载github源码。
  3. 深入浅出学习product,consumer源码:
  4. http://blog.csdn.net/quhongwei_zhanqiu/article/details/39142693
    http://www.cnblogs.com/wxd0108/p/6055108.html

猜你喜欢

转载自blog.csdn.net/xhpscdx/article/details/77853031