com.azure.messaging.eventhubs.EventHubConsumerAsyncClient Maven / Gradle / Ivy
Show all versions of azure-messaging-eventhubs Show documentation
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.messaging.eventhubs;
import com.azure.core.amqp.exception.AmqpException;
import com.azure.core.amqp.implementation.AmqpReceiveLink;
import com.azure.core.amqp.implementation.CreditFlowMode;
import com.azure.core.amqp.implementation.MessageFlux;
import com.azure.core.amqp.implementation.MessageSerializer;
import com.azure.core.amqp.implementation.RequestResponseChannelClosedException;
import com.azure.core.amqp.implementation.RetryUtil;
import com.azure.core.amqp.implementation.StringUtil;
import com.azure.core.annotation.ReturnType;
import com.azure.core.annotation.ServiceClient;
import com.azure.core.annotation.ServiceMethod;
import com.azure.core.util.logging.ClientLogger;
import com.azure.messaging.eventhubs.implementation.AmqpReceiveLinkProcessor;
import com.azure.messaging.eventhubs.implementation.EventHubManagementNode;
import com.azure.messaging.eventhubs.implementation.instrumentation.EventHubsConsumerInstrumentation;
import com.azure.messaging.eventhubs.implementation.instrumentation.InstrumentedMessageFlux;
import com.azure.messaging.eventhubs.models.EventPosition;
import com.azure.messaging.eventhubs.models.PartitionEvent;
import com.azure.messaging.eventhubs.models.ReceiveOptions;
import reactor.core.publisher.BaseSubscriber;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.publisher.SignalType;
import java.io.Closeable;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import static com.azure.core.util.FluxUtil.fluxError;
import static com.azure.core.util.FluxUtil.monoError;
import static com.azure.messaging.eventhubs.implementation.ClientConstants.CONNECTION_ID_KEY;
import static com.azure.messaging.eventhubs.implementation.ClientConstants.LINK_NAME_KEY;
import static com.azure.messaging.eventhubs.implementation.ClientConstants.PARTITION_ID_KEY;
import static com.azure.messaging.eventhubs.implementation.ClientConstants.SIGNAL_TYPE_KEY;
import static com.azure.messaging.eventhubs.implementation.instrumentation.OperationName.GET_EVENT_HUB_PROPERTIES;
import static com.azure.messaging.eventhubs.implementation.instrumentation.OperationName.GET_PARTITION_PROPERTIES;
/**
* An asynchronous consumer responsible for reading {@link EventData} from either a specific Event Hub partition
* or all partitions in the context of a specific consumer group.
*
* The examples shown in this document use a credential object named DefaultAzureCredential for authentication,
* which is appropriate for most scenarios, including local development and production environments. Additionally, we
* recommend using
* managed identity
* for authentication in production environments. You can find more information on different ways of authenticating and
* their corresponding credential types in the
* Azure Identity documentation".
*
*
* Sample: Creating an {@link EventHubConsumerAsyncClient}
*
* The following code sample demonstrates the creation of the asynchronous client
* {@link EventHubConsumerAsyncClient}. The {@code fullyQualifiedNamespace} is the Event Hubs Namespace's host name.
* It is listed under the "Essentials" panel after navigating to the Event Hubs Namespace via Azure Portal. The
* {@code consumerGroup} is found by navigating to the Event Hub instance, and selecting "Consumer groups" under the
* "Entities" panel. The {@link EventHubClientBuilder#consumerGroup(String)} is required for creating consumer clients.
* The credential used is {@code DefaultAzureCredential} because it combines commonly used credentials in deployment
* and development and chooses the credential to used based on its running environment.
*
*
*
* TokenCredential credential = new DefaultAzureCredentialBuilder().build();
*
* // "<<fully-qualified-namespace>>" will look similar to "{your-namespace}.servicebus.windows.net"
* // "<<event-hub-name>>" will be the name of the Event Hub instance you created inside the Event Hubs namespace.
* EventHubProducerAsyncClient producer = new EventHubClientBuilder()
* .credential("<<fully-qualified-namespace>>", "<<event-hub-name>>",
* credential)
* .buildAsyncProducerClient();
*
*
*
* Sample: Consuming events a single partition from Event Hub
*
* The code sample below demonstrates receiving events from partition "0" of an Event Hub starting from
* {@link EventPosition#latest()}. {@link EventPosition#latest()} points to the end of the partition stream. The
* consumer receives events enqueued after it started subscribing for events.
*
* {@link #receiveFromPartition(String, EventPosition)} is a non-blocking call. After setting up the operation,
* its async representation is returned. The {@code Flux} must be subscribed to, like the sample below,
* to start receiving events.
*
*
*
* EventHubConsumerAsyncClient consumer = new EventHubClientBuilder()
* .credential("<<fully-qualified-namespace>>", "<<event-hub-name>>",
* new DefaultAzureCredentialBuilder().build())
* .consumerGroup(EventHubClientBuilder.DEFAULT_CONSUMER_GROUP_NAME)
* .buildAsyncConsumerClient();
*
* // Obtain partitionId from EventHubConsumerAsyncClient.getPartitionIds()
* String partitionId = "0";
* EventPosition startingPosition = EventPosition.latest();
*
* // Keep a reference to `subscription`. When the program is finished receiving events, call
* // subscription.dispose(). This will stop fetching events from the Event Hub.
* //
* // NOTE: This is a non-blocking call and will move to the next line of code after setting up the async
* // operation. If the program ends after this, or the class is immediately disposed, no events will be
* // received.
* Disposable subscription = consumer.receiveFromPartition(partitionId, startingPosition)
* .subscribe(partitionEvent -> {
* PartitionContext partitionContext = partitionEvent.getPartitionContext();
* EventData event = partitionEvent.getData();
*
* System.out.printf("Received event from partition '%s'%n", partitionContext.getPartitionId());
* System.out.printf("Contents of event as string: '%s'%n", event.getBodyAsString());
* }, error -> {
* // This is a terminal signal. No more events will be received from the same Flux object.
* System.err.print("An error occurred:" + error);
* }, () -> {
* // This is a terminal signal. No more events will be received from the same Flux object.
* System.out.print("Stream has ended.");
* });
*
*
*
* Sample: Including latest partition information in received events
*
* {@link EventData} can be decorated with the latest partition information and sent to consumers. Enable this by
* setting {@link ReceiveOptions#setTrackLastEnqueuedEventProperties(boolean)} to {@code true}. As events come in,
* explore the {@link PartitionEvent} object. This is useful in scenarios where customers want to constant up-to-date
* information about their Event Hub. This does take a performance hit as the extra partition information must be sent
* over the wire with every event.
*
* {@link #receiveFromPartition(String, EventPosition, ReceiveOptions)} is a non-blocking call. After setting up the
* operation, its async representation is returned. The {@code Flux} must be subscribed to, like
* sample below, to start receiving events.
*
*
*
* // Set `setTrackLastEnqueuedEventProperties` to true to get the last enqueued information from the partition for
* // each event that is received.
* ReceiveOptions receiveOptions = new ReceiveOptions()
* .setTrackLastEnqueuedEventProperties(true);
* EventPosition startingPosition = EventPosition.earliest();
*
* // Receives events from partition "0" starting at the beginning of the stream.
* // Keep a reference to `subscription`. When the program is finished receiving events, call
* // subscription.dispose(). This will stop fetching events from the Event Hub.
* Disposable subscription = consumer.receiveFromPartition("0", startingPosition, receiveOptions)
* .subscribe(partitionEvent -> {
* LastEnqueuedEventProperties properties = partitionEvent.getLastEnqueuedEventProperties();
* System.out.printf("Information received at %s. Last enqueued sequence number: %s%n",
* properties.getRetrievalTime(),
* properties.getSequenceNumber());
* });
*
*
*
* Sample: Rate limiting consumption of events from Event Hub
*
* For event consumers that need to limit the number of events they receive at a given time, they can use
* {@link BaseSubscriber#request(long)}. Using a custom subscriber allows developers more granular control over the
* rate at which they receive events.
*
* {@link #receiveFromPartition(String, EventPosition)} is a non-blocking call. After setting up the operation,
* its async representation is returned. The {@code Flux} must be subscribed to, like the sample below,
* to start receiving events.
*
*
*
* consumer.receiveFromPartition(partitionId, EventPosition.latest()).subscribe(new BaseSubscriber<PartitionEvent>() {
* private static final int NUMBER_OF_EVENTS = 5;
* private final AtomicInteger currentNumberOfEvents = new AtomicInteger();
*
* @Override
* protected void hookOnSubscribe(Subscription subscription) {
* // Tell the Publisher we only want 5 events at a time.
* request(NUMBER_OF_EVENTS);
* }
*
* @Override
* protected void hookOnNext(PartitionEvent value) {
* // Process the EventData
*
* // If the number of events we have currently received is a multiple of 5, that means we have reached the
* // last event the Publisher will provide to us. Invoking request(long) here, tells the Publisher that
* // the subscriber is ready to get more events from upstream.
* if (currentNumberOfEvents.incrementAndGet() % 5 == 0) {
* request(NUMBER_OF_EVENTS);
* }
* }
* });
*
*
*
* Sample: Receiving from all partitions
*
* The code sample below demonstrates receiving events from all partitions of an Event Hub starting the beginning of
* each partition's stream. This is valuable for demo purposes but is not intended for production scenarios.
* For production scenarios, consider using {@link EventProcessorClient}.
*
* {@link #receive(boolean)} is a non-blocking call. After setting up the operation, its async representation is
* returned. The {@code Flux} must be subscribed to, like the sample below, to start receiving events.
*
*
*
*
* // Keep a reference to `subscription`. When the program is finished receiving events, call
* // subscription.dispose(). This will stop fetching events from the Event Hub.
* Disposable subscription = consumer.receive(true)
* .subscribe(partitionEvent -> {
* PartitionContext context = partitionEvent.getPartitionContext();
* EventData event = partitionEvent.getData();
*
* System.out.printf("Event %s is from partition %s%n.", event.getSequenceNumber(),
* context.getPartitionId());
* }, error -> {
* // This is a terminal signal. No more events will be received from the same Flux object.
* System.err.print("An error occurred:" + error);
* }, () -> {
* // This is a terminal signal. No more events will be received from the same Flux object.
* System.out.print("Stream has ended.");
* });
*
*
*
* @see com.azure.messaging.eventhubs
* @see EventHubClientBuilder
*/
@ServiceClient(builder = EventHubClientBuilder.class, isAsync = true)
public class EventHubConsumerAsyncClient implements Closeable {
private static final String RECEIVER_ENTITY_PATH_FORMAT = "%s/ConsumerGroups/%s/Partitions/%s";
private static final ClientLogger LOGGER = new ClientLogger(EventHubConsumerAsyncClient.class);
private final AtomicBoolean isDisposed = new AtomicBoolean();
private final ReceiveOptions defaultReceiveOptions = new ReceiveOptions();
private final String fullyQualifiedNamespace;
private final String eventHubName;
private final ConnectionCacheWrapper connectionProcessor;
private final MessageSerializer messageSerializer;
private final String consumerGroup;
private final int prefetchCount;
private final boolean isSharedConnection;
private final Runnable onClientClosed;
private final String identifier;
private final EventHubsConsumerInstrumentation instrumentation;
/**
* Keeps track of the open partition consumers keyed by linkName. The link name is generated as: {@code
* "partitionId_GUID"}. For receiving from all partitions, links are prefixed with {@code "all-GUID-partitionId"}.
*/
private final ConcurrentHashMap openPartitionConsumers =
new ConcurrentHashMap<>();
EventHubConsumerAsyncClient(String fullyQualifiedNamespace, String eventHubName,
ConnectionCacheWrapper connectionProcessor, MessageSerializer messageSerializer, String consumerGroup,
int prefetchCount, boolean isSharedConnection, Runnable onClientClosed, String identifier,
EventHubsConsumerInstrumentation instrumentation) {
this.fullyQualifiedNamespace = fullyQualifiedNamespace;
this.eventHubName = eventHubName;
this.connectionProcessor = connectionProcessor;
this.messageSerializer = messageSerializer;
this.consumerGroup = consumerGroup;
this.prefetchCount = prefetchCount;
this.isSharedConnection = isSharedConnection;
this.onClientClosed = onClientClosed;
this.identifier = identifier;
this.instrumentation = instrumentation;
}
/**
* Gets the fully qualified Event Hubs namespace that the connection is associated with. This is likely similar to
* {@code {yournamespace}.servicebus.windows.net}.
*
* @return The fully qualified Event Hubs namespace that the connection is associated with
*/
public String getFullyQualifiedNamespace() {
return fullyQualifiedNamespace;
}
/**
* Gets the Event Hub name this client interacts with.
*
* @return The Event Hub name this client interacts with.
*/
public String getEventHubName() {
return eventHubName;
}
/**
* Gets the consumer group this consumer is reading events as a part of.
*
* @return The consumer group this consumer is reading events as a part of.
*/
public String getConsumerGroup() {
return consumerGroup;
}
boolean isV2() {
return connectionProcessor.isV2();
}
/**
* Retrieves information about an Event Hub, including the number of partitions present and their identifiers.
*
* @return The set of information for the Event Hub that this client is associated with.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono getEventHubProperties() {
return instrumentation.instrumentMono(connectionProcessor.getManagementNodeWithRetries()
.flatMap(EventHubManagementNode::getEventHubProperties),
GET_EVENT_HUB_PROPERTIES, null);
}
/**
* Retrieves the identifiers for the partitions of an Event Hub.
*
* @return A Flux of identifiers for the partitions of an Event Hub.
*/
@ServiceMethod(returns = ReturnType.COLLECTION)
public Flux getPartitionIds() {
return getEventHubProperties().flatMapMany(properties -> Flux.fromIterable(properties.getPartitionIds()));
}
/**
* Retrieves information about a specific partition for an Event Hub, including elements that describe the available
* events in the partition event stream.
*
* @param partitionId The unique identifier of a partition associated with the Event Hub.
*
* @return The set of information for the requested partition under the Event Hub this client is associated with.
*
* @throws NullPointerException if {@code partitionId} is null.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono getPartitionProperties(String partitionId) {
if (Objects.isNull(partitionId)) {
return monoError(LOGGER, new NullPointerException("'partitionId' cannot be null."));
} else if (partitionId.isEmpty()) {
return monoError(LOGGER, new IllegalArgumentException("'partitionId' cannot be an empty string."));
}
return instrumentation.instrumentMono(
connectionProcessor.getManagementNodeWithRetries().flatMap(node -> node.getPartitionProperties(partitionId)),
GET_PARTITION_PROPERTIES, partitionId);
}
/**
* Consumes events from a single partition starting at {@code startingPosition}.
*
* @param partitionId Identifier of the partition to read events from.
* @param startingPosition Position within the Event Hub partition to begin consuming events.
*
* @return A stream of events for this partition starting from {@code startingPosition}.
*
* @throws NullPointerException if {@code partitionId}, or {@code startingPosition} is null.
* @throws IllegalArgumentException if {@code partitionId} is an empty string.
*/
@ServiceMethod(returns = ReturnType.COLLECTION)
public Flux receiveFromPartition(String partitionId, EventPosition startingPosition) {
return receiveFromPartition(partitionId, startingPosition, defaultReceiveOptions);
}
/**
* Consumes events from a single partition starting at {@code startingPosition} with a set of {@link ReceiveOptions
* receive options}.
*
*
* - If receive is invoked where {@link ReceiveOptions#getOwnerLevel()} has a value, then Event Hubs service will
* guarantee only one active consumer exists per partitionId and consumer group combination. This receive operation
* is sometimes referred to as an "Epoch Consumer".
* - Multiple consumers per partitionId and consumer group combination can be created by not setting
* {@link ReceiveOptions#getOwnerLevel()} when invoking receive operations. This non-exclusive consumer is sometimes
* referred to as a "Non-Epoch Consumer."
*
*
* @param partitionId Identifier of the partition to read events from.
* @param startingPosition Position within the Event Hub partition to begin consuming events.
* @param receiveOptions Options when receiving events from the partition.
*
* @return A stream of events for this partition. If a stream for the events was opened before, the same position
* within that partition is returned. Otherwise, events are read starting from {@code startingPosition}.
*
* @throws NullPointerException if {@code partitionId}, {@code startingPosition}, {@code receiveOptions} is
* null.
* @throws IllegalArgumentException if {@code partitionId} is an empty string.
*/
@ServiceMethod(returns = ReturnType.COLLECTION)
public Flux receiveFromPartition(String partitionId, EventPosition startingPosition,
ReceiveOptions receiveOptions) {
if (Objects.isNull(partitionId)) {
return fluxError(LOGGER, new NullPointerException("'partitionId' cannot be null."));
} else if (partitionId.isEmpty()) {
return fluxError(LOGGER, new IllegalArgumentException("'partitionId' cannot be an empty string."));
}
if (Objects.isNull(startingPosition)) {
return fluxError(LOGGER, new NullPointerException("'startingPosition' cannot be null."));
}
final String linkName = StringUtil.getRandomString(partitionId);
return createConsumer(linkName, partitionId, startingPosition, receiveOptions);
}
/**
* Consumes events from all partitions starting from the beginning of each partition.
*
* This method is not recommended for production use; the {@link EventProcessorClient} should be used for
* reading events from all partitions in a production scenario, as it offers a much more robust experience with
* higher throughput.
*
* It is important to note that this method does not guarantee fairness amongst the partitions. Depending on service
* communication, there may be a clustering of events per partition and/or there may be a noticeable bias for a
* given partition or subset of partitions.
*
*
* @return A stream of events for every partition in the Event Hub starting from the beginning of each partition.
*/
@ServiceMethod(returns = ReturnType.COLLECTION)
public Flux receive() {
return receive(true, defaultReceiveOptions);
}
/**
* Consumes events from all partitions.
*
* This method is not recommended for production use; the {@link EventProcessorClient} should be used for
* reading events from all partitions in a production scenario, as it offers a much more robust experience with
* higher throughput.
*
* It is important to note that this method does not guarantee fairness amongst the partitions. Depending on service
* communication, there may be a clustering of events per partition and/or there may be a noticeable bias for a
* given partition or subset of partitions.
*
* @param startReadingAtEarliestEvent {@code true} to begin reading at the first events available in each
* partition; otherwise, reading will begin at the end of each partition seeing only new events as they are
* published.
*
* @return A stream of events for every partition in the Event Hub.
*/
@ServiceMethod(returns = ReturnType.COLLECTION)
public Flux receive(boolean startReadingAtEarliestEvent) {
return receive(startReadingAtEarliestEvent, defaultReceiveOptions);
}
/**
* Consumes events from all partitions configured with a set of {@code receiveOptions}.
*
* This method is not recommended for production use; the {@link EventProcessorClient} should be used for
* reading events from all partitions in a production scenario, as it offers a much more robust experience with
* higher throughput.
*
* It is important to note that this method does not guarantee fairness amongst the partitions. Depending on service
* communication, there may be a clustering of events per partition and/or there may be a noticeable bias for a
* given partition or subset of partitions.
*
*
* - If receive is invoked where {@link ReceiveOptions#getOwnerLevel()} has a value, then Event Hubs service will
* guarantee only one active consumer exists per partitionId and consumer group combination. This receive operation
* is sometimes referred to as an "Epoch Consumer".
* - Multiple consumers per partitionId and consumer group combination can be created by not setting
* {@link ReceiveOptions#getOwnerLevel()} when invoking receive operations. This non-exclusive consumer is sometimes
* referred to as a "Non-Epoch Consumer."
*
*
* @param startReadingAtEarliestEvent {@code true} to begin reading at the first events available in each
* partition; otherwise, reading will begin at the end of each partition seeing only new events as they are
* published.
* @param receiveOptions Options when receiving events from each Event Hub partition.
*
* @return A stream of events for every partition in the Event Hub.
*
* @throws NullPointerException if {@code receiveOptions} is null.
*/
@ServiceMethod(returns = ReturnType.COLLECTION)
public Flux receive(boolean startReadingAtEarliestEvent, ReceiveOptions receiveOptions) {
if (Objects.isNull(receiveOptions)) {
return fluxError(LOGGER, new NullPointerException("'receiveOptions' cannot be null."));
}
final EventPosition startingPosition = startReadingAtEarliestEvent
? EventPosition.earliest()
: EventPosition.latest();
final String prefix = StringUtil.getRandomString("all");
final Flux allPartitionEvents = getPartitionIds().flatMap(partitionId -> {
final String linkName = prefix + "-" + partitionId;
return createConsumer(linkName, partitionId, startingPosition, receiveOptions);
});
return Flux.merge(allPartitionEvents);
}
/**
* Disposes of the consumer by closing the underlying connection to the service.
*/
@Override
public void close() {
if (isDisposed.getAndSet(true)) {
return;
}
openPartitionConsumers.forEach((key, value) -> value.close());
openPartitionConsumers.clear();
if (isSharedConnection) {
onClientClosed.run();
} else {
connectionProcessor.dispose();
}
}
private Flux createConsumer(String linkName, String partitionId, EventPosition startingPosition,
ReceiveOptions receiveOptions) {
return openPartitionConsumers
.computeIfAbsent(linkName,
name -> createPartitionConsumer(name, partitionId, startingPosition, receiveOptions))
.receive()
.doFinally(signal -> removeLink(linkName, partitionId, signal));
}
private void removeLink(String linkName, String partitionId, SignalType signalType) {
LOGGER.atInfo()
.addKeyValue(LINK_NAME_KEY, linkName)
.addKeyValue(PARTITION_ID_KEY, partitionId)
.addKeyValue(SIGNAL_TYPE_KEY, signalType)
.log("Receiving completed.");
final EventHubPartitionAsyncConsumer consumer = openPartitionConsumers.remove(linkName);
if (consumer != null) {
consumer.close();
}
}
private EventHubPartitionAsyncConsumer createPartitionConsumer(String linkName, String partitionId,
EventPosition startingPosition, ReceiveOptions receiveOptions) {
final String entityPath = String.format(Locale.US, RECEIVER_ENTITY_PATH_FORMAT,
getEventHubName(), consumerGroup, partitionId);
final AtomicReference> initialPosition = new AtomicReference<>(() -> startingPosition);
// The Mono, when subscribed, creates a AmqpReceiveLink in the AmqpConnection emitted by the connectionProcessor
//
final Mono receiveLinkMono = connectionProcessor.getConnection()
.flatMap(connection -> {
LOGGER.atInfo()
.addKeyValue(LINK_NAME_KEY, linkName)
.addKeyValue(PARTITION_ID_KEY, partitionId)
.addKeyValue(CONNECTION_ID_KEY, connection.getId())
.log("Creating receive consumer for partition.");
return connection.createReceiveLink(linkName, entityPath, initialPosition.get().get(), receiveOptions, identifier);
});
// A Mono that resubscribes to 'receiveLinkMono' to retry the creation of AmqpReceiveLink.
//
// The scenarios where this retry helps are -
// [1]. When we try to create a link on a session being disposed but connection is healthy, the retry can
// eventually create a new session then the link.
// [2]. When we try to create a new session (to host the new link) but on a connection being disposed,
// the retry can eventually receive a new connection and then proceed with creating session and link.
//
final Mono retryableReceiveLinkMono = RetryUtil.withRetry(receiveLinkMono.onErrorMap(
RequestResponseChannelClosedException.class,
e -> {
// When the current connection is being disposed, the connectionProcessor can produce
// a new connection if downstream request.
// In this context, treat RequestResponseChannelClosedException from the RequestResponseChannel scoped
// to the current connection being disposed as retry-able so that retry can obtain new connection.
return new AmqpException(true, e.getMessage(), e, null);
}),
connectionProcessor.getRetryOptions(),
"Failed to create receive link " + linkName,
true);
// A Flux that produces a new AmqpReceiveLink each time it receives a request from the below
// 'AmqpReceiveLinkProcessor'. Obviously, the processor requests a link when there is a downstream subscriber.
// It also requests a new link (i.e. retry) when the current link it holds gets terminated
// (e.g., when the service decides to close that link).
//
final Flux receiveLinkFlux = retryableReceiveLinkMono
.repeat()
// The re-subscribe nature of 'MonoRepeat' following the emission of a link will cause the 'MonoFlatmap' (the
// upstream of repeat operator) to cache the same link. When AmqpReceiveLinkProcessor later requests a new link,
// the corresponding request from the 'MonoRepeat' will be answered with the cached (and closed) link by the
// 'MonoFlatmap'. We'll filter out these cached (closed) links to avoid AmqpReceiveLinkProcessor from doing
// unusable work (creating subscriptions and attempting to place the credit) on those links and associated logging.
// See the PR description (https://github.com/Azure/azure-sdk-for-java/pull/33204) for more details.
.filter(link -> !link.isDisposed());
final MessageFluxWrapper linkMessageProcessor;
if (connectionProcessor.isV2()) {
MessageFlux messageFlux = new MessageFlux(receiveLinkFlux, prefetchCount, CreditFlowMode.EmissionDriven, MessageFlux.NULL_RETRY_POLICY);
linkMessageProcessor = new MessageFluxWrapper(InstrumentedMessageFlux.instrument(messageFlux, partitionId, instrumentation));
} else {
final AmqpReceiveLinkProcessor receiveLinkProcessor = receiveLinkFlux.subscribeWith(
new AmqpReceiveLinkProcessor(entityPath, prefetchCount, partitionId, connectionProcessor, instrumentation));
linkMessageProcessor = new MessageFluxWrapper(receiveLinkProcessor);
}
return new EventHubPartitionAsyncConsumer(linkMessageProcessor, messageSerializer, getFullyQualifiedNamespace(),
getEventHubName(), consumerGroup, partitionId, initialPosition,
receiveOptions.getTrackLastEnqueuedEventProperties());
}
boolean isConnectionClosed() {
return this.connectionProcessor.isChannelClosed();
}
EventHubsConsumerInstrumentation getInstrumentation() {
return instrumentation;
}
/**
* Gets the client identifier.
*
* @return The unique identifier string for current client.
*/
public String getIdentifier() {
return identifier;
}
}