You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -244,6 +244,10 @@ Note that the properties used are direct Pulsar consumer properties.
244
244
[[pulsar-message-listener-container]]
245
245
==== Pulsar Message Listener Container
246
246
247
+
Now that we saw the basic interactions on the consumer side through `PulsarListener`, let us now dive into the inner workings of how `PulsarListener` interacts with the underlying Pulsar consumer.
248
+
Keep in mind that, for end-user applications, in most scenarios, we recommend using `PulsarListener` annotation directly for consuming from a Pulsar topic when using Spring for Apache Pulsar, as that model covers a broad set of application use cases.
249
+
However, it is important to understand how `PulsarListener` works internally and this section will go through those details.
250
+
247
251
As briefly mentioned above, the message listener container is at the heart of message consumption when using Spring for Apache Pulsar.
248
252
`PulsarListener` uses the message listener container infrastructure behind the scenes to create and manage the Pulsar consumer.
249
253
Spring for Apache Pulsar provides the contract for this message listener container through `PulsarMessageListenerContainer`.
@@ -336,6 +340,47 @@ public ConcurrentPulsarMessageListenerContainer(PulsarConsumerFactory<? super T>
336
340
Concurrency of more than `1` is only allowed on non-exclusive subscriptions (`failover`, `shared` and `key-shared`).
337
341
You can only have the default `1` for concurrency when you have an exclusive subscription mode.
338
342
343
+
Here is an example of enabling `concurrency` through the `PulsarListener` annotation for a `failover` subscription.
In the above listener, it is assumed that the topic `my-topic` has 3 partitions.
359
+
If it is a non-partitioned topic, then having concurrency set to `3`, will not do anything, you will simply get two idle consumers in addition to the main active one.
360
+
If the topic has more than 3 partitions, then messages will be load-balanced across the consumers that the container creates.
361
+
If you run this `PulsarListener`, you will see that messages from different partitions will be consumed through different consumers as implied by the thread name and consumer names printouts in the example code above.
362
+
363
+
**Note: When using the `Failover` subscription this way on partitioned topics, Pulsar guarantees message ordering.**
364
+
365
+
Here is another example of `PulsarListener`, but with `Shared` subscription and `concurrency` enabled.
In the example above, the `PulsarListener` creates 5 different consumers (once again, we are assuming that the topic has 5 partitions).
379
+
380
+
**Keep in mind that, in this version, there is no message ordering as `Shared` subscriptions do not guarantee any message ordering in Pulsar**
381
+
382
+
If you need message ordering and still want a shared subscription types, then you need to use the `Key_Shared` subscription type.
383
+
339
384
==== Consuming the Records
340
385
341
386
In this section, we are going to see how the message listener container enables both single record and batch based message consumption.
@@ -396,31 +441,159 @@ MANUAL;
396
441
```
397
442
398
443
`BATCH` acknowledgment mode is the default, but you can change it on the message listener container.
444
+
In the following sections, we will see how acknowledgment works when using both single and batch versions of `PulsarListener` and how they translate to the backing message listener container (and of course ultimately to the Pulsar consumer).
445
+
446
+
==== Automatic Message Ack in Single Record Mode
399
447
400
-
==== Message Ack in Single Record Mode
448
+
Let us revisit our basic single message based `PulsarListener`.
401
449
402
-
When consuming single records using `PulsarRecordMessageListener` and the default ack mode of `BATCH` is used, the framework waits for all the record received from the `batchReceive` call to process successfully and then call the `acknowledge` method on the Pulsar Consumer.
It is natural to wonder, how acknowledgment works when using `PulsarListener`, espcially if you are familiar with Pulsar consumer directly.
461
+
The answer comes down to the message listener container as that is the central place in Spring for Apache Pulsar which coordinates all the consumer related activities.
462
+
463
+
Assuming you are not overriding the default behavior, this is what happens behind the scenes when using the above `PulsarListener`.
464
+
465
+
. First, the Listener container receives messages as batch from the Pulsar consumer.
466
+
. The received messages are handed down to `PulsarListener` one message at a time
467
+
. When all the records are handed down to the listener method and successfully processed, the container will acknowledge all the messages from the original bach receive.
468
+
469
+
This is the normal flow. If any record from the original batch received, throws an exception, Spring for Apache Pulsar will track them separately.
470
+
When all the records from the batch are processed, then Spring for Apache Pulsar will acknowledge all the succesful messages and negatively acknowledge (nack) all the failed messages.
471
+
In other words, when consuming single records using `PulsarRecordMessageListener` and the default ack mode of `BATCH` is used, the framework waits for all the record received from the `batchReceive` call to process successfully and then call the `acknowledge` method on the Pulsar Consumer.
403
472
If any particular record throws an exception when invoking the handler method, Spring for Apache Pulsar tracks those records and separately call `negativeAcknowledge` on those records after the entire batch is processed.
404
473
405
474
If the application wants the acknowledgment or negative acknowledgment to occur per record, then the `RECORD` ack mode can be enabled.
406
-
In that case, after handling each record the message is acknowledged if no error or negatively acknowledged if there was an error.
475
+
In that case, after handling each record, the message is acknowledged if no error and negatively acknowledged if there was an error.
476
+
Here is an example of enabling `RECORD` ack mode on Pulsar Listener.
Few things merit explanation here - First we are enabling manual ack mode by setting `ackMode` on `PulsarListener`.
510
+
When enabling manual ack mode, Spring for Apache Pulsar allows the application to inject an `Acknowledgment` object as you can see in the above `PulsarListener` method.
511
+
The framework achieves this by selecting a compatible message listener container - `PulsarAcknowledgingMessageListener` for single record based consumption which gives you access to an `Acknowledgment` object.
512
+
513
+
The `Acknowledgment` object provides the following API methods.
514
+
515
+
====
516
+
[source, java]
517
+
----
518
+
void acknowledge();
519
+
520
+
void acknowledge(MessageId messageId);
521
+
522
+
void acknowledge(List<MessageId> messageIds);
523
+
524
+
void nack();
525
+
526
+
void nack(MessageId messageId);
527
+
----
528
+
====
529
+
530
+
You can inject this `Acknowledgment` object to your `PulsarListener` while using `MANUAL` ack mode and then call one of the corresponding methods above.
531
+
532
+
In the above `PulsarListener` example, we are calling a parameter-less `acknowledge` method.
533
+
This is because the framework knows which `Message` it is operating under currently.
534
+
When calling `acknowledge()`, you do not need to receive the payload with the `Message` enveloper`, but rather simply using the target type - `String` in this example.
535
+
You can also call a different variant of `acknowledge` by providing the message id - `acknowledge.acknowledge(message.getMessageId());`
536
+
When using `acknowledge(messageId)`, you must receive the payload using the `Message<?>` envelope.
537
+
538
+
Similar to what is possible for acknowledging, the `Acknowledgment` API also provides options for negatively acknowledging - see the nack methods above.
539
+
540
+
You can also call `acknowledge` directly on the Pulsar consumer as below.
As you can see, when calling `acknowledge` directly on the underlying consumer, then you need to do error handling by yourself.
559
+
Using the `Acknowledgment` does not require that as the framework can do that for you.
560
+
Therefore, it is recommended to use the `Acknowledgment` object approach when using manual acknowledgment.
561
+
562
+
When using manual acknowledgment, it is important to understand that the framework completely stay from any acknowledgment at all.
563
+
Hence, it is extremely important for the end-users to think through the right acknowledgment strategies when designing applications.
407
564
408
565
==== Message Ack in Batch Consumption
409
566
410
567
When records are consumed in batches (See the section above), then if the default ack mode of `BATCH` is used, then when the entire batch is processed successfully, it will be acknowledged.
411
568
If any records throw an exception, then the entire batch is negatively acknowledged.
569
+
Note that this may not be the same batch that was batched on the producer side, rather this is the batch that returned from calling `batchReceive` on the consumer
When all the messages in the incoming collection (`messages` in this example) are processed, the framework will acknowledge all of them.
586
+
412
587
When consuming in batch mode, `RECORD` is not an allowed ack mode.
413
-
This might cause an issue as application does not want the entire batch to be re-delivered again.
588
+
This might cause an issue as application may not want the entire batch to be re-delivered again.
414
589
For such situations, you need to use the `MANUAL` acknowledgement mode.
415
590
416
-
==== Manual Acknowledgment
591
+
==== Manual Messge Acknowledgment in Batch Consumption
417
592
418
-
When `MANUAL` ack mode is set on the message listener container, then the framework will not do any acknowledgment - positive or negative.
593
+
As seen in the previous section, when `MANUAL` ack mode is set on the message listener container, then the framework will not do any acknowledgment - positive or negative.
419
594
It is entirely up to the application to take care of such concerns.
420
-
When `MANUAL` ack mode is set, Spring for Apache Pulsar selects a compatible message listener container - `PulsarAcknowledgingMessageListener` when in record consumption and `PulsarBatchAcknowledgingMessageListener` for batch consumption.
421
-
These interfaces provide you access to an `Acknowledgment` object.
422
-
The `Acknowledgment` object provides the following API methods.
423
-
595
+
When `MANUAL` ack mode is set, Spring for Apache Pulsar selects a compatible message listener container - `PulsarBatchAcknowledgingMessageListener` for batch consumption which gives you access to an `Acknowledgment` object.
596
+
Once again, the following are the methods availble in the `Acknowledgment` API.
You can also call `acknowledgment.nack()` to negatively acknowledge in which case the record will be re-delivered.
454
-
455
633
When using a batch listener, the message listener container cannot know which record it is currently operating upon.
456
634
Therefore, in order to manually acknowledge, you need to use one of the overloaded `acknowledge` method that takes a `MessageId` or a `List<MessageId>`.
457
635
You can also negatively acknowledge with the `MessageId` for the batch listener.
@@ -588,20 +766,16 @@ public void listen(org.apache.pulsar.client.api.Message<String> message) {
0 commit comments