1、引入消息队列之后该如何保证其高可用性?
搭建ActiveMQ的集群
基于Zookeeper和LevelDB搭建ActiveMQ集群。集群尽提供主备方式的高可用集群功能,避免单点故障
zookeeper+replicated+Leveldb-store的主从集群
三种集群方式:
- 基于shareFileSystem共享文件系统(KahaDB默认)
- 基于JDBC
- 基于可复制的LevelDB
LevelDB 5.6版本之后推出了LevelDB的持久化引擎,它使用自定义的索引代替常用的BTree索引,其持久化性能高于KahaDB,虽然默认的持久化方式还是KahaDB,但是LevelDB可能回事趋势。
在5.9版本还提供可基于LevelDB和zookeeper的数据复制方式,作为Master-Slave方式的首选数据复制方案。
zookeeper+replicated+Leveldb-store的主从集群原理说明:
使用Zookeeper集群注册所有的ActiveMQ Broker但是只有一个Broker可以提供服务它将被视为Master,其他的Broker处于待机状态被视为Slave。
如果Master因为故障而不能提供服务Zookeeper会从Slave中选举出一个Broker充当Master。
Slave连接Master并同步他们的存储状态,Slave不接收客户端连接。所有的存储操作都将被复制到连接至Master的Slaves。如果Master宕机得到了最新更新的Slave会称为Master。故障那个节点在恢复后会重新假如到集群中并连接Master进入Slave模式。
所有需要同步的消息操作都将等待存储状态被复制到其他节点的操作完成才能完成。
所以,如果你配置了replicas=3,那么法定大小(3/2)+1=2.Master将会存储并更新然后等待(2-1)=1个Slave存储和更新完成才汇报success,至于为什么是2-1,单点故障,(确保一般以上的节点保存了数据)
有一个node要作为观察者存在。当一个新的Master被选中,你需要至少保障一个法定node在线以能够找到最新状态的node,这个node才可以称为新的Master。因此,推荐运行至少3个replica nodes以防止node失败后服务中断。
2、为什么要异步投递?
ActiveMQ支持同步、异步两种发送模式将消息发送到broker,模式的选择对发送延时有巨大的影响。producer能达到怎样的产出率(产出率=发送数据总数量|时间)主要受发送延时的影响,使用异步发送可以显著的提高发送的性能。
ActiveMQ默认使用异步发送的模式:除非明确指定使用同步发送的方式或者在未使用事物的前提下发送持久化的消息,这两种情况都是同步发送的。
如果你没有使用事物且发送的是持久化的消息,每一次发送都是同步发送的且会阻塞producer直到broker返回一个确认,表示消息已经被安全的持久化到磁盘。确认机制提供了消息安全的保障,但同时会阻塞客户端带来了很大的延迟。
很多高性能的应用,允许在失败的情况下有少量数据丢失。如果你的应用满足这个特点,你可以使用异步发送来提高生产率,即使发送的是持久化的消息。
2.1、异步投递的优点:
- 它可以最大化producer端的发送效率。我们通常在发送消息量比较密集的情况下使用异步发送,它可以很大的提升producer性能;
2.2、异步投递的缺点
- 就是需要消耗较多的client端内存同时也会导致broker端性能消耗增加;
- 此外它不能有效的确保消息发送成功与否。在userAsyncSend=true的情况下客户端需要容忍消息丢失的可能。
3、异步发送 怎么确认发送消息成功
异步发送丢失消息的场景是:生产者设置UserAsyncSend=true,使用producer.send(msg)持续发送消息。
由于消息不阻塞,生产者会认为所有send的消息被发送成功至MQ。
如果MQ突然宕机,此时生产者端内存中尚未被发送至MQ的消息都会丢失。
所以,正确的异步发送方法是需要接受回调的。
同步发送和异步发送的区别
- 1、同步发送等send不阻塞了就表示一定发送成功了
- 2、异步发送需要接收回执并由客户端再判断一次是否发送成功。
4、延迟投递和定时投递:
四大属性:
AMQ_SCHEDLED_DELAY -long -延迟投递的时间
AMQ_SCHEDLED_PERIOD -long-重复投递的时间间隔
AMQ_SCHEDLED_REPERT-int-重复投递次数
AMQ_SCHEDLED_CRON -string-cron表达式
- 要再activemq.xml中配置schedulerSupport属性未true
- java代码里面封装的辅助消息类型:ScheduledMessage
5、消费重试机制
官网:https://activemq.apache.org/redelivery-policy有消息重发的配置参数
1、具体那些情况会引发消息重发
- 1:Client用了transcations(ActiveMQ的事物)且session中调用了rollback()
- 2:Client用了transcations且commit()之前关闭或者没有commit
- 3、Client再CLENT_ACKNOWLEDGE的传递模式下,再session中调用了recover()
2、请说说消息重发时间间隔和重发次数吗
- 默认间隔:1秒 默认次数:6次
3、有毒消息poison ACK谈谈你的理解
- 一个消息被redeliveded超过默认的最大重发次数(默认6次)时,消费端会给MQ发送一个”poison ack“表示这个消息有毒,告诉broker不要再发了。这个时候broker会把消息放到DLQ(死信队列)
4、死信队列
一般生产环境中再使用MQ的时候设计两个队列:一个时核心业务队列,一个时死信队列。
核心业务队列,就是比如上图专门用来让订单系统发送订单消息的,然后另外一个死信队列就是用来处理异常情况的。
假如第三方物流系统故障了此时无法请求,那么仓储系统每次消费到一条订单消息,尝试通知发货和配货都会遇到对方的接口报错。此时仓储系统就可以把这条消息拒绝访问或者标准处理失败。一旦标志这条消息处理失败了之后,MQ就会把这条消息转入提前设置好的死信队列中。然后你会看到的就是,再第三方物流系统故障期间,所有订单消息全部处理失败,全部会转入死信队列。然后你仓储系统得专门有一个后台线程,健康第三方物流系统是否正常,能否请求的,不停的监视,一旦发现对方恢复正常,这个后台线程就从死信队列消费出来处理失败的订单,重新执行发货和配送的通知逻辑。
4.1策略:
4.1.1、SharedDeadLetterStrategy:
默认,将所有的DeadLetter保存再一个共享的队列中,这是ActiveMQ broker端默认的策略。
共享队列默认为”ActiveMQ.DLQ“,可以通过”deadLetterQueue“属性来设定。
4.2.2、IndividualDeadLetterStrategy:
把DeadLetter放入各自的死信通道中
对于Queue而言,死信通道的前缀默认为”ActiveMQ.DLQ.Queue“
对于Topic而言,死信通道的前缀默认为”ActiveMQ.DLQ.Topic“;
比如队列Order,那么它对应的死信通道为”ActiveMQ.DLQ.Queue.Order“
我们使用”queuePrefix“”topicPrefix“指定上述前缀。
默认情况下,无论Topic还是Queue,broker将使用Queue来保存DeaDLeader,即死信通道通常为Queue;不过开发者也可以指定为Topic。
将队列Order中出现的DeadLetter保存在DLQ.Order中,不过此时DLQ.Order为Topic
属性”useQueueForTopicMessages“,此指表示是否将Topic的DeadLetter保存道Queue中,默认为true
4.3、自动删除过期消息:
有时候需要直接删除过期消息而不需要发送到死信队列中,
“processExpired”表示是否将过期消息放入到死信队列,默认是false
4.4、非持久化消息发送到死信队列
默认情况下,ActiveMQ不会把非持久的死信息发送到死信队列中。
processNonPersistent表示是否将”飞持久化“消息放入死信队列,默认是false。
非持久化如果你想把非持久化的消息发送到死信队列中,需要设置属性”processNonPersistent=true“
4.5、如何保证消息不被重复消费呢?
造成的原因:网络延迟传输中,会造成进行MQ重试中,在重试过程中,可能会造成重复消费。
解决方案:
第一种办法:如果消息是做数据库的插入操作,给这个消息做一个唯一主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。
第二种办法:准备一个第三服务方来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将<id,message>以k-v形式写入redis。那消费者开始消费前,先去redis中查询有没有消费记录即可。(推荐这个)