1.1 消费者
消费者通过 Delivery Acknowledgements
确保数据的可靠投递,Delivery Acknoledgements
包含两种模式,分别为自动和手动。
1.1.1 自动Acknowledgement
Channel#basicConsume(autoAck=false)
,这种模式下,consumer并未真正的发送Ack到Broker,即消费者不做任何操作,Broker便自动确认,其确认的具体时机为消息写入TCP Socket。所以这种模式下,并不能保证数据的安全投递(Delivery,指Broker到消费者)。
1.1.2 手动Acknowledgement
Channel#basicConsume(autoAck=false)
,这种模式下,consumer需要手动发送Ack,其确认的时机由用户决定,一般是处理完消息后发送Ack。
1.1.2.1 手动Acknowledgement原理
Broker向Consumer投递消息时,每条消息都会附带一个Devlivery Tag
(一个单调递增的整数),消费者在对某条消息进行手动Ack时,需要携带其Devlivery Tag
,Broker便是根据Devlivery Tag
识别Ack所对应的消息的。
1.1.2.2 手动Acknowledgement类型
-
Channel#basicAck
:表明消息已经受到并被正确处理,Broker可将该消息删除。 -
Chanenl#basicReject
:表明消息已经收到但是未被处理,Broker可根据Consumer发送的reject消息中包含的requeue
参数决定将消息删除或是将消息重新入队。应用场景:有多个消费者实例,若当前消费者繁忙,则可
reject
当前消息,并令其重新入队。然后由其他消费者实例处理。 -
Channel#basicNack
:同basicReject
方法类似,但是支持批量拒绝消息。
1.1.2.3 批量Ack
Consumer每次进行Ack,都会向Broker发送一次请求,为减轻带宽压力,Consumer支持批量Ack,示例代码如下:
channel.basicAck(deliveryTag, multiple=false)
注:
Chanenl#basicReject
不支持批量Ack。
1.2 生产者
生产者可通过事务
或者confirm
,确保消息的可靠发布(publish),其中事务
机制性能较差,并不推荐使用。
1.2.1 confirm方案
1.2.1.1 基本原理
可使用channel#confirmSelect
启用生产者confirm
。
生产者confirm
方案的原理借鉴于消费者的acknowledgement
,Producer向Broker发布消息时,每条消息都会附带一个Devlivery Tag
(一个单调递增的整数),Broker在收到消息后会向Producer返回Acknowledgement
(包括ack或者nack),Acknowledgement
中同样会携带收到的消息中包含的Devlivery Tag
生产者可根据Devlivery Tag
识别对应的消息。
confirm的完整流程如下图所示:
1.2.1.2 生产者处理confirm信息
生产者处理confirm信息的策略有如下几种
- 逐条发送,同步等待,处理Confirm信息
while (thereAreMessagesToPublish()) {
byte[] body = ...;
BasicProperties properties = ...;
channel.basicPublish(exchange, queue, properties, body);
// uses a 5 second timeout
channel.waitForConfirmsOrDie(5_000);
}
- 批量发送,同步等待,处理批量Confirm信息
int batchSize = 100;
int outstandingMessageCount = 0;
while (thereAreMessagesToPublish()) {
byte[] body = ...;
BasicProperties properties = ...;
channel.basicPublish(exchange, queue, properties, body);
outstandingMessageCount++;
if (outstandingMessageCount == batchSize) {
channel.waitForConfirmsOrDie(5_000);
outstandingMessageCount = 0;
}
}
if (outstandingMessageCount > 0) {
channel.waitForConfirmsOrDie(5_000);
}
- 异步处理confirm信息
该方案更为常用,通过为channel绑定ConfirmListener回调函数,来处理confirm信息。
Channel channel = connection.createChannel();
channel.confirmSelect();
channel.addConfirmListener((sequenceNumber, multiple) -> {
// code when message is confirmed
}, (sequenceNumber, multiple) -> {
// code when message is nack-ed
});
1.2.1.3 生产者处理return信息
根据上述的confirm流程可以看出,若Message发布到Exchange之后,未正确路由到队列,Broker可将该Message返回给Producer,Producer可使用ReturnListener接受返回的消息,然后作进一步处理,示例代码如下:
channel.addReturnListener(new ReturnListener() {
public void handleReturn(int replyCode,
String replyText,
String exchange,
String routingKey,
AMQP.BasicProperties properties,
byte[] body)
throws IOException {
...
}
});
1.3 Broker
Broker为保证数据可靠性,可使用durable queue
,其会将数据持久化到磁盘。
注:生产者发送数据时,需声明
Delivery mode
为persistent,若不声明,即便其位于durable queue
中,当Broker重启后也会丢失。
为保证生产者的消息可靠的发布到Broker,Broker是在将所有消息路由到所有匹配队列,并落盘之后才会向生产者发送Ack。
为保证数据从Broker可靠的投递到消费者,Broker支持消费者打回数据,并重新入队,重新投递。