activemq - ack机制
疑问:
- 在写 demo 的时候,如果 client 被强制中断,消息来不及处理,这时候消息又出队列了,这样不是会产生严重的问题嘛?
- 一个会话中,可以同时处理一批数据,如果一条失败了,之前的也要求回滚的话,要怎么处理?
- 获取一个消息之后,发现程序无法处理这条消息,想要退还回去,该怎么办?
方案:
实际上,activemq 中还有一个消息确认机制。
消息被 consumer 接收之后,仅仅只是成功收到消息而已;
broker 只有接收到 ACK 指令,才会认为消息被正确的消费了;
在写 demo 的时候,一般设置 AUTO_ACKNOWLEDGE,消息确认过程被自动处理了。
要解决前面提出的这些问题,别让程序自动处理,我们需要手动处理,即可解决问题。
brocker 是啥?
就是一个 MQ 服务实例,消息队列服务端。
可以在代码中单独启动 brocker,与使用安装包启动的程序相比,只包含核心部分,缺少日志、数据存储等功能。
此外,像是 RabbitMQ,有集群的概念,此时 borcker 就是集群的一个节点。
public class LocalBroker {
public static void main(String[] args) throws Exception {
BrokerService brokerService = new BrokerService();
brokerService.setUseJmx(true);
brokerService.addConnector("tcp://localhost:61616");
brokerService.start();
}
}
消息确认机制(ACK)
ACK(Acknowledgement)是确认字符的缩写,JMS API 中约定了四种 ACK_MODE(javax.jms.Session);
* 自动ACK(AUTO_ACKNOWLEDGE):这是默认的确认模式,只要消息被发送或接收,即视为完成;
* 手动ACK(CLIENT_ACKNOWLEDGE):对一条消息进行 ACK,ACK 的时候,会话上的所有消息,都会被 ACK;
* 批量ACK(DUPS_OK_ACKNOWLEDGE):允许批量确认消息,因为线程不安全,可能会重复消费,该种方式很少使用到;
* 事务ACK(SESSION_TRANSACTED):事务提交的时候确认;
此外 AcitveMQ 补充了一个自定义的 ACK_MODE(org.apache.activemq.ActiveMQSession)
- 单条确认(INDIVIDUAL_ACKNOWLEDGE):对一条消息进行 ACK,只会 ACK 那一条消息;
手动 ACK
有 3 个 ACK 的方法,只要关注第一个即可;
后面两个方法,需要特殊写法才能调用,用的机会并不多。
- message.acknowledge(),
- ActiveMQMessageConsumer.acknowledege(),
- ActiveMQSession.acknowledge();
class Test{
public void a(){
ActiveMQSession mqSession = (ActiveMQSession) session;
activeMQSession.acknowledge();
ActiveMQMessageConsumer consumer = (ActiveMQMessageConsumer) mqSession.createConsumer(topic);
consumer.acknowledge();
}
}
ACK_TYPE
client 与 broker 在交换 ACK 指令的时候,还需要告知 ACK_TYPE,ACK_TYPE 表示此确认指令的类型,不同的 ACK_TYPE 将传递着消息的状态,broker 可以根据不同的 ACK_TYPE 对消息进行不同的操作。
在 JMS API 中并没有定义 ACT_TYPE,因为它通常是一种内部机制,并不会面向开发者,简单了解一下即可。
- DELIVERED_ACK_TYPE = 0 消息"已接收",但尚未处理结束
- STANDARD_ACK_TYPE = 2 "标准"类型,通常表示为消息"处理成功",broker端可以删除消息了
- POSION_ACK_TYPE = 1 消息"错误",通常表示"抛弃"此消息,比如消息重发多次后,都无法正确处理时,消息将会被删除或者DLQ(死信队列)
- REDELIVERED_ACK_TYPE = 3 消息需"重发",比如consumer处理消息时抛出了异常,broker稍后会重新发送此消息
- INDIVIDUAL_ACK_TYPE = 4 表示只确认"单条消息",无论在任何ACK_MODE下
- UNMATCHED_ACK_TYPE = 5 BROKER间转发消息时,接收端"拒绝"消息
- EXPIRED_ACK_TYPE = 6 消息发生过程中已经过期
package org.apache.activemq.command;
public class MessageAck extends BaseCommand {
/**
* Used to let the broker know that the message has been delivered to the
* client. Message will still be retained until an standard ack is received.
* This is used get the broker to send more messages past prefetch limits
* when an standard ack has not been sent.
*/
public static final byte DELIVERED_ACK_TYPE = 0;
/**
* The standard ack case where a client wants the message to be discarded.
*/
public static final byte STANDARD_ACK_TYPE = 2;
/**
* In case the client want's to explicitly let the broker know that a
* message was not processed and the message was considered a poison
* message.
*/
public static final byte POISON_ACK_TYPE = 1;
/**
* In case the client want's to explicitly let the broker know that a
* message was not processed and it was re-delivered to the consumer
* but it was not yet considered to be a poison message. The messageCount
* field will hold the number of times the message was re-delivered.
*/
public static final byte REDELIVERED_ACK_TYPE = 3;
/**
* The ack case where a client wants only an individual message to be discarded.
*/
public static final byte INDIVIDUAL_ACK_TYPE = 4;
/**
* The ack case where a durable topic subscription does not match a selector.
*/
public static final byte UNMATCHED_ACK_TYPE = 5;
/**
* the case where a consumer does not dispatch because message has expired inflight
*/
public static final byte EXPIRED_ACK_TYPE = 6;
}