RocketMQ教程(二) - 进阶

目录

(一)基础
(二)进阶
(三)高可用部署
(四)常见问题

 

正文

1. 定义

RocketMQ中的几个重要概念:

Producer, Consumer, Broker, Topic, Message Queue

下图大致描述了它们之间的关系

消费者通过Topic和Tags订阅感兴趣的消息。Topic可以设置一个或多个Message Queue, 消息可以并行地向各个
Message Queue 发送, 消费者也可以并行地从多个Message Queue 读取消息, 从而提高并发处理能力。

另外还有一个重要的角色是NameServer, 负责Producer、Consumer和Broker之间的协调。

2. 消费者类型

消费者分为PushConsumer和PullConsumer两种类型, PushConsumer使用简单, 只需设置订阅参数和消息处理方法, 系统自动保存Offset。示例代码见上一篇。

DefaultMQPushConsumer使用长轮询方式达到把新消息推送给Client的效果。

使用PullConsumer则可控性更高, 如设定拉取时间、控制Offset。代码如下:

@Service
public class PullConsumerService {

    private DefaultMQPullConsumer pullConsumer = null;
    private static final Map<MessageQueue, Long> OFFSET_TABLE = new HashMap<>();
    private static final Executor PULL_EXECUTOR = Executors.newFixedThreadPool(8);
    private static AtomicBoolean RUNNING = new AtomicBoolean(true);

    @PostConstruct
    public void initMQConsumer() {
        pullConsumer = new DefaultMQPullConsumer("bossGroup");
        pullConsumer.setNamesrvAddr("localhost:9876");
        pullConsumer.setVipChannelEnabled(false);
        try {
            pullConsumer.start();
            Set<MessageQueue> mqs = pullConsumer.fetchSubscribeMessageQueues("boss");
            for(MessageQueue mq : mqs) {
                PULL_EXECUTOR.execute(new Runnable() {
                    @Override
                    public void run() {
                        while (RUNNING.get()) {
                            try {
                                PullResult pullResult =
                                        pullConsumer.pullBlockIfNotFound(mq, null, getMessageQueueOffset(mq), 32);
                                putMessageQueueOffset(mq, pullResult.getNextBeginOffset());
                                switch (pullResult.getPullStatus()) {
                                    case FOUND:
                                        System.out.println("Found " + pullResult.getMsgFoundList().size() + " message(s)");
                                        break;
                                    case NO_MATCHED_MSG:
                                        break;
                                    case NO_NEW_MSG:
                                        break;
                                    case OFFSET_ILLEGAL:
                                        break;
                                    default:
                                        break;
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                });
            }
        } catch (MQClientException e) {
            e.printStackTrace();
        }
    }

    private long getMessageQueueOffset(MessageQueue mq) {
        Long offset = OFFSET_TABLE.get(mq);
        if (offset != null)
            return offset;
        return 0;
    }

    private void putMessageQueueOffset(MessageQueue mq, long offset) {
        OFFSET_TABLE.put(mq, offset);
    }


    @PreDestroy
    public void shutDownConsumer() {
        RUNNING.set(false);
        if (pullConsumer != null) {
            pullConsumer.shutdown();
        }
    }
}

3. ConsumerGroup

设置相同的GroupName可以把多个Consumer归并为一组, 提高并发处理能力。

4. 延时设置

message.setDelayTimeLevel(1);

注意只能设置预设值, 内置的delayTimeLevel选项有(单位秒)

protected static final int[] DELAY_LEVEL = {
        1, 5, 10, 30, 1 * 60, 5 * 60, 10 * 60,
        30 * 60, 1 * 3600, 2 * 3600, 6 * 3600, 12 * 3600, 1 * 24 * 3600};

5. 提高Producer发送速度

增加Producer数量或者使用Oneway方式发送, Oneway方式只发送请求不等待应答, 适用于对可靠性要求不高的场景, 如日志数据。

6. 选择Queue

默认情况下, Producer会轮流向Topic下的各Queue发送消息, 被哪个消费者消费也不能控制。使用MessageQueueSelector可以把消息发送到指定的Queue中, 下面示例中把value相同的消息发送到同一个Queue。

    public void send(String topic, String tags, Something something) {
        Message msg = new Message(topic, tags, "", JSON.toJSONString(something).getBytes());
        try {
            producer.send(msg, new MessageQueueSelector() {
                @Override
                public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                    Integer id = (Integer) arg;
                    int index = id % mqs.size();
                    return mqs.get(index);
                }
            }, something.getValue());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

7. 消息顺序处理

有时候我们希望按顺序处理一批消息, 比如给用户发奖后再发送站内信。在上一步中把同value的消息发送到同一个Queue中后, 再对Consumer做简单改造即可保证value相同的一系列消息按顺序被消费。

    public void initMQConsumer() {
        pushConsumer = new DefaultMQPushConsumer("bossGroup");
        pushConsumer.setNamesrvAddr("localhost:9876");
        try {
            pushConsumer.subscribe("boss", "*");
            pushConsumer.registerMessageListener(new MessageListenerOrderly() {
                @Override
                public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
                    return ConsumeOrderlyStatus.SUCCESS;
                }
            });
            pushConsumer.start();
        } catch (MQClientException e) {
            e.printStackTrace();
        }
    }

关注微信

  • 8
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值