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

discord4j.connect.rabbitmq.gateway.RabbitMQPayloadSource Maven / Gradle / Ivy

package discord4j.connect.rabbitmq.gateway;

import com.rabbitmq.client.Delivery;
import com.rabbitmq.client.ShutdownSignalException;
import discord4j.connect.common.ConnectPayload;
import discord4j.connect.common.PayloadSource;
import discord4j.connect.common.SourceMapper;
import discord4j.connect.rabbitmq.ConnectRabbitMQ;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.rabbitmq.ConsumeOptions;
import reactor.rabbitmq.QueueSpecification;
import reactor.util.Logger;
import reactor.util.Loggers;
import reactor.util.retry.RetryBackoffSpec;
import reactor.util.retry.RetrySpec;

import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;
import java.util.function.Function;

/**
 * A RabbitMQ consumer that can process a stream of incoming payloads.
 */
public class RabbitMQPayloadSource implements PayloadSource {

    private static final Logger log = Loggers.getLogger(RabbitMQPayloadSource.class);

    public static final RetryBackoffSpec DEFAULT_RETRY_STRATEGY =
            RetrySpec.fixedDelay(Long.MAX_VALUE, Duration.ofSeconds(1))
                    .filter(t -> !(t instanceof ShutdownSignalException))
                    .doBeforeRetry(retry -> log.info("Consumer retry {} due to {}", retry.totalRetriesInARow(),
                            retry.failure()));

    private final SourceMapper mapper;
    private final ConnectRabbitMQ rabbitMQ;
    private final ConsumeOptions consumeOptions;
    private final Collection queues;
    private final RetryBackoffSpec consumeErrorStrategy;

    RabbitMQPayloadSource(SourceMapper mapper, ConnectRabbitMQ rabbitMQ, ConsumeOptions consumeOptions,
                          Collection queues, RetryBackoffSpec consumeErrorStrategy) {
        this.mapper = mapper;
        this.rabbitMQ = rabbitMQ;
        this.consumeOptions = consumeOptions;
        this.queues = queues;
        this.consumeErrorStrategy = consumeErrorStrategy;
    }

    /**
     * Create a default source using the given parameters, able to subscribe to a list of queues.
     *
     * @param mapper mapper to read {@link Delivery} instances from the received messages
     * @param rabbitMQ RabbitMQ broker abstraction
     * @param queues a list of queues that should be subscribed to
     * @return a source ready to consume payloads
     */
    public static RabbitMQPayloadSource create(SourceMapper mapper,
                                               ConnectRabbitMQ rabbitMQ,
                                               String... queues) {
        return new RabbitMQPayloadSource(mapper, rabbitMQ, new ConsumeOptions(), Arrays.asList(queues),
                DEFAULT_RETRY_STRATEGY);
    }

    /**
     * Create a default source using the given parameters, able to subscribe to a list of queues.
     *
     * @param mapper mapper to read {@link Delivery} instances from the received messages
     * @param rabbitMQ RabbitMQ broker abstraction
     * @param queues a list of queues that should be subscribed to
     * @return a source ready to consume payloads
     */
    public static RabbitMQPayloadSource create(SourceMapper mapper,
                                               ConnectRabbitMQ rabbitMQ,
                                               Collection queues) {
        return new RabbitMQPayloadSource(mapper, rabbitMQ, new ConsumeOptions(), queues, DEFAULT_RETRY_STRATEGY);
    }

    /**
     * Customize the {@link ConsumeOptions} used when consuming each payload.
     *
     * @param consumeOptions options to configure receiving
     * @return a new instance with the given parameter
     */
    public RabbitMQPayloadSource withConsumeOptions(ConsumeOptions consumeOptions) {
        return new RabbitMQPayloadSource(mapper, rabbitMQ, consumeOptions, queues, consumeErrorStrategy);
    }

    /**
     * Customize the retry strategy on consumer errors.
     *
     * @param consumeErrorStrategy a Reactor retrying strategy to be applied on consumer errors
     * @return a new instance with the given parameter
     */
    public RabbitMQPayloadSource withConsumeErrorStrategy(RetryBackoffSpec consumeErrorStrategy) {
        return new RabbitMQPayloadSource(mapper, rabbitMQ, consumeOptions, queues, consumeErrorStrategy);
    }

    @Override
    public Flux receive(Function> processor) {
        return Flux.fromIterable(queues)
                .flatMap(queue -> Mono.just(queue)
                        .delaySubscription(rabbitMQ.getSender().declare(QueueSpecification.queue(queue))))
                .flatMap(queue -> rabbitMQ.getReceiver().consumeAutoAck(queue, consumeOptions)
                        .retryWhen(consumeErrorStrategy))
                .doOnSubscribe(s -> log.info("Begin receiving from server"))
                .doOnError(e -> log.error("Receive failed", e))
                .doFinally(s -> log.info("Receiver completed after {}", s))
                .share() // allow multi-casting inbound payload
                .flatMap(mapper::apply)
                .flatMap(processor);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy