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

io.hekate.rpc.RpcService 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.rpc;

import io.hekate.cluster.ClusterFilterSupport;
import io.hekate.cluster.ClusterNode;
import io.hekate.cluster.ClusterView;
import io.hekate.core.Hekate;
import io.hekate.core.HekateBootstrap;
import io.hekate.core.service.DefaultServiceFactory;
import io.hekate.core.service.Service;
import io.hekate.messaging.loadbalance.LoadBalancerContext;
import io.hekate.messaging.retry.GenericRetryConfigurer;
import io.hekate.partition.RendezvousHashMapper;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;

/**
 * « start hereMain entry point to Remote Procedure Call (RPC) API.
 *
 *
 * 

Overview

*

* This service provides support for remote calls of Java objects in a cluster of {@link Hekate} nodes. Each such object must declare one * or more Java interfaces marked with the @{@link Rpc} annotation. Client nodes use this interface to build a proxy object that * transparently executes all local methods calls on remote nodes. *

* * * * *

Service Configuration

*

* {@link RpcService} can be configured and registered in {@link HekateBootstrap} with the help of {@link RpcServiceFactory} as shown in * the example below: *

* *
* *
* ${source: rpc/RpcServiceJavadocTest.java#configure} *
*
* Note: This example requires Spring Framework integration * (see HekateSpringBootstrap). * ${source: rpc/rpc-xsd.xml#example} *
*
* Note: This example requires Spring Framework integration * (see HekateSpringBootstrap). * ${source: rpc/rpc-bean.xml#example} *
*
* * *

RPC Interface

*

* Every object that is exposed for RPC access must implement at least one @{@link Rpc}-annotated interface. * Below is the example of a simple RPC interface: * ${source: rpc/RpcServiceJavadocTest.java#interface} *

* *

Synchronous vs Asynchronous

*

* RPC interfaces can declare both synchronous and asynchronous methods. Synchronous methods are regular methods that return an object of * arbitrary Java type. Invocations of such methods are always synchronous and blocks the client thread unless remote invocation is * completed. *

* *

* Asynchronous methods must use a {@link CompletableFuture} as their result type. Such methods are always executed asynchronously by the * {@link RpcService} without blocking the client thread. *

* *

Aggregate and Broadcast

*

* RPC service provides support for broadcast/aggregate operations. Each such RPC operations gets submitted to multiple nodes at once and * all RPC results from those nodes gets aggregated into a single result object. *

*

* In order to enable such functionality, an RPC method must be annotated with @{@link RpcAggregate}. Such method must be declared * with one of the following result types: *

*
    *
  • {@link List}
  • *
  • {@link Set}
  • *
  • {@link Map}
  • *
  • {@link Collection}
  • *
  • {@link CompletableFuture}{@code <}{@link List}|{@link Set}|{@link Collection}|{@link Map}{@code >}
  • *
* *

* If no results are expected to be returned by an RPC method then such method must be annotated with @{@link RpcBroadcast} and must have a * {@code void} return type (or {@link CompletableFuture}{@code } for asynchronous calls). *

* *

Arguments Splitting

*

* RPC service provides support for Map/Reduce style of data processing. This can be achieved by placing @{@link RpcSplit} annotation on * one of an @{@link RpcAggregate}-annotated method's parameter. Such parameter must be of one of the following types: *

*
    *
  • {@link List}
  • *
  • {@link Set}
  • *
  • {@link Map}
  • *
  • {@link Collection}
  • *
*

* If @{@link RpcSplit} annotation is present then the value of that parameter will be split into smaller chunks (sub-collections) based * on the number of available cluster nodes. All chunks will be evenly distributed among the cluster nodes for parallel processing and once * processing on all nodes is completed then results of each node will be aggregated the same way as during the regular {@link * RpcAggregate} * call. *

* *

Versioning

*

* RPC interface can define client compatibility rules by using the interface versioning approach. Version can be specified via {@link * Rpc#version()} and {@link Rpc#minClientVersion()} attributes. {@link Rpc#version()} defines the current version of the RPC interface * while {@link Rpc#minClientVersion()} defines the minimum version of this interface that can be used on the client side. If client * detects that its local version is less than the minimum required version of the server then such client will not route any RPC requests * to such server. *

*

* Consider the following scenario: *

*
    *
  1. Same jar with an RPC interface {@link Rpc#version()}{@code =1} is deployed both on the client and on the server nodes
  2. *
  3. After some time a new {@link Rpc#version()}={@code 2} of this interface is implemented (possibly with some breaking changes of * API)
  4. *
  5. New jar file with {@link Rpc#version()}{@code =2} is deployed on a new node
  6. *
  7. At this point, if {@link Rpc#minClientVersion()} is set to {@code 2} then old client with version {@code 1} will know that its API * is not compatible with the server version {@code 2} and will not try to route any requests to such server.
  8. *
  9. Alternatively, if {@link Rpc#minClientVersion()} is set to {@code 1} (meaning that there were no breaking changes) then old client * will still be able to route requests to the new server.
  10. *
* * *

RPC Server

*

* RPC server is a Java class that implements one or more @{@link Rpc}-annotated interfaces. Below is the example of such class: * ${source: rpc/RpcServiceJavadocTest.java#impl} *

* *

Server Registration

*

* Each RPC server must be registered within the {@link RpcServiceFactory} in order to be exposed for remote access. Configuration of each * RPC server is represented by the {@link RpcServerConfig} class. Below is the example of RPC server registration: * ${source: rpc/RpcServiceJavadocTest.java#server} *

* *

Tagging

*

* If multiple servers implement the same RPC interface and must be deployed to the same {@link Hekate} node then each such server must * have an additional qualifier that will help RPC clients to distinguish which exact RPC server they are communicating with. *

* *

* Such qualifiers are called "tags" and can be specified for each RPC server via {@link RpcServerConfig#setTags(Set)} method. If one or * more tags are specified for an RPC server then RPC clients of that server must be constructed via * {@link RpcService#clientFor(Class, String)} method. The {@code tag} parameter of this method must match with one of the RPC server's * tags. If client doesn't specify a tag or if tag doesn't match any of the RPC server's tags then such client will not be able to discover * and communicate with that server. *

* * *

RPC Client

*

* The client side of RPC communication is represented by a Java reflections proxy of an @{@link Rpc}-annotated interface. Such proxies can * be constructed on an RPC client node via {@link RpcService#clientFor(Class)} method. This method returns an instance of {@link * RpcClientBuilder} interface that provides support for dynamically configuring different aspects of a client proxy object (f.e. timeouts, * load balancing, retry policies, etc). The client proxy object can be created by calling the {@link RpcClientBuilder#build()} method as * in the example below: * ${source: rpc/RpcServiceJavadocTest.java#client} *

* *

* Instead of configuring all of the RPC client's options dynamically, it is possible to pre-configure some of those options by * registering an instance of the {@link RpcClientConfig} class for each such client individually * (see {@link RpcServiceFactory#setClients(List)} method). *

* *

* If such configuration is registered then an instance of {@link RpcClientBuilder}б that is returned from the {@link #clientFor(Class)} * method, will contain all of the pre-preconfigured options. *

* *

* For the complete list of pre-configurable options please see the documentation of {@link RpcClientConfig} class. *

* * *

Routing and Load Balancing

*

* Every RPC client proxy uses an instance of {@link RpcLoadBalancer} interface to perform routing of RPC unicast operations. Load balancer * can be pre-configured via the {@link RpcClientConfig#setLoadBalancer(RpcLoadBalancer)} method or specified dynamically via the {@link * RpcClientBuilder#withLoadBalancer(RpcLoadBalancer)} method. If load balancer is not specified then the RPC client will fall back to * the {@link DefaultRpcLoadBalancer}. *

*

* Note that load balancing does not get applied to RPC broadcast operations (i.e. @{@link RpcAggregate}-annotated methods). Such * operations are submitted to all nodes within the RPC client's cluster topology. * Please see the "Cluster topology filtering" section for details of how to control the RPC client's * cluster topology. *

* *

Consistent Routing

*

* Applications can provide an affinity key to the {@link RpcLoadBalancer} so that it could perform consistent routing based on some * application-specific criteria. For example, if the {@link DefaultRpcLoadBalancer} is being used by the RPC client then it will * utilize the the {@link RendezvousHashMapper} algorithm make sure that all RPC operations with the same affinity key will always be * routed to the same cluster node (unless the cluster topology doesn't change). Custom implementations of the {@link RpcLoadBalancer} * interface can use their own algorithms for consistent routing. *

*

* Affinity key can be specified by annotating one of the RPC method's parameters with @{@link RpcAffinityKey}. If such such annotation is * present then RPC client will transparently use the value of that parameter as an {@link LoadBalancerContext#affinityKey() affinity key} * for the {@link RpcLoadBalancer}. *

*

* Note that there can be only one @{@link RpcAffinityKey}-annotated parameter per RPC method and value of that parameter can't be {@code * null}. Also it is important to make sure that type of that parameter provides consistent implementation of {@link Object#hashCode()} and * {@link Object#equals(Object)} methods. *

* *

Thread Affinity

*

* Besides providing a hint to the {@link RpcLoadBalancer}, specifying an {@link RpcAffinityKey} also instructs the RPC service to * process all RPC operations of the same affinity key on the same thread. This applies both to the server side and to the client side of * RPC interactions. Thus, if RPC method returns an instance of {@link CompletableFuture} then such future will be * {@link CompletableFuture#complete(Object) completed} on a thread that is mapped to the value of {@link RpcAffinityKey}. *

* * *

Cluster Topology Filtering

*

* Routing of RPC operations among the cluster nodes is based on the RPC client's cluster view. By default, it includes all of the cluster * nodes that have a compliant {@link RpcServerConfig RPC server}. *

* *

* It is possible to narrow down the list of client-visible nodes via the following methods of the {@link RpcClientBuilder} class: *

*
    *
  • {@link RpcClientBuilder#forRemotes() forRemotes()}
  • *
  • {@link RpcClientBuilder#forRole(String) forRole(String)}
  • *
  • {@link RpcClientBuilder#forProperty(String) forProperty(String)}
  • *
  • {@link RpcClientBuilder#forNode(ClusterNode) forNode(ClusterNode)}
  • *
  • {@link RpcClientBuilder#forOldest() forOldest()}
  • *
  • {@link RpcClientBuilder#forYoungest() forYoungest()}
  • *
  • ...and other methods from the {@link ClusterFilterSupport} base interface
  • *
* *

* If filtering rules are specified for an RPC client then all RPC operations of that client will be distributed only among those nodes * that do match the filtering criteria. *

* * *

Retrying on Error

*

* RPC service provides support for specifying a retry behavior in case of a remote invocation error. *

* *

* This can be done by marking a method of an RPC interface with the {@link RpcRetry} annotation. If such annotation is present on a method * then all failed invocations of that method will be transparently retried based on the annotation attribute values. *

* *

* {@link RpcRetry}'s attributes provide support for specifying different parameters of retry behavior (like maximum attempts, delay * between retries, etc). It is also possible to configure default values of those attributes by registering an instance * of {@link GenericRetryConfigurer} interface in the RPC client's configuration via the * {@link RpcClientConfig#setRetryPolicy(GenericRetryConfigurer)} or at the RPC client construction time via the * {@link RpcClientBuilder#withRetryPolicy(GenericRetryConfigurer)} method. *

* * @see RpcServiceFactory */ @DefaultServiceFactory(RpcServiceFactory.class) public interface RpcService extends Service { /** * Constructs a new RPC client proxy builder for the specified Java interface and tag. * *

* This method returns an instance of {@link RpcClientBuilder} that can be used to configure and {@link RpcClientBuilder#build() build} * a Java proxy object for the specified RPC interface that will redirect all method invocations to remote cluster nodes. *

* *

* Note: Some of the builder's options can be preconfigured via {@link RpcClientConfig} (see its javadocs for more details). *

* * @param type RPC interface (must be an @{@link Rpc}-annotated Java interface). * @param tag Tag (see {@link RpcServerConfig#setTags(Set)}). * @param RPC interface. * * @return Builder. */ RpcClientBuilder clientFor(Class type, String tag); /** * Constructs a new RPC client proxy builder for the specified Java interface. * *

* This method returns an instance of {@link RpcClientBuilder} that can be used to configure and {@link RpcClientBuilder#build() build} * a Java proxy object for the specified RPC interface that will redirect all method invocations to remote cluster nodes. *

* *

* Note: Some of the builder's options can be preconfigured via {@link RpcClientConfig} (see its javadocs for more details). *

* * @param type RPC interface (must be an @{@link Rpc}-annotated Java interface). * @param RPC interface. * * @return Builder. */ RpcClientBuilder clientFor(Class type); /** * Returns the cluster view that includes only those nodes that have a {@link #servers() server} for the specified * {@link Rpc}-annotated Java interface and tag. * * @param type {@link Rpc}-annotated Java interface. * @param tag Tag (see {@link RpcServerConfig#setTags(Set)}). * * @return Cluster view that includes only those node that have a {@link #servers() server} for the specified {@link Rpc}-annotated * Java interface and tag. */ ClusterView clusterOf(Class type, String tag); /** * Returns the cluster view that includes only those nodes that have a {@link #servers() server} for the specified * {@link Rpc}-annotated Java interface. * * @param type {@link Rpc}-annotated Java interface. * * @return Cluster view that includes only those node that have a {@link #servers() server} for the specified {@link Rpc}-annotated * Java interface. */ ClusterView clusterOf(Class type); /** * Returns an immutable list of all RPC servers registered on this node. * * @return Immutable list of RPC servers. * * @see RpcServiceFactory#setServers(List) */ List servers(); /** * Returns the size of a thread pool for handling NIO-based socket connections * (see {@link RpcServiceFactory#setNioThreads(int)}). * * @return Size of a thread pool for handling NIO-based socket connections. */ int nioThreads(); /** * Returns the worker thread pool size (see {@link RpcServiceFactory#setWorkerThreads(int)}). * * @return Worker thread pool size. */ int workerThreads(); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy