Pulsar Consumer(消费者)

目录

1、消息的两种接收方式

2、消息监听器

3、消息的确认

4、消息的否定确认

5、消息的超时确认

6、重试消息主题( retry letter topic)

7、死亡消息主题(dead letter topic)​


        消费者(consumer)是通过订阅附加到一个消息主题,然后接收消息的程序。// 订阅+主题

        消费者向代理发送许可请求获取消息。消费者端有一个队列,用于接收从代理推送的消息。可以使用 receiverQueueSize 参数配置队列的大小(默认大小为1000)。每调用一次 receive() 方法,会从缓存队列中出队一条消息。

1、消息的两种接收方式

        从代理(brokers)接收消息可以是同步的也可以是异步的:

ModeDescription

Sync receive

// 同步

A sync receive is blocked until a message is available.

Async receive

// 异步

An async receive returns immediately with a future value—for example, a CompletableFuture in Java—that completes once a new message is available.

// 异步接收立即返回一个future值,例如,Java中的CompletableFuture,一旦有新消息可用,它就会完成。

2、消息监听器

        客户端为消费者提供侦听器的实现。例如,Java 客户端提供 MessageListener 接口。在此接口中,每当接收到新消息时,都会调用 received() 方法。

3、消息的确认

        消费者在成功消费消息后会向代理(broker)发送确认请求。消息(message )将被永久存储,注意,只有在所有订阅者都确认后才会被删除。如果要存储消费者已消费的消息,则需要配置消息的保留策略。

        对于批量处理的消息,可以启用批量索引确认机制,以避免将已确认的消息重复发送给消费者。有关批量索引确认的详细信息,请参阅批量处理文档

        消费者可以通过以下两种方式之一确认消息:

  1. 单独确认。消费者每消费一条消息,都会向代理发送确认请求。
  2. 累计确认。消费者只确认其收到的最后一条消息。确认后,Stream 流中所有的消息都不会重新传递给该消费者。

        单独确认消息,可以使用下边 API

consumer.acknowledge(msg);

        累计确认消息,可以使用下边 API:

consumer.acknowledgeCumulative(msg);

注意事项

        累积确认不能用于共享订阅类型( Shared subscription type),因为共享订阅类型涉及多个可以访问同一订阅的消费者。在共享订阅类型中,消息是单独确认的。

4、消息的否定确认

        否定确认机制允许消费者向代理发送通知,指示消费者未处理消息。当消费者未能处理消息并需要重新处理它时,消费者会向代理(broker)发送否定确认(nack),触发代理将此消息重新传递给消费者。

        消费者也可以单独的或累积的对消息进行否定确认,这取决于消费的订阅模式

  • 在 Exclusive(独占)和 Failover subscription(容错订阅)模式中,消费者只会对他们收到的最后一条消息进行否定确认。
  • 在 Shared 和 Key_Shared 订阅模式中,消费者可以单独的对每一条消息进行否定确认。

        需要注意的是,对有排序的订阅模式(如 Exclusive、Failover 和 Key_Shared)的否定确认可能会导致重新发送的消息超出它原来的顺序。

        如果要对消息使用否定确认,请确保在超时确认之前对其进行否定确认// 虽然都会触发消息重传,但超时确认后,否定确认就无意义了

Consumer<byte[]> consumer = pulsarClient.newConsumer()
                .topic(topic)
                .subscriptionName("sub-negative-ack")
                .subscriptionInitialPosition(SubscriptionInitialPosition.Earliest)
                .negativeAckRedeliveryDelay(2, TimeUnit.SECONDS) // the default value is 1 min
                .subscribe();

Message<byte[]> message = consumer.receive(); // 消费消息

// call the API to send negative acknowledgement,在超时之前进行否定确认
consumer.negativeAcknowledge(message); // 否定确认

message = consumer.receive();
consumer.acknowledge(message);

        可以通过设置传递消息的重试次数来设置消息重传的不同时延:

Use the following API to enable Negative Redelivery Backoff.

Consumer<byte[]> consumer = pulsarClient.newConsumer()
        .topic(topic)
        .subscriptionName("sub-negative-ack")
        .subscriptionInitialPosition(SubscriptionInitialPosition.Earliest)
        .negativeAckRedeliveryBackoff(MultiplierRedeliveryBackoff.builder()
            .minDelayMs(1000)
            .maxDelayMs(60 * 1000)
            .build())
        .subscribe();

        消息重传的时延如下所示:// 时延会随着重传次数的增加而增加

Redelivery countRedelivery delay
110 + 1 seconds // 11s
210 + 2 seconds // 12s
310 + 4 seconds
410 + 8 seconds
510 + 16 seconds
610 + 32 seconds
710 + 60 seconds
810 + 60 seconds

注意事项

        如果启用了批量处理,则同一批量中的所有消息都会重新传递给消费者。

5、消息的超时确认

        超时确认机制可以为客户端跟踪未确认消息设置一个时间范围。如果客户端在规定(ackTimeout)时间内没有发送确认消息,那么客户端会向代理(broker)发送否定确认请求,触发 broker 重传。

        如果客户端在规定时间内不发送确认消息,或者使用定时任务在超时时间内去检查超时确认的消息,都可以通过配置超时确认机制来触发消息重传

        超时确认机制可以通过设置重试次数来实现重传消息的不同时延。

        如果要使用重传机制,可以使用以下API:

consumer.ackTimeout(10, TimeUnit.SECOND)
        .ackTimeoutRedeliveryBackoff(MultiplierRedeliveryBackoff.builder() // 启用消息重传
        .minDelayMs(1000)
        .maxDelayMs(60000)
        .multiplier(2).build())

        消息重传的时延如下所示:// 跟否定确认的消息重传时延是一样的,同一个机制

Redelivery countRedelivery delay
110 + 1 seconds
210 + 2 seconds
310 + 4 seconds
410 + 8 seconds
510 + 16 seconds
610 + 32 seconds
710 + 60 seconds
810 + 60 seconds

注意事项

        与超时确认相比,推荐优先选择确认否定。首先,超时时间很难确定,其次,当消息处理时间超过规定的时间时,超时确认会通知代理会重新发送消息,但这些消息可能并不需要重新被消费。

        Use the following API to enable acknowledgement timeout(确认超时).

Consumer<byte[]> consumer = pulsarClient.newConsumer()
                .topic(topic)
                .ackTimeout(2, TimeUnit.SECONDS) // the default value is 0
                .ackTimeoutTickTime(1, TimeUnit.SECONDS)
                .subscriptionName("sub")
                .subscriptionInitialPosition(SubscriptionInitialPosition.Earliest)
                .subscribe();

Message<byte[]> message = consumer.receive();

// wait at least 2 seconds
message = consumer.receive();
consumer.acknowledge(message);

6、重试消息主题( retry letter topic)

        “retry letter” 主题,会存储消费失败的消息,并在之后尝试重新消费。通过这个方式,可以自定义消息重传的时间间隔。订阅原始主题的消费者也会自动订阅 “retry letter” 主题。一旦 “retry letter” 主题中的消息达到最大重传次数,该消息将会被移动到死亡消息主题中,死亡消息主题中的消息需要手动进行处理。

        下图说明了重试消息主题的概念:

        使用 “retry letter” 主题的目的与使用消息延迟传递的目的不同,尽管两者都传递消息。“retry letter” 主题在消息消费失败后,通过消息重传机制,确保关键数据不会丢失,而延迟消息传递是为了在指定的延迟时间传递消息。

        默认情况下,Pulsar 会禁用自动重传。你可以将 enableRetry 设置为 true,对消费者启用自动重传。

        使用以下 API 从重传消息主题中消费消息。当消息重传次数达到最大重传次数(maxRedeliverCount)后,该消息会被移动到死亡消息主题中:

Consumer<byte[]> consumer = pulsarClient.newConsumer(Schema.BYTES)
                .topic("my-topic")
                .subscriptionName("my-subscription")
                .subscriptionType(SubscriptionType.Shared)
                .enableRetry(true) // 开启自动重试
                .deadLetterPolicy(DeadLetterPolicy.builder()
                        .maxRedeliverCount(maxRedeliveryCount)
                        .build())
                .subscribe();

“retry letter” 主题默认使用以下格式:

// 主题-订阅名称-RETRY
<topicname>-<subscriptionname>-RETRY

        也可以使用 Java client 指定自己的 “retry letter” 主题名称:

Consumer<byte[]> consumer = pulsarClient.newConsumer(Schema.BYTES)
        .topic("my-topic")
        .subscriptionName("my-subscription")
        .subscriptionType(SubscriptionType.Shared)
        .enableRetry(true)
        .deadLetterPolicy(DeadLetterPolicy.builder()
                .maxRedeliverCount(maxRedeliveryCount)
                .retryLetterTopic("my-retry-letter-topic-name") //重试主题
                .build())
        .subscribe();

        “retry letter” 主题中的消息包含一些由客户端自动创建的特殊属性

Special propertyDescription

REAL_TOPIC

//真正主题

The real topic name.

ORIGIN_MESSAGE_ID

//原始消息ID

The origin message ID. It is crucial for message tracking.

// 对于消息跟踪非常关键

RECONSUMETIMES

//重新消费次数

The number of retries to consume messages.

DELAY_TIME

//延迟时间

Message retry interval in milliseconds.

// 消息重试间隔(毫秒)

例如:

REAL_TOPIC = persistent://public/default/my-topic
ORIGIN_MESSAGE_ID = 1:0:-1:0
RECONSUMETIMES = 6
DELAY_TIME = 3000

        使用以下 API 将消息存储在重传队列中:

consumer.reconsumeLater(msg, 3, TimeUnit.SECONDS);

        使用以下 API 为 Reconsumerater 函数添加自定义属性。在下一次尝试消费时,可以通过 message#getProperty 获取自定义属性。

Map<String, String> customProperties = new HashMap<String, String>();
customProperties.put("custom-key-1", "custom-value-1");
customProperties.put("custom-key-2", "custom-value-2");
consumer.reconsumeLater(msg, customProperties, 3, TimeUnit.SECONDS);

注意事项

        当前,在共享订阅类型中启用了 “retry letter” 主题。

        与否定确认相比,“retry letter” 主题更适合配置了重试时间间隔且需要进行大量消息重传的场景。因为 “retry letter” 主题中的消息持久化在 BookKeeper 中(broker中),而否定确认后需要重传的消息缓存在客户端。// 两者存储的方式不一样

7、死亡消息主题(dead letter topic)

        死亡消息主题(DLQ)允许继续使用消费未成功的消息。存储无法被消费的消息的主题,称为死亡消息主题(dead letter topic)。你可以决定如何处理死亡消息主题中的消息。

        在 Java 客户端中启用默认的死亡消息主题。

Consumer<byte[]> consumer = pulsarClient.newConsumer(Schema.BYTES)
                .topic("my-topic")
                .subscriptionName("my-subscription")
                .subscriptionType(SubscriptionType.Shared)
                .deadLetterPolicy(DeadLetterPolicy.builder() // 启用死信主题
                      .maxRedeliverCount(maxRedeliveryCount)
                      .build())
                .subscribe();

        死亡消息主题的默认格式:

// 主题-订阅名称-DLQ
<topicname>-<subscriptionname>-DLQ

        你也可以使用 Java client 自定义死亡消息主题的名字:

Consumer<byte[]> consumer = pulsarClient.newConsumer(Schema.BYTES)
                .topic("my-topic")
                .subscriptionName("my-subscription")
                .subscriptionType(SubscriptionType.Shared)
                .deadLetterPolicy(DeadLetterPolicy.builder()
                      .maxRedeliverCount(maxRedeliveryCount)
                      .deadLetterTopic("my-dead-letter-topic-name") // 定义死信主题的名字
                      .build())
                .subscribe();

        默认情况下,死亡消息主题不会被订阅。如果不能实时订阅死亡消息主题,那么可能会丢失这些消息。如果需要自动创建 DLQ 主题的初始化订阅,可以指定 initialSubscriptionName 参数。但是如果设置了此参数,代理的 allowAutoSubscriptionCreation 被禁用, DLQ 的生产者也会创建失败。

Consumer<byte[]> consumer = pulsarClient.newConsumer(Schema.BYTES)
                .topic("my-topic")
                .subscriptionName("my-subscription")
                .subscriptionType(SubscriptionType.Shared)
                .deadLetterPolicy(DeadLetterPolicy.builder()
                      .maxRedeliverCount(maxRedeliveryCount)
                      .deadLetterTopic("my-dead-letter-topic-name")
                      .initialSubscriptionName("init-sub") // 创建自动订阅
                      .build())
                .subscribe();

        DLQ 主题的消息可以进行重传,重传机制由超时确认、否定确认、重试消息主题等触发。

注意事项

        目前,已经在 Shared 和 Key_Shared 订阅类型中启用死亡消息主题。

点击回到首页

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值