阿里巴巴开源的消息中间件框架,后捐献给apache基金会
RocketMQ特点:
1.能够保证严格的消息顺序
2.提供丰富的消息拉取模式
3.高效的订阅者水平扩展能力
4.实时的消息订阅机制
5.支持事务消息
6.亿级消息堆积能力
RocketMQ架构:
Name Server
Broker Master
Producer
Comsumer
Topic
Message Queue
BrokerName来决定主丛节点是否为一组,BrokerId为0表示这个服务是主节点 非0表示从节点
Broker主节点负责将消息者发送的消息写入,再将消息同步到从节点,消费端监听从节点接收消息
集群模式:
1.单Master模式
一旦Master宕机,整个服务不可用,线上不用,开发测试可以用
2.多Master模式
其中一个Master宕机其他Master也可以顶上,性能较高
3.多Master多Slave模式(异步)
一个Slave只有一个Master,一个Master可以用多个Slave,Master和Slave的消息复制使用异步
4.多Master多Slave模式(同步)
一个Slave只有一个Master,一个Master可以用多个Slave,Master和Slave的消息复制使用同步
集群架构使用双主双从 2m-2s
整合springboot依赖
<!-- RocketMQ -->
<dependency>
<groupId>com.alibaba.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>3.5.8</version>
</dependency>
<dependency>
<groupId>com.alibaba.rocketmq</groupId>
<artifactId>rocketmq-all</artifactId>
<version>3.5.8</version>
<type>pom</type>
</dependency>
RockerMQ可以发送普通消息、顺序消息、事务消息;
2种常见的消费消息模式:
1. DefaultMQPushConsumer
消息消费者和broker建立长连接,broker每5秒检查一次队列有没这个消费者需要的消息,如果有就将消息推送给这个消息者
2. DefaultMQPullConsumer
消息消费者直接从broker中拉取需要的消息
发送消息步骤:
1.创建DefaultMQProducer
2.设置NameServer地址
3.开启DefaultMQProducer
4.创建消息Message
5.发送消息
6.关闭DefaultMQProducer
消息发送测试:
public class TestProducer {
public static void main(String[] args) {
//创建MQ生产者对象
DefaultMQProducer producer = new DefaultMQProducer("group1");
try {
//指定NameService地址
producer.setNamesrvAddr("127.0.0.1:9876");
//开启消息生产者
producer.start();
//创建消息对象
Message message = new Message("base","tag1","Hello RocketMQ33".getBytes());
//发送消息
SendResult result = producer.send(message);
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭消息生产者
producer.shutdown();
}
}
}
消费消息步骤:
1.创建DefaultMQPushConsumer
2.设置NameServer地址
3.设置subscribe,这里是要读取的主题信息
4.创建消息监听MessageListener
5.获取消息信息
6.返回消息读取状态
7.开启消费者
消费消息测试:
public class TestConsumer {
public static void main(String[] args) {
//创建MQ消费者对象
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
try {
//指定NameServer地址
consumer.setNamesrvAddr("127.0.0.1:9876");
//订阅topic
consumer.subscribe("base","tag1");
//消费消息
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
for (MessageExt messageExt : list) {
System.out.println(new String(messageExt.getBody()));
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//开启消费者MQ
consumer.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
消费消息模式分为两种:广播模式、负载均衡模式(默认)
setMessageModel方法指定选用哪种消费模式
顺序消息:
发送消息的顺序和消费消息的顺序一致
消息队列选择器
发送顺序消息测试:
public class OrderProducer {
public static void main(String[] args) throws Exception {
//创建消息生产者MQ
DefaultMQProducer producer = new DefaultMQProducer("group1");
//指定NameServer地址
producer.setNamesrvAddr("127.0.0.1:9876");
//启动生产者
producer.start();
List<Order> order = Order.getOrder();
//发送顺序消息
for (int i = 0; i < 9; i++) {
String details = order.get(i).getOrderDetails();
//创建消息对象
if(!StringUtils.isEmpty(details)){
Message message = new Message("base","tag1",details.getBytes());
producer.send(message, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> list, Message message, Object o) {
Integer index = (Integer)o % list.size();
return list.get(index);
}
},order.get(i).getOrderId());
}
}
//关闭生产者
producer.shutdown();
}
}
消费顺序消息测试:
public class OrderConsumer {
public static void main(String[] args) throws Exception {
//创建消费者MQ对象
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
//指定NameServer地址
consumer.setNamesrvAddr("127.0.0.1:9876");
//订阅topic
consumer.subscribe("base","tag1");
//消费消息
consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {
for (MessageExt messageExt : list) {
System.out.println("线程"+Thread.currentThread().getName()+"消费了消息:"+new String(messageExt.getBody()));
}
return ConsumeOrderlyStatus.SUCCESS;
}
});
//开启消费消息MQ
consumer.start();
}
}
延迟消息:
延迟消息级别,不能直接写时间,只能设定指定的时间间隔
延迟消息发送测试
public class DelayLeveProducer {
public static void main(String[] args) throws Exception {
//创建消息生产者MQ
DefaultMQProducer producer = new DefaultMQProducer("group1");
//指定NameServer地址
producer.setNamesrvAddr("127.0.0.1:9876");
//启动生产者
producer.start();
List<Order> order = Order.getOrder();
//发送顺序消息
for (int i = 0; i < 9; i++) {
String details = order.get(i).getOrderDetails();
//创建消息对象
if(!StringUtils.isEmpty(details)){
Message message = new Message("base","tag1",details.getBytes());
//设置延迟消息时间
message.setDelayTimeLevel(3);
//发送消息
producer.send(message, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> list, Message message, Object o) {
Integer index = (Integer)o % list.size();
return list.get(index);
}
},order.get(i).getOrderId());
}
}
//关闭生产者
producer.shutdown();
}
}
事务消息:
正常事务消息的发送、提交,事务消息的补偿流程
事务消息发送测试:
public static void main(String[] args) throws Exception {
//创建事务消息生产者
TransactionMQProducer producer = new TransactionMQProducer("group1");
//指定NameServer地址
producer.setNamesrvAddr("127.0.0.1:9876");
//启动事务生产者
producer.start();
//事务监听
producer.setTransactionCheckListener(new TransactionCheckListener() {
@Override
public LocalTransactionState checkLocalTransactionState(MessageExt messageExt) {
String tags = messageExt.getTags();
if(StringUtils.pathEquals(tags,"C")){
return LocalTransactionState.COMMIT_MESSAGE;
}
return null;
}
});
//发送消息
String tags[] = {"A","B","C"};
for (int i = 0; i < 3; i++) {
Message msg = new Message("TransationTopic",tags[i],("Hello 事务消息"+i).getBytes());
producer.sendMessageInTransaction(msg, new LocalTransactionExecuter() {
@Override
public LocalTransactionState executeLocalTransactionBranch(Message message, Object o) {
if(StringUtils.pathEquals("A",message.getTags())){
return LocalTransactionState.COMMIT_MESSAGE;
}else if(StringUtils.pathEquals("B",message.getTags())){
return LocalTransactionState.ROLLBACK_MESSAGE;
}else {
return LocalTransactionState.UNKNOW;
}
}
},tags[i]);
}
}
事务消息消费测试:
public static void main(String[] args) throws Exception {
//创建消费者MQ对象
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
//指定NameServer地址
consumer.setNamesrvAddr("127.0.0.1:9876");
//订阅topic
consumer.subscribe("TransationTopic","*");
//消费消息
consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {
for (MessageExt messageExt : list) {
System.out.println("线程"+Thread.currentThread().getName()+"消费了消息:"+new String(messageExt.getBody()));
}
return ConsumeOrderlyStatus.SUCCESS;
}
});
//开启消费消息MQ
consumer.start();
}
整合SpringBoot
1.导入pom依赖
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.0.3</version>
</dependency>
2.配置文件
rocketmq:
name-server: 127.0.0.1:9876 #指定nameserver的地址,如果是集群可以用逗号隔开
producer:
group: springboot-rocketmq-gmall #指定消息发送到哪个组
3.发送消息
@RestController
@RequestMapping("/rocketmq")
public class RocketMQProducer {
@Autowired
private RocketMQTemplate rocketMQTemplate;
@RequestMapping("/send")
public String send(){
String topic = "springboot-rocketMQ";
rocketMQTemplate.convertAndSend(topic,"hello springboot-rocketMQ");
return "发送消息成功";
}
}
4.消费消息
@RocketMQMessageListener(consumerGroup = "rocketmq-consumer",topic = "springboot-rocketMQ")
@Component
public class RocketMQConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String s) {
System.out.println("消费的消息是"+s);
}
}
消息存储(持久化):
消息存储顺序写保证消息写入磁盘的速度
RocketMQ利用零拷贝技术提高消息存盘和网络发送速度
刷盘机制:
高可用性:
NameServer集群、Broker集群、Producer集群、Consumer集群
Broker主从复制:
保证主从服务的消息一致性
同步复制
当消息生产者发送消息后,master将消息同步到slave,当同步成功之后才会返回发送成功的状态,这样的话就会有延迟,效率低;
异步复制
当消息生产者发送消息后,只要master接收到了消息,那么直接会返回消息发送成功的状态,然后另外开启一个线程去将消息同步到slave,效率会比较高;可能出现写入slave的时候master宕机了,那么消息就会丢失;
生产者一般使用异步刷盘+同步复制的方案,既保证吞吐量又保证消息可靠性
负载均衡:
生产者负载均衡默认开启,利用topic每次都分发到不同的消息队列
消费者负载均衡,多个消费者会均摊消息队列,每个消费者消费固定的消息队列中的消息
消息重试机制:
分为顺序消息重试和无序消息(普通、定时、延时、事务)重试
顺序消息默认是重试的,前面的消息没被消费后面的消息不会被消费,会产生消息阻塞的情况
只有集群模式支持重试机制,广播模式不支持重试机制
重试次数:
16次重试还没消费成功消息,消息将会进入死信队列
死信队列:
当消息重试次数超过最大次数后,消息将会放入死信队列;
不会再被消费者正常消费
有效期为3天,3天后自动删除,因此需要3天内去处理这些死信消息
一个死信队列对应一个group,和topic无关
消息幂等性:
网络波动或者发送消息时宕机或者消费者消息时宕机等等,都会出现消息重复的原因,那么必须有一个幂等性校验消费;
每次消费消息去redis查看当前消息id存不存在,存在表示消费过直接丢弃,不存在表示没消费,可以消费此消息,消费完消息后将消息id存入redis中方便下一次做校验;