RocketMQ是一款分布式、队列模型的消息中间件,由阿里巴巴团队研发,借鉴参考了 JMS 规范的MQ实现(如 activeMQ),更参考了优秀的开源消息中间件kafka,具有低延迟,高性能和可靠性。
主要运用在比较重要的消息传递或通知等业务
producer.send(msg);
主要用于对发送消息响应时间要求更高/快的场景
producer.send(msg, sendCallback);
// sendCallback:onSuccess(SendResult sendResult),onException(Throwable e);
适用于某些耗时非常短,但对可靠性要求并不高的场景,如日志收集等
producer.sendOneway(msg);
如果需要消息保持顺序,需要生产者生产消息的时候也要保持顺序。
producer.send(message, new SelectMessageQueueByHash(), hashKey);
rocketMQ通过MessageQueueSelector实现的算法来确定消息发送到哪一个队列上。
按照hash值分配队列,这个是比较常用的,例如可以使用订单号等,同一订单号的则会分配到同一队列上
按照随机数分配队列
字面意思是按照机房分配队列,需要重写里面的方法,否则会报错
执行过程:
示例:
@MQTransactionProducer(producerGroup = "tmcbGroup")
public class MessageProducer extends AbstractMQTransactionProducer {
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
return LocalTransactionState.UNKNOW;
}
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
return LocalTransactionState.COMMIT_MESSAGE;
}
}
需要实现【TransactionListener】类,然后根据本地事务执行的状况判断消息是发送还是不发送,消费端要做好幂等处理。
LocalTransactionState的三种状态:
RocketMQ 支持延时发送消息,不过不是随意延迟时间,有多个等级对应不同的延时时间
message.setDelayTimeLevel(3);
RocketMQ 支持批量发送消息,示例如下:
String topic = "BatchTest";
List<Message> messages = new ArrayList<>();
messages.add(new Message(topic, "TagA", "OrderID001", "Hello world 0".getBytes()));
messages.add(new Message(topic, "TagA", "OrderID002", "Hello world 1".getBytes()));
messages.add(new Message(topic, "TagA", "OrderID003", "Hello world 2".getBytes()));
producer.send(messages);
RocketMQ支持使用日志的格式生产消息,支持 log4j、log4j2、logback等,示例如下:
<appender name="mqAppender1" class="org.apache.rocketmq.logappender.logback.RocketmqLogbackAppender">
<tag>yourTag</tag>
<topic>yourLogTopic</topic>
<producerGroup>yourLogGroup</producerGroup>
<nameServerAddress>yourRocketmqNameserverAddress</nameServerAddress>
<layout>
<pattern>%date %p %t - %m%n</pattern>
</layout>
</appender>
<appender name="mqAsyncAppender1" class="ch.qos.logback.classic.AsyncAppender">
<queueSize>1024</queueSize>
<discardingThreshold>80</discardingThreshold>
<maxFlushTime>2000</maxFlushTime>
<neverBlock>true</neverBlock>
<appender-ref ref="mqAppender1"/>
</appender>
客户端手动去拉取消息,示例如下
DefaultMQPullConsumer consumer = new DefaultMQPullConsumer("consumer_first");
PullResult pullResult = consumer.pullBlockIfNotFound(mq, null, getMessageQueueOffset(mq),32);
实质还是pull的方式,不过跟消费者保持了长连接,如果有消息产生就会让消费者去消费。
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_first");
consumer.subscribe("rocket_first", "tag-a");
consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> {
list.forEach(me -> System.out.println("push: " + new String(me.getBody()) + "\n"));
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
类似于ActiveMQ中的发布订阅模式,消息会发给Consume Group中的每一个消费者进行消费。
consumer.setMessageModel(MessageModel.BROADCASTING);
一个 ConsumerGroup 中的 Consumer 实例平均分摊消费生产者发送的消息。
consumer.setMessageModel(MessageModel.CLUSTERING);
注意:
如果有多个 ConsumerGroup 订阅相同的 topic,则每个 ConsumerGroup 都会消费相同的消息。
常见的过滤方式,示例如下:
// 方法一:
consumer.subscribe("rocket_first", "tag-a");
// 方法二:
consumer.subscribe("rocket_first", MessageSelector.byTag("tag-a || TAGB"));
支持SQL92语法方式过滤,支持的语法如下
举例如下:
// 生产者
message.putUserProperty("a", "1");
producer.send(message);
// 消费者
consumer.subscribe("rocket_first", MessageSelector.bySql("a < 2"));
如果消费者端报【The broker does not support consumer to filter message by SQL92】,请在 broker 的配置文件里添加如下配置:
enablePropertyFilter=true
然后重启下 broker 即可。