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

com.azure.messaging.eventhubs.EventHubConsumerClient 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.annotation.ReturnType;
import com.azure.core.annotation.ServiceClient;
import com.azure.core.annotation.ServiceMethod;
import com.azure.core.util.Context;
import com.azure.core.util.IterableStream;
import com.azure.core.util.logging.ClientLogger;
import com.azure.messaging.eventhubs.implementation.SynchronousEventSubscriber;
import com.azure.messaging.eventhubs.implementation.SynchronousReceiveWork;
import com.azure.messaging.eventhubs.implementation.instrumentation.EventHubsTracer;
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.Flux;
import reactor.core.publisher.FluxSink;

import java.io.Closeable;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;

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

/**
 * 

A synchronous consumer responsible for reading {@link EventData} from an Event Hub partition in the context of * a specific consumer group.

* *

Most receive operations contain a parameter {@code maxWaitTime}. The iterable is returned when either * {@code maxWaitTime} has elapsed or {@code numberOfEvents} have been received. It is possible to have an empty * iterable if no events were received in that time frame. {@link #receiveFromPartition(String, int, EventPosition)} * does not have a parameter for {@code maxWaitTime}, consequently, it can take a long time to return results if * {@code numberOfEvents} is too high and there is low traffic in that Event Hub.

* *

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 a synchronous consumer

* *

The following code sample demonstrates the creation of the synchronous client {@link EventHubConsumerClient}. * 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.

* * *
 * 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.
 * EventHubConsumerClient consumer = new EventHubClientBuilder()
 *     .credential("<<fully-qualified-namespace>>", "<<event-hub-name>>",
 *         credential)
 *     .consumerGroup(EventHubClientBuilder.DEFAULT_CONSUMER_GROUP_NAME)
 *     .buildConsumerClient();
 * 
* * *

Sample: Consuming events from a single partition

* *

Events from a single partition can be consumed using {@link #receiveFromPartition(String, int, EventPosition)} or * {@link #receiveFromPartition(String, int, EventPosition, Duration)}. The call to {@code receiveFromPartition} * completes and returns an {@link IterableStream} when either the maximum number of events is received, or the * timeout has elapsed. It is possible to have an empty iterable returned if there were no events received in that * duration.

* * *
 * 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.
 * EventHubConsumerClient consumer = new EventHubClientBuilder()
 *     .credential("<<fully-qualified-namespace>>", "<<event-hub-name>>",
 *         credential)
 *     .consumerGroup(EventHubClientBuilder.DEFAULT_CONSUMER_GROUP_NAME)
 *     .buildConsumerClient();
 *
 * Instant twelveHoursAgo = Instant.now().minus(Duration.ofHours(12));
 * EventPosition startingPosition = EventPosition.fromEnqueuedTime(twelveHoursAgo);
 * String partitionId = "0";
 *
 * // Reads events from partition '0' and returns the first 100 received or until the 30 seconds has elapsed.
 * IterableStream<PartitionEvent> events = consumer.receiveFromPartition(partitionId, 100,
 *     startingPosition, Duration.ofSeconds(30));
 *
 * Long lastSequenceNumber = -1L;
 * for (PartitionEvent partitionEvent : events) {
 *     // For each event, perform some sort of processing.
 *     System.out.print("Event received: " + partitionEvent.getData().getSequenceNumber());
 *     lastSequenceNumber = partitionEvent.getData().getSequenceNumber();
 * }
 *
 * // Figure out what the next EventPosition to receive from is based on last event we processed in the stream.
 * // If lastSequenceNumber is -1L, then we didn't see any events the first time we fetched events from the
 * // partition.
 * if (lastSequenceNumber != -1L) {
 *     EventPosition nextPosition = EventPosition.fromSequenceNumber(lastSequenceNumber, false);
 *
 *     // Gets the next set of events from partition '0' to consume and process.
 *     IterableStream<PartitionEvent> nextEvents = consumer.receiveFromPartition(partitionId, 100,
 *         nextPosition, Duration.ofSeconds(30));
 * }
 * 
* * * @see com.azure.messaging.eventhubs * @see EventHubClientBuilder */ @ServiceClient(builder = EventHubClientBuilder.class) public class EventHubConsumerClient implements Closeable { private static final ClientLogger LOGGER = new ClientLogger(EventHubConsumerClient.class); private final EventHubConsumerAsyncClient consumer; private final ReceiveOptions defaultReceiveOptions = new ReceiveOptions(); private final Duration timeout; private final AtomicInteger idGenerator = new AtomicInteger(); private final EventHubsTracer tracer; EventHubConsumerClient(EventHubConsumerAsyncClient consumer, Duration tryTimeout) { Objects.requireNonNull(tryTimeout, "'tryTimeout' cannot be null."); this.consumer = Objects.requireNonNull(consumer, "'consumer' cannot be null."); this.timeout = tryTimeout; this.tracer = consumer.getInstrumentation().getTracer(); } /** * 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 consumer.getFullyQualifiedNamespace(); } /** * Gets the Event Hub name this client interacts with. * * @return The Event Hub name this client interacts with. */ public String getEventHubName() { return consumer.getEventHubName(); } /** * 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 consumer.getConsumerGroup(); } /** * 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 EventHubProperties getEventHubProperties() { return consumer.getEventHubProperties().block(); } /** * Retrieves the identifiers for the partitions of an Event Hub. * * @return The set of identifiers for the partitions of an Event Hub. */ @ServiceMethod(returns = ReturnType.COLLECTION) public IterableStream getPartitionIds() { return new IterableStream<>(consumer.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 PartitionProperties getPartitionProperties(String partitionId) { return consumer.getPartitionProperties(partitionId).block(); } /** * Receives a batch of {@link PartitionEvent events} from the Event Hub partition. * * @param maximumMessageCount The maximum number of messages to receive in this batch. * @param partitionId Identifier of the partition to read events from. * @param startingPosition Position within the Event Hub partition to begin consuming events. * * @return A set of {@link PartitionEvent} that was received. The iterable contains up to * {@code maximumMessageCount} events. 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}, or {@code startingPosition} is null. * @throws IllegalArgumentException if {@code maximumMessageCount} is less than 1, or if {@code partitionId} is an * empty string. */ @ServiceMethod(returns = ReturnType.COLLECTION) public IterableStream receiveFromPartition(String partitionId, int maximumMessageCount, EventPosition startingPosition) { return receiveFromPartition(partitionId, maximumMessageCount, startingPosition, timeout); } /** * Receives a batch of {@link PartitionEvent events} from the Event Hub partition. * * @param partitionId Identifier of the partition to read events from. * @param maximumMessageCount The maximum number of messages to receive in this batch. * @param startingPosition Position within the Event Hub partition to begin consuming events. * @param maximumWaitTime The maximum amount of time to wait to build up the requested message count for the * batch; if not specified, the default wait time specified when the consumer was created will be used. * * @return A set of {@link PartitionEvent} that was received. The iterable contains up to * {@code maximumMessageCount} events. * * @throws NullPointerException if {@code partitionId}, {@code maximumWaitTime}, or {@code startingPosition} is * {@code null}. * @throws IllegalArgumentException if {@code maximumMessageCount} is less than 1 or {@code maximumWaitTime} is * zero or a negative duration. */ @ServiceMethod(returns = ReturnType.COLLECTION) public IterableStream receiveFromPartition(String partitionId, int maximumMessageCount, EventPosition startingPosition, Duration maximumWaitTime) { if (Objects.isNull(maximumWaitTime)) { throw LOGGER.logExceptionAsError(new NullPointerException("'maximumWaitTime' cannot be null.")); } else if (Objects.isNull(startingPosition)) { throw LOGGER.logExceptionAsError(new NullPointerException("'startingPosition' cannot be null.")); } else if (Objects.isNull(partitionId)) { throw LOGGER.logExceptionAsError(new NullPointerException("'partitionId' cannot be null.")); } if (partitionId.isEmpty()) { throw LOGGER.logExceptionAsError(new IllegalArgumentException("'partitionId' cannot be empty.")); } if (maximumMessageCount < 1) { throw LOGGER.logExceptionAsError( new IllegalArgumentException("'maximumMessageCount' cannot be less than 1.")); } else if (maximumWaitTime.isNegative() || maximumWaitTime.isZero()) { throw LOGGER.logExceptionAsError( new IllegalArgumentException("'maximumWaitTime' cannot be zero or less.")); } Instant startTime = tracer.isEnabled() ? Instant.now() : null; Flux events = Flux.create(emitter -> { queueWork(partitionId, maximumMessageCount, startingPosition, maximumWaitTime, defaultReceiveOptions, emitter); }); events = tracer.reportSyncReceiveSpan("EventHubs.receiveFromPartition", startTime, events, Context.NONE); return new IterableStream<>(events); } /** * Receives a batch of {@link PartitionEvent events} from the Event Hub partition. * * @param partitionId Identifier of the partition to read events from. * @param maximumMessageCount The maximum number of messages to receive in this batch. * @param startingPosition Position within the Event Hub partition to begin consuming events. * @param maximumWaitTime The maximum amount of time to wait to build up the requested message count for the * batch; if not specified, the default wait time specified when the consumer was created will be used. * @param receiveOptions Options when receiving events from the partition. * @return A set of {@link PartitionEvent} that was received. The iterable contains up to * {@code maximumMessageCount} events. * * @throws NullPointerException if {@code maximumWaitTime}, {@code startingPosition}, {@code partitionId}, or * {@code receiveOptions} is {@code null}. * @throws IllegalArgumentException if {@code maximumMessageCount} is less than 1 or {@code maximumWaitTime} is * zero or a negative duration. */ @ServiceMethod(returns = ReturnType.COLLECTION) public IterableStream receiveFromPartition(String partitionId, int maximumMessageCount, EventPosition startingPosition, Duration maximumWaitTime, ReceiveOptions receiveOptions) { if (Objects.isNull(maximumWaitTime)) { throw LOGGER.logExceptionAsError(new NullPointerException("'maximumWaitTime' cannot be null.")); } else if (Objects.isNull(startingPosition)) { throw LOGGER.logExceptionAsError(new NullPointerException("'startingPosition' cannot be null.")); } else if (Objects.isNull(partitionId)) { throw LOGGER.logExceptionAsError(new NullPointerException("'partitionId' cannot be null.")); } else if (Objects.isNull(receiveOptions)) { throw LOGGER.logExceptionAsError(new NullPointerException("'receiveOptions' cannot be null.")); } if (partitionId.isEmpty()) { throw LOGGER.logExceptionAsError(new IllegalArgumentException("'partitionId' cannot be empty.")); } if (maximumMessageCount < 1) { throw LOGGER.logExceptionAsError( new IllegalArgumentException("'maximumMessageCount' cannot be less than 1.")); } else if (maximumWaitTime.isNegative() || maximumWaitTime.isZero()) { throw LOGGER.logExceptionAsError( new IllegalArgumentException("'maximumWaitTime' cannot be zero or less.")); } Instant startTime = tracer.isEnabled() ? Instant.now() : null; Flux events = Flux.create(emitter -> { queueWork(partitionId, maximumMessageCount, startingPosition, maximumWaitTime, receiveOptions, emitter); }); events = tracer.reportSyncReceiveSpan("EventHubs.receiveFromPartition", startTime, events, Context.NONE); return new IterableStream<>(events); } /** * {@inheritDoc} */ @Override public void close() { consumer.close(); } /** * Given an {@code emitter}, queues that work in {@link SynchronousEventSubscriber}. If the synchronous job has not * been created, will initialise it. */ private void queueWork(String partitionId, int maximumMessageCount, EventPosition startingPosition, Duration maximumWaitTime, ReceiveOptions receiveOptions, FluxSink emitter) { final long id = idGenerator.getAndIncrement(); final SynchronousReceiveWork work = new SynchronousReceiveWork(id, maximumMessageCount, maximumWaitTime, emitter); final SynchronousEventSubscriber syncSubscriber = new SynchronousEventSubscriber(work); LOGGER.atInfo() .addKeyValue(PARTITION_ID_KEY, partitionId) .log("Started synchronous event subscriber."); consumer.receiveFromPartition(partitionId, startingPosition, receiveOptions).subscribeWith(syncSubscriber); } /** * Gets the client identifier. * * @return The unique identifier string for current client. */ public String getIdentifier() { return this.consumer.getIdentifier(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy