consumer.setMessageModel(MessageModel.CLUSTERING);
当多个消费者组订阅了同一个 Topic 时,每个组中其中一个消费者都会收到这条消息的副本。
由broker以消费组为单位维护消费进度也就是消费位点
consumer.setMessageModel(MessageModel.BROADCASTING);
所有订阅的消费者都会收到这条消息的副本。适合
- 每个消费者 将自己的消费进度保存在本地(默认是磁盘)。
- 不会提交到 Broker(Broker 不知道你消费到哪了)。
- 每个消费者维护一个独立的 offset 文件。
- 消费位点文件路径(默认)$HOME/.rocketmq_offsets/ip@instanceName/消费组名/offsets.json
- 消费者用本地的消费位点去消费消息,文件不存在则创建,并且从最新的消息开始消费
- 不会自动进行重试,即使加了RECONSUME_LATER
同⼀个Topic下有多种不同的消息,消费者只希望关注某⼀类消息。
consumer.subscribe("TopicTest", "TagA || TagB"); // 只消费 TagA 和 TagB 的消息
enablePropertyFilter=true
msg.putUserProperty("a", "15");
//TAGS是msg的tags,msg还可以put多个属性
consumer.subscribe("TopicTest", MessageSelector.bySql("(TAGS is not null and TAGS in ('TagA', 'TagB')) and (a is not null and a between 0 and 3)"));
每⼀个订单有从下单、锁库存、⽀付、下物流等⼏个业务步骤。每个业务步骤都由⼀个消息⽣产者通知给下游服务。如何保证对每个订单的业务处理顺序不乱?
用 MessageQueueSelector 选择队列,利用单个队列fifo机制来实现顺序发送。
通过MessageQueueSelector,将orderId相同的消息,都转发到同⼀个MessageQueue中。
for (int i = 0; i < 20; i++) {
int orderId = i;
for(int j = 0 ; j < 5; j++){
Message msg =
new Message("OrderTopic", "order_"+orderId, "KEY" + orderId,
("order_" + orderId + " step " + j).getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult sendResult = 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);
}
}, orderId);
System.out.printf("%s%n", sendResult);
}
}
注⼊⼀个MessageListenerOrderly实现。
一个消息队列在任一时刻只会被一个线程消费,消息按发送顺序依次拉取和处理。
consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
context.setAutoCommit(true);
for(MessageExt msg:msgs){
System.out.println("收到消息内容 "+new String(msg.getBody()));
}
return ConsumeOrderlyStatus.SUCCESS;
}
});
注意点:
会阻塞整个队列过一段时间在从返回SUSPEND_CURRENT_QUEUE_A_MOMENT的消息开始消费。
RocketMQ 的 延迟消息(Scheduled / Delayed Message) 是一种机制,允许消息 在指定时间后再被消费,适用于如订单超时取消、延时通知、定时任务等场景。
message.setDeliverTimeMs(System.currentTimeMillis() + 10_000L);
//指定固定的延迟级别
message.setDelayTimeLevel(3);
可配置延迟级别(不推荐)
延迟级别实现:
对于指定固定延迟级别的延迟消息,RocketMQ的实现⽅式是预设⼀个系统Topic,名字叫做SCHEDULE_TOPIC_XXXXX。在这个Topic下,预设18个MessageQueue。这⾥每个对列就对应了⼀种延迟级别。然后每次扫描这18个队列⾥的消息,进⾏延迟操作就可以了。
指定时间点的延迟消息,RocketMQ是通过时间轮算法实现的。
在 RocketMQ 中发送 批量消息(Batch Messages) 是一种优化吞吐量的常用方式。它允许你一次发送多条消息,减少网络开销,提升发送性能。但批量消息也有一些使用限制和注意事项。
List<Message> messages = new ArrayList<>();
messages.add(new Message(TOPIC, TAG, "OrderID001", "Hello world 0".getBytes(StandardCharsets.UTF_8)));
messages.add(new Message(TOPIC, TAG, "OrderID002", "Hello world 1".getBytes(StandardCharsets.UTF_8)));
messages.add(new Message(TOPIC, TAG, "OrderID003", "Hello world 2".getBytes(StandardCharsets.UTF_8)));
SendResult sendResult = producer.send(messages);
事务消息是RocketMQ⾮常有特⾊的⼀个⾼级功能。他的基础诉求是通过RocketMQ的事务机制,来保证上下游的数据⼀致性。
考虑到事务的安全性,即要保证相关联的这⼏个业务⼀定是同时成功或者同时失败的。如果要将四个服务⼀起作为⼀个分布式事务来控制,可以做到,但是会⾮常麻烦。⽽使⽤RocketMQ在中间串联了之后,事情可以得到⼀定程度的简化。由于RocketMQ与消费者端有失败重试机制,所以,只要消息成功发送到RocketMQ了,那么可以认为Branch2.1,Branch2.2,Branch2.3这⼏个分⽀步骤,是可以保证最终的数据⼀致性的。这样,⼀个复杂的分布式事务问题,就变成了MinBranch1和Branch2两个步骤的分布式事务问题。
RocketMQ提出了事务消息机制,采⽤两阶段提交的思路,保证Main Branch1和Branch2之间的事务⼀致性。
RocketMQ保证了本地事务和消息生产两步骤的原子性,下游消费者有重试机制和死信机制,做最终的成功。代码需要注意的消息的幂等性,防止重复消费
TransactionListener transactionListener = new TransactionListenerImpl();
TransactionMQProducer producer = new TransactionMQProducer(PRODUCER_GROUP, Arrays.asList(TOPIC));\
producer.setExecutorService(executorService);//设置 RocketMQ 事务消息生产者在执行本地事务回查(TransactionCheck)任务时,所用的线程池。
producer.setTransactionListener(transactionListener);
RocketMQ提供了针对队列、⽤户等不同维度的⾮常全⾯的权限管理机制。通常来说,RocketMQ作为⼀个内部服务,是不需要进⾏权限控制的,但是,如果要通过RocketMQ进⾏跨部⻔甚⾄跨公司的合作,权限控制的重要性就显现出来了。
RocketMQ针对每个Topic,就有完整的权限控制。⽐如,在控制平台中,就可以很⽅便的给每个Topic配置权限。
perm字段表示Topic的权限。有三个可选项。 2:禁写禁订阅,4:可订阅,不能写,6:可写可订阅
在Broker端还提供了更详细的权限控制机制。主要是在broker.conf中打开acl的标志:aclEnable=true。然后就可以⽤他提供的plain_acl.yml来进⾏权限配置了。并且plain_acl这个配置⽂件是热加载的
broker.conf
# ACL 开关
enableAcl=true
# 配置 ACL 白名单 IP(可选)
aclWhiteRemoteAddress=192.168.1.0/24;127.0.0.1
plain_acl.yml
#全局⽩名单,不受ACL控制
#通常需要将主从架构中的所有节点加进来
globalWhiteRemoteAddresses:
- 10.10.103.*
- 192.168.0.*
accounts:
#第⼀个账户
- accessKey: RocketMQ
secretKey: 12345678
whiteRemoteAddress:
admin: false
defaultTopicPerm: DENY #默认Topic访问策略是拒绝
defaultGroupPerm: SUB #默认Group访问策略是只允许订阅
topicPerms:
- topicA=DENY #topicA拒绝
- topicB=PUB|SUB #topicB允许发布和订阅消息
- topicC=SUB #topic只允许订阅
groupPerms:
# the group should convert to retry topic
- groupA=DENY
- groupB=PUB|SUB
- groupC=SUB
#第⼆个账户,只要是来⾃192.168.1.*的IP,就可以访问所有资源
- accessKey: rocketmq2
secretKey: 12345678
whiteRemoteAddress: 192.168.1.*
# if it is admin, it could access all resources
admin: true
客户端登录
mave
<dependency>
<groupId>org.apache.rocketmqgroupId>
<artifactId>rocketmq-aclartifactId>
<version>4.9.1version>
dependency>
然后在声明客户端时,传⼊⼀个RPCHook。
//声明时传⼊RPCHook
DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName", getAclRPCHook());
private static final String ACL_ACCESS_KEY = "RocketMQ";
private static final String ACL_SECRET_KEY = "1234567";
static RPCHook getAclRPCHook() {
return new AclClientRPCHook(new SessionCredentials(ACL_ACCESS_KEY,ACL_SECRET_KEY));
}