20200202 ActiveMQ 11. 高级特性和大厂常考重点
ActiveMQ 11. 高级特性和大厂常考重点
11.1. 引入消息队列之后该如何保证其高可用性
持久化、事务、签收、 以及带复制的 Leavel DB + zookeeper 主从集群搭建
11.2. 异步投递Async Sends
对于一个Slow Consumer,使用同步发送消息可能出现Producer堵塞的情况,慢消费者适合使用异步发送
11.2.1. 是什么
ActiveMQ支持同步,异步两种发送的模式将消息发送到broker,模式的选择对发送延时有巨大的影响。producer能达到怎么样的产出率(产出率=发送数据总量/时间)主要受发送延时的影响,使用异步发送可以显著提高发送的性能。
ActiveMQ默认使用异步发送的模式:除非明确指定使用同步发送的方式或者在未使用事务的前提下发送持久化的消息,这两种情况都是同步发送的。
如果你没有使用事务且发送的是持久化的消息,每一次发送都是同步发送的且会阻塞producer知道broker返回一个确认,表示消息已经被安全的持久化到磁盘。确认机制提供了消息安全的保障,但同时会阻塞客户端带来了很大的延时。
很多高性能的应用,允许在失败的情况下有少量的数据丢失。如果你的应用满足这个特点,你可以使用异步发送来提高生产率,即使发送的是持久化的消息。
异步发送
它可以最大化producer端的发送效率。我们通常在发送消息量比较密集的情况下使用异步发送,它可以很大的提升Producer性能;不过这也带来了额外的问题,就是需要消耗更多的Client端内存同时也会导致broker端性能消耗增加;此外它不能有效的确保消息的发送成功。在userAsyncSend=true
的情况下客户端需要容忍消息丢失的可能。
11.2.2. 官网配置
有三种方法开启异步投递
-
Configuring Async Send using a Connection URI
cf = new ActiveMQConnectionFactory("tcp://locahost:61616?jms.useAsyncSend=true");
-
Configuring Async Send at the ConnectionFactory Level
((ActiveMQConnectionFactory)connectionFactory).setUseAsyncSend(true);
-
Configuring Async Send at the Connection Level
((ActiveMQConnection)connection).setUseAsyncSend(true);
11.2.3. 异步消息如何确定发送成功
异步发送丢失消息的场景是:生产者设置userAsyncSend=true,使用producer.send(msg)持续发送消息。
由于消息不阻塞,生产者会认为所有send的消息均被成功发送至MQ。
如果MQ突然宕机,此时生产者端内存中尚未被发送至MQ的消息都会丢失。
所以,正确的异步发送方法是需要接收回调的。
同步发送和异步发送的区别就在此
- 同步发送等send不阻塞了就表示一定发送成功了,
- 异步发送需要客户端回执并由客户端再判断一次是否发送成功
代码实现
// 创建生产者时,向上转型为ActiveMQMessageProducer
ActiveMQMessageProducer messageProducer = (ActiveMQMessageProducer) session.createProducer(queue);
// 调用带回调的send方法
messageProducer.send(textMessage, new AsyncCallback() {
@Override
public void onSuccess() {
}
@Override
public void onException(JMSException exception) {
}
});
11.3. 延时投递和定时投递
四大属性:
属性名 | 类型 | 描述 |
---|---|---|
AMQ_SCHEDULED_DELAY | long | 延迟投递的时间 |
AMQ_SCHEDULED_PERIOD | long | 重复投递的时间间隔 |
AMQ_SCHEDULED_REPEAT | int | 重复投递次数 |
AMQ_SCHEDULED_CRON | String | Cron表达式 |
11.3.1. 案例演示
-
功能默认没开启,在配置文件activemq.xml中设置定时器开关
schedulerSupport
为 true -
Java 代码中封装的辅助消息类型
ScheduleMessage
long delay = 3 * 1000 ;
long perid = 4 * 1000 ;
int repeat = 7 ;
for (int i = 1; i < 4 ; i++) {
TextMessage textMessage = session.createTextMessage("delay msg--" + i);
// 消息每过 3 秒投递,每 4 秒重复投递一次 ,一共重复投递 7 次
textMessage.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY,delay);
textMessage.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_PERIOD,perid);
textMessage.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_REPEAT,repeat);
messageProducer.send(textMessage);
}
11.4. 分发策略
视频中没有讲
11.5. 消息重试机制
11.5.1. 具体哪些情况会引发消息重发
- Client用了transactions且在session中调用了rollback
- Client用了transactions且在调用commit之前关闭或者没有commit
- Client在CLIENT_ACKNOWLEDGE的传递模式下,在session中调用了recover
消息重发时间间隔和重发次数
间隔:1
次数:6
有毒消息Poison ACK
一个消息被redelivedred超过默认的最大重发次数(默认6次)时,消费的回个MQ发一个“poison ack”表示这个消息有毒,告诉broker不要再发了。这个时候broker会把这个消息放到DLQ(死信队列)。
11.5.2. 属性说明
11.5.3. 修改默认重试机制
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
redeliveryPolicy.setMaximumRedeliveries(3);
activeMQConnectionFactory.setRedeliveryPolicy(redeliveryPolicy);
11.6. 死信队列
Activemq中引入了“死信队列”(Dead Letter Queue) 的概念。即一条消息再被重发了多次后(默认为重发6次redeliveryCounter==6),将会被Activemq移入“死信队列”,开发人员可以在这个Queue中查看处理出错的消息,进行人工干预。
一般生产环境在使用MQ的时候设计两个队列:一个是核心队列,一个是死信队列。
11.6.1. 配置介绍
-
SharedDeadLetterStrategy
将所有的DeadLetter保存在一个共享的队列中,这是ActiveMQ broker端默认的策略。
共享队列默认为
ActiveMQ.DLQ
,可以通过deadLetterQueue
属性来设定。 -
IndividualDeadLetterStrategy
把DeadLetter放入各自的死信通道中,
对于Queue而言,死信通道的前缀默认为
ActiveMQ.DLQ.Queue
对于Topic而言,死信通道的前缀默认为
ActiveMQ.DLQ.Topic
使用queuePrefix、topicPrefix指定上述前缀
11.6.2. 配置案例
-
自动删除过期消息
-
存放非持久消息到死信队列中
11.7. 如何保证消息不被重复消费,幂等性的问题
如果消息是做数据库的插入操作,给这个消息一个唯一的主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据 。
如果不是,可以用redis 等的第三方服务,给消息一个全局 id ,只要消费过的消息,将 id ,message 以 K-V 形式写入 redis ,那消费者开始消费前,先去 redis 中查询有没消费的记录即可。