【笔记于学习尚硅谷课程所作】
11、高级特性和大厂常考重点
11.1 异步投递与其回调函数
ActiveMQ默认使用异步发送的模式:除非明确指定使用同步发送的方式或者在未使用事务的前提下发送持久化的消息,这两种情况都是同步发送的。
如果你没有使用事务且发送的是持久化的消息,每一次发送都是同步发送的且会阻塞producer直到broker返回一个确认,表示消息已经被安全的持久化到磁盘。确认机制提供了消息安全的保障,但同时会阻塞客户端带来了很大的延时。但异步消息并不保证能发送成功
在快生产、慢消费的情况下使用
1.设置异步投递
//在生产者中,创建ActiveMQConnectionFactory对象后,执行以下方法
activeMQConnectionFactory.setuseAsyncSend(true);
2.使用回调函数确认异步发送成功
//在生产者代码中使用Asynccallback
public class JmsProduce{
public static final string ACTIVEMQ_URL="tcp://192.168.111.136:61616";
public static final string QUEUE_NAME="queue02";
public static void main(String[] args) throws JMSException{
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnection actory(ACTIVEMQ_URL);
activeMQConnectionFactory.setuseAsyncSend(true);
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);
//生产者的类使用ActiveMQMessageProducer
ActiveMQMessageProducer activeMQMessageProducer =(ActiveMQMessageProducer)session.createProducer(queue);
TextMessage message=null;
for(inti=1;i<=3;i++){
message = session.createTextMessage("message--"+i);
//加入ID用来确认消息是否发送成功
message.setJMSMessageID(UUID.randomUUID().tostring()+"--id");
string msgID = message.getIMSMessageID();
//编写回调函数确认消息是否发送成功
activeMQMessageProducer.send(message, new Asynccallback(){
@override
public void onSuccess(){
System.out.println(msgID+"has been ok send");
}
@Override
public void onException(JMSException exception){
System.out.println(msgID+"fail to send to mq");
}
});
}
//9、关闭资源
messageProducer.close();
session.close();
connection.close();
System.out.println("*****消息发布到MQ完成");
}
}
11.2延迟投递和定时投递
四大属性
属性名 | 类型 | 含义 |
---|---|---|
AMQ_SCHEDULED_DELAY | long | 延迟投递的时间 |
AMQ_SCHEDULED_PERIOD | long | 重复投递的时间间隔 |
AMQ_SCHEDULED_REPEAT | int | 重复投递次数 |
AMQ_SCHEDULED_CRON | String | Cron表达式 |
1.修改activemq.xml配置
<!--将schedulerSupport设置为true-->
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="mybroker" dataDirectory="${activemq.data}" schedulerSupport="true">
2.修改生产者代码
//大部分代码和上述生产者代码相同,只写不同部分
public class JmsProduce{
public static final string ACTIVEMQ_URL="tcp://192.168.111.136:61616";
public static final string QUEUE_NAME="queue03";
public static void main(String[] args) throws JMSException{
//...
long delay = 3*1000; //延迟投递的时间
long period = 4*1000; //重复投递的时间间隔
int repeat = 5; //重复投递次数
for(inti=1;i<=3;i++){
TextMessage message = session.createTextMessage("msg---" + i);
//添加属性
message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY,delay);
message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_PERIOD,period) ;
message.setIntProperty(ScheduledMessage.AMQ_SCHEDULED_REPEAT,repeat);
messageProducer.send(message);
}
//...
}
}
11.3 消费重试机制
1.具体哪些情况回引起消息重发
- Client用了transactions且在session中调用了rollback()
- Client用了transactions且在调用commit()之前关闭或者没有commit
- Client在CLIENT_ ACKNOWLEDGE的传递模式下,在session中 调用了recover()
2.消息重发时间间隔和重发次数:默认间隔:1,次数:6(第一次不算,重发6次,总共发7次)
3.有毒消息(PoisonACK):一个消息被redelivedred超过默认的最大重发次数(默认6次)时,消费端会给MQ发送一个” poison ack"表示这个消息有毒,告诉broker不要再发了。这个时候broker会把这个消息放到DLQ(死信队列)。
4.在消费者中自定义消息重发次数
RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
redeliveryPolicy.setMaximumRedeliveries(3);//设置重发次数为3
5.整合Spring时自定义消息重发机制属性
<!-- 定义ReDelivery(重发机制)机制-->
<bean id="activeMQRedeliveryPolicy" class="org.apache.activemq.RedeliveryPolicy">
<!--是否在每次尝试重新发送失败后,增长这个等待时间-->
<property name="useExponentialBack0ff" value="true"></property>
<!--重发次数,默认为6次这里设置为3次-->
<property name="maximumRedeliveries" value="3"></property>
<!--重发时间间隔,默认为1秒-->
<property name="initialRedeliveryDelay" value="1000"></property>
<!--第一次失败后重新发送之前等待500毫秒,第二次失败再等待500*2毫秒,这里的2就是value -->
<property name="backOffMultiplier" value="2"></property>
<!--最大传送延迟,只在useExponentialBack0ff. 为true时有效(V5.5) ,
假设首次重连间隔为10ms倍数为2,那么第二次重连时间间隔为20ms,第三次重连时间间隔为40ms,
当重连时间间隔大的最大重连时间间隔时,以后每次 重连时间间隔都为最大重连时间间隔。-->
<property name="maximumRedeliveryDelay" value=" 1000" ></property>
</bean>
<!--创建连接工厂-->
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionbactory">
<property name="brokerURL" value="tcp://localhost:61616" ></property>
<!--引用重发机制-->
<property name="redeliveryPolicy" ref="activeMQRedeliveryPolicy"/>
</bean>
6.消息重发机制的具体属性说明(了解)
- collisionAvoidanceFactor:设置防止冲突范围的正负百分比,只有启用useCollisionAvoidance参数时才生效。也就是在延迟时间上再加一个时间波动范围。默认值为0.15
- maximumRedeliveries:最大重传次数,达到最大重连次数后抛出异常。为-1时不限制次数,为0时表示不进行重传。默认值为6。
- maximumRedeliveryDelay:最大传送延迟,只在useExponentialBackOff为true时有效(V5.5),假设首次重连间隔为10ms,倍数为2,那么第二次重连时间间隔为20ms,第三次重连时间间隔为40ms,当重连时间间隔大的最大重连时间间隔时,以后每次重连时间间隔都为最大重连时间间隔。默认为-1。
- initialRedeliveryDelay:初始重发延迟时间,默认1000L
- redeliveryDelay:重发延迟时间,当initialRedeliveryDelay=0时生效, 默认1000L
- useCollisionAvoidance:启用防止冲突功能,默认false
- useExponentialBackOff:启用指数倍数递增的方式增加延迟时间,默认false
- backOffMultiplier:重连时间间隔递增倍数,只有值大于1和启用useExponentialBackOff参数时才生效。默认是5
11.4 防止重复调用
网络延迟传输中,会造成进行MQ重试中,在重试过程中,可能会造成重复消费。
如果消息是做数据库的插入操作,给这个消息做一个唯一主键, 那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。
如果上面情况还不行,准备一个第三服务方来做消费记录。以redis为例, 给消息分配一个全局id,只要消费过该消息,将<id,message>以K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可。