All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.azure.messaging.eventhubs.EventHubPartitionAsyncConsumer Maven / Gradle / Ivy

There is a newer version: 5.19.2
Show newest version
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.messaging.eventhubs;

import com.azure.core.amqp.implementation.MessageSerializer;
import com.azure.core.util.logging.ClientLogger;
import com.azure.messaging.eventhubs.models.EventPosition;
import com.azure.messaging.eventhubs.models.LastEnqueuedEventProperties;
import com.azure.messaging.eventhubs.models.PartitionContext;
import com.azure.messaging.eventhubs.models.PartitionEvent;
import com.azure.messaging.eventhubs.models.ReceiveOptions;
import org.apache.qpid.proton.message.Message;
import reactor.core.publisher.Flux;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;

import static com.azure.messaging.eventhubs.implementation.ClientConstants.CONSUMER_GROUP_KEY;
import static com.azure.messaging.eventhubs.implementation.ClientConstants.PARTITION_ID_KEY;

/**
 * A package-private consumer responsible for reading {@link EventData} from a specific Event Hub partition in the
 * context of a specific consumer group.
 */
class EventHubPartitionAsyncConsumer implements AutoCloseable {
    private static final ClientLogger LOGGER = new ClientLogger(EventHubPartitionAsyncConsumer.class);
    private final AtomicBoolean isDisposed = new AtomicBoolean();
    private final AtomicReference lastEnqueuedEventProperties = new AtomicReference<>();
    private final MessageFluxWrapper amqpReceiveLinkProcessor;
    private final MessageSerializer messageSerializer;
    private final String fullyQualifiedNamespace;
    private final String eventHubName;
    private final String consumerGroup;
    private final String partitionId;
    private final boolean trackLastEnqueuedEventProperties;
    private final Flux emitterProcessor;
    private final EventPosition initialPosition;

    private volatile Long currentOffset;

    EventHubPartitionAsyncConsumer(MessageFluxWrapper amqpReceiveLinkProcessor,
        MessageSerializer messageSerializer, String fullyQualifiedNamespace, String eventHubName, String consumerGroup,
        String partitionId, AtomicReference> currentEventPosition,
        boolean trackLastEnqueuedEventProperties) {
        this.initialPosition = Objects.requireNonNull(currentEventPosition.get().get(),
            "'currentEventPosition.get().get()' cannot be null.");
        this.amqpReceiveLinkProcessor = amqpReceiveLinkProcessor;
        this.messageSerializer = messageSerializer;
        this.fullyQualifiedNamespace = fullyQualifiedNamespace;
        this.eventHubName = eventHubName;
        this.consumerGroup = consumerGroup;
        this.partitionId = partitionId;
        this.trackLastEnqueuedEventProperties = trackLastEnqueuedEventProperties;

        if (trackLastEnqueuedEventProperties) {
            lastEnqueuedEventProperties.set(new LastEnqueuedEventProperties(null, null, null, null));
        }

        currentEventPosition.set(() -> {
            final Long offset = currentOffset;
            return offset == null
                ? initialPosition
                : EventPosition.fromOffset(offset);
        });

        this.emitterProcessor = amqpReceiveLinkProcessor.flux()
            .map(this::onMessageReceived)
            .doOnNext(event -> {
                // Keep track of the last position so if the link goes down, we don't start from the original location.
                final Long offset = event.getData().getOffset();
                if (offset != null) {
                    currentOffset = offset;
                } else {
                    LOGGER.atWarning()
                        .addKeyValue(PARTITION_ID_KEY,  event.getPartitionContext().getPartitionId())
                        .addKeyValue(CONSUMER_GROUP_KEY, event.getPartitionContext().getConsumerGroup())
                        .addKeyValue("data", () -> event.getData().getBodyAsString())
                        .log("Offset for received event should not be null.");
                }
            });
    }

    /**
     * Disposes of the consumer by closing the underlying connection to the service.
     */
    @Override
    public void close() {
        if (!isDisposed.getAndSet(true)) {
            if (!amqpReceiveLinkProcessor.isTerminated()) {
                // cancel only if the processor is not already terminated.
                amqpReceiveLinkProcessor.cancel();
            }
            LOGGER.atInfo()
                .addKeyValue(PARTITION_ID_KEY, this.partitionId)
                .log("Closed consumer.");
        }
    }

    /**
     * Begin consuming events until there are no longer any subscribers.
     *
     * @return A stream of events received from the partition.
     */
    Flux receive() {
        return emitterProcessor;
    }

    /**
     * On each message received from the service, it will try to:
     * 
    *
  1. Deserialize the message into an {@link EventData}.
  2. *
  3. If {@link ReceiveOptions#getTrackLastEnqueuedEventProperties()} is true, then it will try to update * {@link LastEnqueuedEventProperties}.
  4. *
* * @param message AMQP message to deserialize. * * @return The deserialized {@link EventData} with partition information. */ private PartitionEvent onMessageReceived(Message message) { final EventData event = messageSerializer.deserialize(message, EventData.class); if (trackLastEnqueuedEventProperties) { final LastEnqueuedEventProperties enqueuedEventProperties = messageSerializer.deserialize(message, LastEnqueuedEventProperties.class); if (enqueuedEventProperties != null) { final LastEnqueuedEventProperties updated = new LastEnqueuedEventProperties( enqueuedEventProperties.getSequenceNumber(), enqueuedEventProperties.getOffset(), enqueuedEventProperties.getEnqueuedTime(), enqueuedEventProperties.getRetrievalTime()); lastEnqueuedEventProperties.set(updated); } } final PartitionContext partitionContext = new PartitionContext(fullyQualifiedNamespace, eventHubName, consumerGroup, partitionId); return new PartitionEvent(partitionContext, event, lastEnqueuedEventProperties.get()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy