面试二十二、rocketMq
1、作用
1)解藕
2)有些任务没有实时性要求,当前因为请求量瞬间值高导致服务撑不住,
这时可以把任务放在mq里慢慢消费调。例如统计优惠券使用数量
2、rocketmq有哪些角色组成,作用是什么
1)nameserver:服务发现者,producer、borker、consumer都需定时向其上报自己的信息
以便互相发现彼此,如超时不上报则会从列表删除。
2)producer:生产者,随机获得一个nameserver节点建立长连接,
获取topic信息(topic路由、topic下queue、queue分布在哪些broker)
和topic的master节点建立长连接(只有master可以写消息)
3)broker:mq本身,负责收发、持久化消息
4)consumer:消费者,通过nameserver获取topic信息,连接上对的broker消费消息。
因为master和slave都可以读消息,所以consumer和两者都会建立连接
3、broker中消息被消费后,消息会立即删除吗
不会,消息会持久化在commitlog里,每个connsumer会记录当前消费的offset
4、消息什么时候删除
- 检查这个文件最后访问时间
- 判断是否大于过期时间
- 指定时间删除,默认凌晨4点
1 /** 2 * {@link org.apache.rocketmq.store.DefaultMessageStore.CleanCommitLogService#isTimeToDelete()} 3 */ 4 private boolean isTimeToDelete() { 5 // when = "04"; 6 String when = DefaultMessageStore.this.getMessageStoreConfig().getDeleteWhen(); 7 // 是04点,就返回true 8 if (UtilAll.isItTimeToDo(when)) { 9 return true; 10 } 11 // 不是04点,返回false 12 return false; 13 } 14 15 /** 16 * {@link org.apache.rocketmq.store.DefaultMessageStore.CleanCommitLogService#deleteExpiredFiles()} 17 */ 18 private void deleteExpiredFiles() { 19 // isTimeToDelete()这个方法是判断是不是凌晨四点,是的话就执行删除逻辑。 20 if (isTimeToDelete()) { 21 // 默认是72,但是broker配置文件默认改成了48,所以新版本都是48。 22 long fileReservedTime = 48 * 60 * 60 * 1000; 23 deleteCount = DefaultMessageStore.this.commitLog.deleteExpiredFile(72 * 60 * 60 * 1000, xx, xx, xx); 24 } 25 } 26 27 /** 28 * {@link org.apache.rocketmq.store.CommitLog#deleteExpiredFile()} 29 */ 30 public int deleteExpiredFile(xxx) { 31 // 这个方法的主逻辑就是遍历查找最后更改时间+过期时间,小于当前系统时间的话就删了(也就是小于48小时)。 32 return this.mappedFileQueue.deleteExpiredFileByTime(72 * 60 * 60 * 1000, xx, xx, xx); 33 }
5、消费模式有哪些
1)集群模式:一条消息只会被同一个group里的一个consumer消费;
多个group订阅topic时,每个group会有一个consumer消费到消息
2)广播模式:所有订阅了topic的group下的所有consumer都会消费到消息
6、消费消息是pull还是push
使用的pull
使用pull模式,消费者能更好的控制消费节奏。
如果使用push模式会导致消息push过快而消费速度慢,导致消息在消费者中堆积确无法被其他消费者消费的情况。
7、负载均衡
1)producer端
通过一定策略将数据发送到不同borker里,来实现负载均衡
策略:
1.1)如果不设置策略
第一步:获取一个随机数
第二步:轮询每个queue找到可用的
第三步:如果全部不可用,开启容错时选择一个相对较好的queue
不开启容错则随机选择一个queue
1.3)SelectMessageQueueByHash,通过hash
1.4)SelectMessageQueueByRandom
1.5)也可以自行实现MessageQueueSelector接口中的select方法
(比如订单可以用订单号hash来确定broker,使一个订单号的消息都在同一台broker)
1 MessageQueue select(final List<MessageQueue> mqs, final Message msg, final Object arg);
2)consumer端
策略:
1)默认采用平均分配方法来实现负载均衡
如果consumer个数和queue数不对等时:
consumer个数比queue个数多,多个consumer消费一个queue
consumer个数和queue个数一样,一个consumer消费一个queue
consumer个数比queue个数少,一个consumer消费多个queue
2)AllocateMessageQueueConsistentHash:一致性哈希
何时reblance:
1)当一个consumer宕机最多20秒执行reblance,新consumer重新消费
2)当有新consumer接入时,立即执行reblance。
8、重复消费
正常情况下消息消费完后,consumer会发生ack,通知broker将消息从queue中剔除
当意外原因导致broker没有收到ack时,broker会认为消费没有被消费于是重新再把消息投送给consumer
解决方案:
消息消费前,将消息主键在带有唯一约束性的字段中insert
9、如何保证消息顺序消费
1)同一个queue:消息最终会放在queue里,一个queue是FIFO的,但是多个queue是无法保证消息顺序的
所以方案就是将要顺序消费的消息存入到一个queue中即可(实现MessageQueueSelector的select方法)
2)消费者单线程
3)生产者单线程
10、消息实体转byte[]
1 public BaseMessage(BaseMqEntity dto, String topic, String tag) { 2 if (dto != null) { 3 this.setBody(JSONObject.toJSONBytes(dto, new SerializerFeature[0])); 4 } 5 6 this.setTopic(topic); 7 this.setTag(tag); 8 }
11、如何保证消息可靠
1)producer端:可以采用同步发生方式、重试机制
2)consumer端:ack确认、重试机制
3)broker端:1、修改刷盘策略为同步。
默认异步刷盘:当borker收到消息时会存入cache并通知producer已经存储成功,
而后异步将cache数据持久化到硬盘。
2、集群部署主从模式高可用
12、消息持久化
commitLog:消息会顺序写入磁盘文件commitLog中,实际存储消息的文件
consumerQueue:producer生成完消息后会先进入consumerQueue。
消费消息时消费者只能订阅一个topic,
但是一个topic在commitLog中不可能是连续的 这样查询消息就会变慢,
为了解决这个问题引入了consumerQueue,里面只存储了对应topic的消息,
consumer只需要去取consumerQueue里的消息即可。
刷盘模式:
异步刷盘模式则直接返回成功,当数据量累积到一定时会写入commitLog
同步刷屏模式则直接将数据写入commitLog
indexFile:索引文件,记录的消息key和offset
13、事务消息
当发送mq后事务回滚了,这时需要使用事务消息回滚mq。
过程:
1)生产者将Half Message发生给broker
2)执行本地事务
3)执行结果返回commit,则发生mq到queue供消费结束
返回rollback,则删除之前到Half Message结束
返回unknow,则启动主动查询
4)主动查询结果返回commit,则发送mq到queue供消费结束
返回rollback,则删除之前的Half Message结束
返回unknow,则继续主动查询
实现:
使用TransactionMQProducer
设置TransactionCheckListener,实现checkLocalTransactionState方法
1 public static void main(String[] args) { 2 TransactionMQProducer producer = new TransactionMQProducer(); 3 producer.setNamesrvAddr(""); 4 producer.setTransactionCheckListener(new TransactionCheckListener() { 5 @Override 6 public LocalTransactionState checkLocalTransactionState(MessageExt msg) { 7 return LocalTransactionState.COMMIT_MESSAGE; 8 } 9 }); 10 }
14、实操:目前是接入阿里云的ons(Open Notification Service)
15、为什么不用zookeeper而是自己写一套nameserver:
1)zookeeper是满足cp的,也就是无法保证服务高可用。
2)zookeeper的写是不能扩展的,而nameserver可以通过增加机器扩展,每个节点都可写无主备
3)zookeeper整体太笨重,例如:zookeeper一个功能是选举出master,
而rocketMq是通过配置设定主从节点,也就没必要用选举功能
4)zookeeper每个节点同步数据,
nameserver互相之间不通信由broker、produce、consumer自己上报给每一个nameserver节点
参考文章:https://www.cnblogs.com/javazhiyin/p/13327925.html
posted on 2021-08-30 15:02 Iversonstear 阅读(223) 评论(0) 编辑 收藏 举报