ActiveMQ--高级特性和大厂常考重点
-
引入消息队列之后该如何保证其高可用性
- zookeeper +replicated-leveldbstore-store
-
异步投递Async Sends
- 异步投递
-
http://activemq.apache.org/ async-sends
-
说明
-
对于一个slowConsumer,使用同步发送消息可能出现Producer堵塞等情况,慢消费者适合使用异步发送。
-
- 是什么
-
不使用事务且消息是持久化的发送的消息使持久化的,本身没有使用事务会造成消息不能完全都成功还是都失败,并且持久化的消息(持久化到MQ内存中),此时的消息是同步的
-
- 官网配置
- 第一中开启异步投递的方式
-
第二种开启异步投递的方式
- 第三种开启异步投递的方式
-
- 第一中开启异步投递的方式
- 面试题追问
- 异步发送如何确认发送成功:
-
异步投递需要接收回调,才会确定是否成功
-
同步消息等send不阻塞了就代表发送成功了
- 异步发送的代码实现;
-
package com.model.YiBuTouDi; import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.activemq.ActiveMQMessageProducer; import org.apache.activemq.AsyncCallback; import javax.jms.*; import java.util.UUID; /** * Hello world! * */ public class JmsProducer { // 第一种开启异步投递的方法: public static final String ACTIVEMQ_URL="tcp://192.168.56.130:61616"; public static final String QUEUE_NAME="queue01"; public static void main(String[] args) throws JMSException { // 1.创建连接工厂,按照给定的url地址,采用默认的用户名和密码 ActiveMQConnectionFactory activeMQConnectionFactory=new ActiveMQConnectionFactory(ACTIVEMQ_URL); // 第二种开启异步投递的方式 activeMQConnectionFactory.setUseAsyncSend(true); // 2.创建连接工厂,获得连接connection并启动访问 Connection connection = activeMQConnectionFactory.createConnection(); connection.start(); // 3.创建会话 // 两个参数,第一交事务/第二个叫签收 Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); // 4.创建目的地(具体是队列还是主题topic) Queue queue = session.createQueue(QUEUE_NAME); // 5.创建消息的生产者 ActiveMQMessageProducer activeMQMessageProducer = (ActiveMQMessageProducer) session.createProducer(queue); // 6.通过使用messageProducer生产3条消息发送给MQ的队列里面 TextMessage textMessage=null; for (int i = 0; i < 3; i++) { // 7.创建消息,好比时学生按照要求给来老师写的问题 textMessage = session.createTextMessage("message----" + i); // 为消息设置id号标识 textMessage.setJMSMessageID(UUID.randomUUID().toString()+"======"); String msgID=textMessage.getJMSMessageID(); // 8.通过messageProducer发送给mq activeMQMessageProducer.send(textMessage, new AsyncCallback() { @Override public void onSuccess() { System.out.println(msgID+"发送成功了"); } @Override public void onException(JMSException exception) { System.out.println(msgID+"发送失败了"); } }); } // 9.关闭资源 activeMQMessageProducer.close(); session.close(); connection.close(); System.out.println("*生产者发送消息over******"); } }
-
-
- 异步投递
-
延迟投递和定时投递
- http://activemq.apache.org/delay-and-schedule-message- delivery.html
-
官网说明
-
四大属性
-
-
案例演示
-
要在activemq.xml中配置schedulerSupport属性为true
- schedulerSupport="true"
-
-
Java代码里面封装的辅助消息类型:ScheduledMessage
-
代码:
-
JmsProduce_ DelayAndSchedule
-
package com.model.YanChiHeDingShiTouDi; import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.activemq.ScheduledMessage; import org.springframework.scheduling.annotation.Scheduled; import javax.jms.*; /** * Hello world! * */ public class JmsProducer_DelayAndSchedule { public static final String ACTIVEMQ_URL="tcp://192.168.56.130:61616"; public static final String QUEUE_NAME="queue-delay"; public static void main(String[] args) throws JMSException { ActiveMQConnectionFactory activeMQConnectionFactory=new ActiveMQConnectionFactory(ACTIVEMQ_URL); Connection connection = activeMQConnectionFactory.createConnection(); connection.start(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); Queue queue = session.createQueue(QUEUE_NAME); MessageProducer messageProducer = session.createProducer(queue); long delay=3*1000; //延迟投递时间 long period=4*1000; //重复投递时间间隔 int repeat=5; //重复投递次数 for (int i = 0; i < 3; i++) { TextMessage textMessage = session.createTextMessage("delay--message----" + i); textMessage.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY,delay); textMessage.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_PERIOD,period); textMessage.setIntProperty(ScheduledMessage.AMQ_SCHEDULED_REPEAT,repeat); messageProducer.send(textMessage); } messageProducer.close(); session.close(); connection.close(); System.out.println("生产者发送消息over******"); } }
-
-
JmsConsumer_ DelayAndSchedule
-
package com.model.YanChiHeDingShiTouDi; import org.apache.activemq.ActiveMQConnectionFactory; import javax.jms.*; public class JmsConsumer_DelayAndSchedule { public static final String ACTIVEMQ_URL="tcp://192.168.56.130:61616"; public static final String QUEUE_NAME="queue-delay"; public static void main(String[] args) throws JMSException { // 1.创建连接工厂,按照给定的url地址,采用默认的用户名和密码 ActiveMQConnectionFactory activeMQConnectionFactory=new ActiveMQConnectionFactory(ACTIVEMQ_URL); // 2.创建连接工厂,获得连接connection并启动访问 Connection connection = activeMQConnectionFactory.createConnection(); connection.start(); // 3.创建会话 // 两个参数,第一交事务/第二个叫签收 Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); // 4.创建目的地(具体是队列还是主题topic) Queue queue = session.createQueue(QUEUE_NAME); // 5.创建消息的消费者 MessageConsumer consumer = session.createConsumer(queue); // 6.消费者消费信息 while(true){ TextMessage message = (TextMessage) consumer.receive(); if (message!=null){ System.out.println("message==="+message.getText()); }else { break; } } // 7.关闭资源 consumer.close(); session.close(); connection.close(); } }
-
-
-
-
- http://activemq.apache.org/delay-and-schedule-message- delivery.html
-
分发策略
-
ActiveMQ消费重试机制
- 生产案例的面试题
-
具体哪些情况会引起消息重发:
-
Client用了transactions且在session中调用了rollback()
-
Client用 了transactions且在调用commit()之前关闭或者没有commit
-
Client在CLIENT_ _ACKNOWLEDGE(签收机制)的传递模式下,在session中 调用了recover() (重试)
-
-
请说说消息重发时间间隔和重发次数吗?
- 间隔: 1
- 次数: 6
-
有毒消息PoisonACK谈谈你的理解:
-
一个消息被redelivedred(重发)超过默认的最大重发次数(默认6次)时,消费端会给MQ发送一-个”poison ack"表示这个消息有毒,告诉broker不要
再发了。这个时候broker会把这个消息放到DLQ(死信队列)。
-
-
记得给学生讲解2次,上述回答
-
- 官网 : https://activemq.apache.org/redelivery-policy.html
-
- 属性说明
- 案例验证与讲解
-
JmsProduce_ Redelivery
-
和前面一样,正常生产三条消息
-
-
JmsConsumer_ Redelivery
-
消费者开启事务,但是不提交,进行消费一次
- 消费一次,但是后台控制端 的中的三条消息不变,没有出队,造成重复消费
-
消费者在连续消费六次,重复消费。
-
当消费者在次消费,超过了默认的最大重发次数。将三条信息加入到DLQ(死信队列)中,消费者将无法在消费到这三条信息
-
-
故意出bug,默认调用6次后看情况,了 解死信队列
-
JmsConsumer_ Redelivery修改
-
修改最大重发次数,重发三次后就会将消息加入到,DLQ失信队列中
-
-
-
整合spring后如何使用,假如在工作中需要
-
- 生产案例的面试题
-
死信队列
-
http:/activemq.apache.org/message-redelivery-and-dlq-handling.html
- 是什么
-
死信队列的使用:处理失败的消息
-
*一般生产环境中在使用MQ的时候设计两个队列:一个是核心业务队列,- .个是死信队列。
-
*核心业务队列,就是比如上图专门用来让订单系统发送订单消息的,然后另外-一个死信队列就是用来处理异常情况的。
-
*假如第三方物流系统故障了此时无法请求,那么仓储系统每次消费到--条订单消息,尝试通知发货和配送都会遇到对方的接口报错。此时仓储系
统就可以把这条消息拒绝访问或者标志位处理失败。--旦标志这条消息处理失败了之后,MQ就会把这条消息转入提前设置好的-一个死信队列中。
然后你会看到的就是,在第三方物流系统故障期间,所有订单消息全部处理失败,全部会转入死信队列。然后你的仓储系统得专门有一一个后台线
程,监控第三方物流系统是否正常,能否请求的,不停的监视。- -旦发现对方恢复正常,这个后台线程就从死信队列消费出来处理失败的订单, .重新执行发货和配送的通知逻辑
-
-
activemq死信队列的配置介绍
-
SharedDeadLetterStrategy
-
IndividualDeadLetterStrategy
-
-
配置案例
-
自动删除过期消息
-
存放非持久消息到死队列中
-
-
-
-
-
如何保证消息不被重复消费呢?幂等性问题你谈谈(防止消息重复消费的问题)
- 网络延迟传输中,会造成进行MQ重试中,在重试过程中,可能会造成重复消费。
-
如果消息是做数据库的插入操作,给这个消息做一个唯一-主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。
-
如果上面两种情况还不行,准备- -个第 三服务方来做消费记录。以redis为例, 给消息分配-一个 全局id,只要消费过该消息,将<id,message>以
K-V形式写入redis.那消费者开始消费前,先去redis中查询有没消费记录即可。
- 网络延迟传输中,会造成进行MQ重试中,在重试过程中,可能会造成重复消费。