090:RocketMQ-RocketMQ集群部署原理与顺序消息原理
- 1 rocketmq架构原理简单技术回顾
- 2 rocketmq集群四种模式架构
- 3 rocketmqBroker集群的方式
- 4 rocketmq如何实现实现动态扩容
- 5 rocketmq如何实现topic拆分多个不同队列
- 6 rocketmq如何保证消息的顺序的问题
- 7 rocketmq.代码形式解决消息顺序性问题
1 rocketmq架构原理简单技术回顾
课程内容:
- Broker如何实现多主集群模式
- Broker单个主题分为多个不同队列模式
- 生产者手写投递分片策略算法
- RocketMQ顺序消息实现原理
2 rocketmq集群四种模式架构
RocketMQ 四种集群部署方式
- 单个Master节点,缺点:负载压力大,如果宕机之后可能整个服务不可用;
- 多个Master节点,分摊存放消息,缺点:没有Slave节点,主的Master节点宕机之后没有备份消息数据可能会丢失;
- 多个Master和Slave节点,采用同步形式实现主从数据同步,主备节点都存放队列消息才返回ack表示投递成功,效率比较低,数据不会产生延迟、不会丢失;
- 多个Master和Slave节点,采用异步形式实现主从数据同步,效率非常高,数据同步可能短暂产生延迟(毫秒级别的)、数据可能丢失;
3 rocketmqBroker集群的方式
RocketMQ 集群部署
cd /usr/local/rocketmq/conf
vi broker.conf
#集群名称,可以区分不同集群,不同的业务可以建多个集群
brokerClusterName=mayikt
# Broker 的名称, Master 和Slave 通过使用相同的Broker 名称来表明相互关系,以说明某个Slave 是哪个Master 的Slave。
brokerName=broker-a
# 一个Master Barker 可以有多个Slave, 0 表示Master ,大于0 表示不同Slave 的ID。
brokerId=0
#与fileReservedTim巳参数呼应,表明在几点做消息删除动作,默认值04 表示凌晨4点。
deleteWhen=04
namesrvAddr=mqnameserver1:9876;mqnameserver2:9876
autoCreateTopicEnable=true
#topic默认创建的队列数
defaultTopicQueueNums=4
#是否允许Broker自动创建订阅组,建议线下开启,线上关闭,默认【true】
autoCreateSubscriptionGroup=true
#Broker 监听的端口号,如果一台机器上启动了多个Broker,则要设置不同的端口号,避免冲突。
listenPort=10911
brokerIp=192.168.1.1
集群搭建
集群效果
4 rocketmq如何实现实现动态扩容
rocketmq动态扩容原理
新增的节点统一注册到相同的nameServer上,Producer从nameServer动态获取节点地址,再本地实现rpc负载均衡调用投递消息。
5 rocketmq如何实现topic拆分多个不同队列
RocketMQ单机版本情况下,默认情况下创建一个topic会将该topic分成四个队列存放;优点:高吞吐量
需要注意的问题:消息的顺序消费问题。
6 rocketmq如何保证消息的顺序的问题
顺序消息产生背景:
生产者向同一个主题中投递的消息行为每次可能不同
基于消息中间件投递消息 订单相关
新增订单、修改订单、删除订单 必然产生顺序性问题
最大的难点:消费者集群、队列集群
解决顺序问题核心思路:
- 相同的业务逻辑一定要存放到同一个队列(先进先出);
- 每个队列必须要对应同一个消费者消费;
- RocketMQ每个队列1:1均等匹配消费者;
单机版本中如何增加rocketMQ吞吐量?只需增加队列总数即可,消费者数量1:1增加。
7 rocketmq.代码形式解决消息顺序性问题
maven依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.0.3</version>
</dependency>
</dependencies>
application.yml
rocketmq:
###连接地址nameServer
name-server: 192.168.206.57:9876
producer:
group: mayikt_producer
server:
port: 8089
核心代码
@RestController
public class ProducerController {
@Autowired
private RocketMQTemplate rocketMQTemplate;
@RequestMapping("/sendMsg")
public String sendMsg() throws InterruptedException, RemotingException, MQClientException, MQBrokerException {
Long orderId = System.currentTimeMillis();
String insertSql = getSqlMsg("insert", orderId);
String updateSql = getSqlMsg("update", orderId);
String deleteSql = getSqlMsg("delete", orderId);
Message insertMsg = new Message("mayikt-topic", insertSql.getBytes());
Message updateMsg = new Message("mayikt-topic", updateSql.getBytes());
Message deleteMsg = new Message("mayikt-topic", deleteSql.getBytes());
DefaultMQProducer producer = rocketMQTemplate.getProducer();
// producer.send(insertMsg);
// producer.send(updateMsg);
// producer.send(deleteMsg);
rocketMQTemplate.getProducer().send(insertMsg
, new MessageQueueSelector() {
/**
* @param mqs 集群队列总数
* @param msg 消息内容
* @param arg
* @return 传递orderId
*/
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg,
Object arg) {
// 该消息存放到队列0中
return mqs.get(0);
}
}, orderId);
rocketMQTemplate.getProducer().send(updateMsg
, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg,
Object arg) {
// 该消息存放到队列0中
return mqs.get(0);
}
}, orderId);
rocketMQTemplate.getProducer().send(deleteMsg
, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg,
Object arg) {
// 该消息存放到队列0中
return mqs.get(0);
}
}, orderId);
return orderId + "";
}
public String getSqlMsg(String type, Long orderId) {
JSONObject dataObject = new JSONObject();
dataObject.put("type", type);
dataObject.put("orderId", orderId);
return dataObject.toJSONString();
}
}
@Service
@RocketMQMessageListener(topic = "mayikt-topic", consumerGroup = "mayiktTopic", consumeMode = ConsumeMode.ORDERLY, consumeThreadMax = 1)
public class OrderConsumer implements RocketMQListener<MessageExt> {
// consumeMode = ConsumeMode.ORDERLY一个队列对应一个Thread线程
// consumeThreadMax设置最大线程数
@Override
public void onMessage(MessageExt message) {
System.out.println(Thread.currentThread().getName() + "," +
"队列" + message.getQueueId() + "," + new String(message.getBody()));
}
}
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class AppRocketMQ {
public static void main(String[] args) {
SpringApplication.run(AppRocketMQ.class);
}
}
运行结果: