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

io.hekate.messaging.MessagingService Maven / Gradle / Ivy

There is a newer version: 4.1.3
Show newest version
/*
 * Copyright 2020 The Hekate Project
 *
 * The Hekate Project licenses this file to you 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.hekate.messaging;

import io.hekate.cluster.ClusterFilterSupport;
import io.hekate.cluster.ClusterNode;
import io.hekate.cluster.ClusterNodeFilter;
import io.hekate.core.Hekate;
import io.hekate.core.service.DefaultServiceFactory;
import io.hekate.core.service.Service;
import io.hekate.messaging.loadbalance.DefaultLoadBalancer;
import io.hekate.messaging.loadbalance.LoadBalancer;
import io.hekate.messaging.operation.Aggregate;
import io.hekate.messaging.operation.AggregateResult;
import io.hekate.messaging.operation.Broadcast;
import io.hekate.messaging.operation.Request;
import io.hekate.messaging.operation.RequestCallback;
import io.hekate.messaging.operation.Send;
import io.hekate.messaging.operation.SendCallback;
import io.hekate.messaging.operation.Subscribe;
import io.hekate.partition.Partition;
import java.util.List;

/**
 * « start hereMain entry point to messaging API.
 *
 * 

Overview

*

* Messaging service provides support for building message-oriented communications in the cluster of {@link Hekate} nodes. Message exchange * is based on the concept of messaging channels. Such channels hide all the complexities of managing resources (like socket and threads) * and provide a high level API for implementing various messaging patterns. *

* * * * *

Messaging Channels

*

* Messaging channel is a communication unit that can act as a sender, as a receiver or perform both of those roles simultaneously. * Channels provide support for unicast messaging (node to node communication) and broadcast messaging (node to many nodes communication). * Note that "unicast" and "broadcast" in this context are NOT related to UDP (all communications are TCP-based) and merely outline * the communication patterns. *

* * *

Configuring Channels

*

* Configuration of a messaging channel is represented by the {@link MessagingChannelConfig} class. *

* *

* Instances of this class can be registered via the {@link MessagingServiceFactory#withChannel(MessagingChannelConfig)} method. *

* *

* Example: *

* *
* *
* ${source: messaging/MessagingServiceJavadocTest.java#configure_channel} *
*
* Note: This example requires Spring Framework integration * (see HekateSpringBootstrap). * ${source: messaging/service-xsd.xml#example} *
*
* Note: This example requires Spring Framework integration * (see HekateSpringBootstrap). * ${source: messaging/service-bean.xml#example} *
*
* *

* For more details about the configuration options please see the documentation of {@link MessagingChannelConfig} class. *

* * *

Accessing Channels

*

* Channel can be accessed via the {@link MessagingService#channel(String, Class)} method, with the first parameter being the * {@link MessagingChannelConfig#setName(String) channel name} and the second parameter being the * {@link MessagingChannelConfig#of(Class) base type} of messages that can be transferred over that channel: * ${source: messaging/MessagingServiceJavadocTest.java#access_channel} *

* * *

Sending Messages

*

* {@link MessagingChannel} provides API for the following communication patterns: *

*
    *
  • Request - Submit a request and get a response
  • *
  • Send - Submit a one-way message that doesn't need a response
  • *
  • Subscribe - Submit a request (subscribe) and get multiple response chunks (updates)
  • *
  • Aggregate - Submit a one-way message to multiple nodes simultaneously
  • *
  • Broadcast - Submit a request to multiple nodes simultaneously and aggregate their responses
  • *
* * *

Request

*

* {@link Request} interface can be used for bidirectional communications with remote nodes using the request-response * pattern: * ${source: messaging/MessagingServiceJavadocTest.java#unicast_request_sync} *

* *

* ... or using a completely asynchronous callback-based approach: * ${source: messaging/MessagingServiceJavadocTest.java#unicast_request_async} *

* *

* For more details please see the documentation of {@link Request} interface. *

* * *

Send

*

* {@link Send} interface provides support for unidirectional communications (i.e. when remote node doesn't need to send * back a response) using the fire and forget approach: * ${source: messaging/MessagingServiceJavadocTest.java#unicast_send_sync} *

* *

* ... or using a completely asynchronous callback-based approach: * ${source: messaging/MessagingServiceJavadocTest.java#unicast_send_async} *

* *

* For more details please see the documentation of {@link Send} interface. *

* * *

Subscribe

*

* {@link Subscribe} interface can be used for bidirectional communications with remote nodes using the request-response pattern. * Unlike the basic {@link Request} operation, this operation doesn't end with the first response and continues receiving updates unless * the very final response is received: * ${source: messaging/MessagingServiceJavadocTest.java#unicast_subscribe_async} *

* *

* For more details please see the documentation of {@link Subscribe} interface. *

* * *

Aggregate

*

* {@link Aggregate} interface can be used for bidirectional communications by submitting a message to multiple nodes and * gathering (aggregating) replies from those nodes. Results of such aggregation are represented by the {@link AggregateResult} interface. * This interface provides methods for analyzing responses from remote nodes and checking for possible failures. *

* *

* Below is the example of synchronous aggregation: * ${source: messaging/MessagingServiceJavadocTest.java#aggregate_sync} *

* *

* ... or using a completely asynchronous callback-based approach: * ${source: messaging/MessagingServiceJavadocTest.java#aggregate_async} *

* *

* For more details please see the documentation of {@link Aggregate} interface. *

* * *

Broadcast

*

* {@link Broadcast} interface provides support for unidirectional broadcasting (i.e. when remote nodes do not need to * send a reply and no aggregation should take place) using the fire and forget approach. *

* *

* Below is the example of synchronous broadcast: * ${source: messaging/MessagingServiceJavadocTest.java#broadcast_sync} *

*

* ... or using a completely asynchronous callback-based approach: * ${source: messaging/MessagingServiceJavadocTest.java#broadcast_async} *

* *

* For more details please see the documentation of {@link Broadcast} interface. *

* * *

Receiving Messages

*

* Messaging channel can receive messages from remote nodes by registering an instance of {@link MessageReceiver} interface via the {@link * MessagingChannelConfig#setReceiver(MessageReceiver)} method. *

* *

* Important: Only one receiver can be registered per each messaging channel. *

* *

* Received messages are represented by the {@link Message} interface. This interface provides methods for {@link Message#payload() * getting} * the payload of a received message as well as methods for {@link Message#reply(Object) replying} to that message. *

* *

* Below is the example of {@link MessageReceiver} implementation: * ${source: messaging/MessagingServiceJavadocTest.java#message_receiver} *

* * *

Routing and Load Balancing

*

* Every messaging channel uses an instance of {@link LoadBalancer} interface to perform routing of unicast operations * (like {@link MessagingChannel#newSend(Object) send(...)} and {@link MessagingChannel#newRequest(Object) request(...)}). Load balancer * can * be pre-configured via the {@link MessagingChannelConfig#setLoadBalancer(LoadBalancer)} method or specified dynamically via the {@link * MessagingChannel#withLoadBalancer(LoadBalancer)} method. If load balancer is not specified then messaging channel will fall back to the * {@link DefaultLoadBalancer}. *

*

* Note that load balancing does not get applied to broadcast operations (like {@link MessagingChannel#newBroadcast(Object)} and {@link * MessagingChannel#newAggregate(Object)}). Such operations are submitted to all nodes within the channel's cluster topology. * Please see the "Cluster topology filtering" section for details of how to control the * channel's cluster topology. *

* * *

Consistent Routing

*

* Applications can provide an affinity key to the {@link LoadBalancer} so that it could perform consistent routing based on some * application-specific criteria. For example, if the {@link DefaultLoadBalancer} is being used by the messaging channel then it will make * sure that all messages with the same affinity key will always be routed to the same cluster node (unless the cluster topology doesn't * change) by using the channel's {@link MessagingChannel#partitions() partition mapper}. Custom implementations of the {@link * LoadBalancer} interface can use their own algorithms for consistent routing. *

* *

* Affinity key can for unicast operations can be specified via the following methods: *

*
    *
  • {@link Send#withAffinity(Object)}
  • *
  • {@link Request#withAffinity(Object)}
  • *
  • {@link Subscribe#withAffinity(Object)}
  • *
* *

* Affinity key can for broadcast operations can be specified via the following methods: *

*
    *
  • {@link Broadcast#withAffinity(Object)}
  • *
  • {@link Aggregate#withAffinity(Object)}
  • *
* *

* Note: If affinity key is specified for a broadcast operation then messaging channel will use its * {@link MessagingChannel#partitions() partition mapper} to select the target {@link Partition} for that key. Once the partition is * selected then all of its {@link Partition#nodes() nodes} will be used for broadcast (i.e. {@link Partition#primaryNode() primary node} + * {@link Partition#backupNodes() backup nodes}). *

* * *

Thread Affinity

*

* Besides providing a hint to the {@link LoadBalancer}, specifying an affinity key also instructs the messaging channel to process all * messages of the same affinity key on the same thread. This applies both to sending a message (see {@link SendCallback} or {@link * RequestCallback}) and to receiving a message (see {@link MessageReceiver#receive(Message)}). *

* * *

Cluster Topology Filtering

*

* It is possible to narrow down the list of nodes that are visible to the {@link MessagingChannel} by setting a {@link ClusterNodeFilter}. * Such filter can be pre-configured via the {@link MessagingChannelConfig#setClusterFilter(ClusterNodeFilter)} method or set dynamically * via the {@link MessagingChannel#filter(ClusterNodeFilter)} method. *

* *

* Note that the {@link MessagingChannel} interface extends the {@link ClusterFilterSupport} interface, which gives it a number of shortcut * methods for dynamic filtering of the cluster topology: *

*
    *
  • {@link MessagingChannel#forRemotes()}
  • *
  • {@link MessagingChannel#forRole(String)}
  • *
  • {@link MessagingChannel#forProperty(String)}
  • *
  • {@link MessagingChannel#forNode(ClusterNode)}
  • *
  • {@link MessagingChannel#forOldest()}
  • *
  • {@link MessagingChannel#forYoungest()}
  • *
  • ...{@link ClusterFilterSupport etc}
  • *
* *

* If filter is specified then all messaging operations will be distributed among only those nodes that match the filter's criteria. *

* * *

Thread Pooling

*

* Messaging service manages a pool of threads for each of its registered channels. The following thread pools are managed: *

* *
    *
  • * NIO thread pool - thread pool for managing NIO socket channels. The size of this thread pool is controlled by the {@link * MessagingChannelConfig#setNioThreads(int)} configuration option. *
  • *
  • * Worker thread pool - Optional thread pool to offload messages processing work from NIO threads. The size of this pool is * controlled by the {@link MessagingChannelConfig#setWorkerThreads(int)} configuration option. It is recommended to set this parameter in * case if message processing is a heavy operation that can block NIO thread for a long time. *
  • *
* * @see MessagingServiceFactory */ @DefaultServiceFactory(MessagingServiceFactory.class) public interface MessagingService extends Service { /** * Returns all channels that are {@link MessagingServiceFactory#setChannels(List) registered} within this service. * * @return Channels or an empty list if there are no registered channels. */ List> allChannels(); /** * Returns an unchecked messaging channel for the specified name. * * @param name Channel name (see {@link MessagingChannelConfig#setName(String)}). * * @return Messaging channel. * * @throws IllegalArgumentException if there is no such channel configuration with the specified name. * @see MessagingChannelConfig */ MessagingChannel channel(String name) throws IllegalArgumentException; /** * Returns a type-safe messaging channel for the specified name. * * @param name Channel name (see {@link MessagingChannelConfig#setName(String)}). * @param baseType Base type of messages that can be supported by the messaging channel (see {@link * MessagingChannelConfig#MessagingChannelConfig(Class)}). * @param Base type of messages that can be supported by the messaging channel. * * @return Messaging channel. * * @throws IllegalArgumentException if there is no such channel configuration with the specified name. * @see MessagingChannelConfig */ MessagingChannel channel(String name, Class baseType) throws IllegalArgumentException; /** * Returns {@code true} if this service has a messaging channel with the specified name. * * @param channelName Channel name (see {@link MessagingChannelConfig#setName(String)}). * * @return {@code true} if messaging channel exists. */ boolean hasChannel(String channelName); }