/* * Copyright 2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package io.atomix.group.messaging; import io.atomix.catalyst.annotations.Experimental; import io.atomix.catalyst.util.Assert; import java.util.concurrent.CompletableFuture; /** * Produces messages to a remote {@link MessageService} queue. * <p> * Producers facilitate sending messages to remote queues through the Atomix cluster. Producers can be used * to broadcast messages to entire groups or send direct messages to random or specific members of a group. * Messages can be sent synchronously or asynchronously, or producers can await replies from consumers. * <p> * When messages are sent by a producer to consumers, they're sent through the Atomix cluster as writes to * the group's replicated state machine. Messages are enqueued in memory on each stateful server in the cluster * until received and acknowledged by the appropriate consumers. * <p> * To configure a message producer, the producer must be constructed with * {@link io.atomix.group.messaging.MessageProducer.Options Options}. * <pre> * {@code * MessageProducer.Options options = new MessageProducer.Options() * .withExecution(MessageProducer.Execution.ASYNC) * .withDelivery(MessageProducer.Delivery.RANDOM); * MessageProducer<String> producer = group.messaging().producer("foo"); * producer.send("Hello world!").thenRun(() -> { * // Message was stored to the cluster * }); * } * </pre> * The configured {@link io.atomix.group.messaging.MessageProducer.Execution Execution} defines the criteria for * completion of messages sent by a producer. * <ul> * <li>{@link io.atomix.group.messaging.MessageProducer.Execution#SYNC SYNC} producers send messages to consumers * and await acknowledgement from the consumer side of the queue. If a producer is producing to an entire group, * synchronous producers will await acknowledgement from all members of the group.</li> * <li>{@link io.atomix.group.messaging.MessageProducer.Execution#ASYNC ASYNC} producers await acknowledgement of * persistence in the cluster but not acknowledgement that messages have been received and processed by consumers.</li> * <li>{@link io.atomix.group.messaging.MessageProducer.Execution#REQUEST_REPLY REQUEST_REPLY} producers await * arbitrary responses from all consumers to which a message is sent. If a message is sent to a group of consumers, * message reply futures will be completed with a list of reply values.</li> * </ul> * The configured {@link io.atomix.group.messaging.MessageProducer.Delivery Delivery} defines how messages are delivered * to consumers, particularly when the producer is sending messages to a group. * <ul> * <li>{@link io.atomix.group.messaging.MessageProducer.Delivery#DIRECT DIRECT} producers send messages directly to * specific group members. This option applies only to producers constructed from {@link io.atomix.group.GroupMember} * messaging clients.</li> * <li>{@link io.atomix.group.messaging.MessageProducer.Delivery#RANDOM} producers send each message to a random * member of the group. In the event that a message is not successfully {@link Message#ack() acknowledged} by a * member and that member fails or leaves the group, random messages will be redelivered to remaining members * of the group.</li> * <li>{@link io.atomix.group.messaging.MessageProducer.Delivery#BROADCAST} producers send messages to all available * members of a group. This option applies only to producers constructed from {@link io.atomix.group.DistributedGroup} * messaging clients.</li> * </ul> * * @author <a href="http://github.com/kuujo>Jordan Halterman</a> */ @Experimental public interface MessageProducer<T> extends AutoCloseable { /** * Execution policy for defining the criteria for completion of a message produced by a producer. * <p> * Execution policies define how a producer interacts with the cluster and the type of feedback a producer * expects from consumers. Producers send messages through the Atomix cluster, and send operations may be * completed when committed to the cluster or when acknowledged by consumers depending on the configured * execution policy. * <ul> * <li>{@link io.atomix.group.messaging.MessageProducer.Execution#SYNC SYNC} producers send messages to consumers * and await acknowledgement from the consumer side of the queue. If a producer is producing to an entire group, * synchronous producers will await acknowledgement from all members of the group.</li> * <li>{@link io.atomix.group.messaging.MessageProducer.Execution#ASYNC ASYNC} producers await acknowledgement of * persistence in the cluster but not acknowledgement that messages have been received and processed by consumers.</li> * <li>{@link io.atomix.group.messaging.MessageProducer.Execution#REQUEST_REPLY REQUEST_REPLY} producers await * arbitrary responses from all consumers to which a message is sent. If a message is sent to a group of consumers, * message reply futures will be completed with a list of reply values.</li> * </ul> */ enum Execution { /** * Sends messages to consumers and awaits acknowledgement from the consumer side of the queue. If a producer * is producing to an entire group, synchronous producers will await acknowledgement from all members of the group. */ SYNC, /** * Awaits acknowledgement of persistence in the cluster but not acknowledgement that messages have been received * and processed by consumers. Once a message is completed by a asynchronous producer, the message is guaranteed to * be persisted on a majority of the cluster and will eventually be delivered to consumers. */ ASYNC, /** * Awaits arbitrary responses from all consumers to which a message is sent. If a message is sent to a group of * consumers, message reply futures will be completed with a list of reply values. */ REQUEST_REPLY, } /** * Delivery policy for defining how messages are delivered to consumers, particularly when producing messages to * a group. * <p> * Delivery policies define how messages are delivered to consumers once persisted in the Atomix cluster. When sending * messages directly to a specific member of a group, delivery policies typically do not apply. However, for producers * created via the group-wide {@link MessageClient}, delivery policies allow producers to control how the cluster * delivers messages to producers. * <ul> * <li>{@link io.atomix.group.messaging.MessageProducer.Delivery#DIRECT DIRECT} producers send messages directly to * specific group members. This option applies only to producers constructed from {@link io.atomix.group.GroupMember} * messaging clients.</li> * <li>{@link io.atomix.group.messaging.MessageProducer.Delivery#RANDOM} producers send each message to a random * member of the group. In the event that a message is not successfully {@link Message#ack() acknowledged} by a * member and that member fails or leaves the group, random messages will be redelivered to remaining members * of the group.</li> * <li>{@link io.atomix.group.messaging.MessageProducer.Delivery#BROADCAST} producers send messages to all available * members of a group. This option applies only to producers constructed from {@link io.atomix.group.DistributedGroup} * messaging clients.</li> * </ul> */ enum Delivery { /** * Sends messages directly to specific group members. This option applies only to producers constructed from * {@link io.atomix.group.GroupMember} messaging clients. */ DIRECT, /** * Sends each message to a random member of the group. In the event that a message is not successfully * {@link Message#ack() acknowledged} by a member and that member fails or leaves the group, random messages * will be redelivered to remaining members of the group. */ RANDOM, /** * Sends messages to all available members of a group. This option applies only to producers constructed from * {@link io.atomix.group.DistributedGroup} messaging clients. */ BROADCAST, } /** * Message producer options. * <p> * Producer options allow users to define how messages are delivered to consumers and the criteria by which the * cluster determines when the delivery of a message is complete. */ class Options { private Delivery delivery = Delivery.BROADCAST; private Execution execution = Execution.SYNC; /** * Sets the producer delivery policy. * * @param delivery The producer delivery policy. * @return The producer options. */ public Options withDelivery(Delivery delivery) { this.delivery = Assert.notNull(delivery, "delivery"); return this; } /** * Returns the producer delivery policy. * * @return The producer delivery policy. */ public Delivery getDelivery() { return delivery; } /** * Sets the producer execution policy. * * @param execution The producer execution policy. * @return The producer options. */ public Options withExecution(Execution execution) { this.execution = Assert.notNull(execution, "execution"); return this; } /** * Returns the producer execution policy. * * @return The producer execution policy. */ public Execution getExecution() { return execution; } } /** * Sends a message. * <p> * The behavior of the queue when sending a message is defined by the configured producer * {@link io.atomix.group.messaging.MessageProducer.Options Options}. All messages when sent by a producer are * committed as writes to the Atomix cluster, but the returned {@link CompletableFuture} will be completed based * on the configured {@link io.atomix.group.messaging.MessageProducer.Execution Execution} and * {@link io.atomix.group.messaging.MessageProducer.Delivery Delivery} policies. * <pre> * {@code * MessageProducer.Options options = new MessageProducer.Options() * .withExecution(Execution.SYNC); * MessageProducer<String> producer = member.messaging().producer("foo"); * producer.send("Hello world!").thenRun(() -> { * // Consumer acknowledged the message * }); * } * </pre> * * @param message The message to send. * @return A completable future to be completed once the message has been acknowledged. */ <U> CompletableFuture<U> send(T message); /** * Closes the producer. */ @Override default void close() { } }