目录
正文
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();
}
}
关注微信