org.apache.flink.runtime.rpc.RpcEndpoint Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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 org.apache.flink.runtime.rpc;
import org.apache.flink.api.common.time.Time;
import org.apache.flink.util.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import static org.apache.flink.util.Preconditions.checkNotNull;
/**
* Base class for RPC endpoints. Distributed components which offer remote procedure calls have to
* extend the RPC endpoint base class. An RPC endpoint is backed by an {@link RpcService}.
*
* Endpoint and Gateway
* To be done...
* Single Threaded Endpoint Execution
*
* All RPC calls on the same endpoint are called by the same thread
* (referred to as the endpoint's main thread).
* Thus, by executing all state changing operations within the main
* thread, we don't have to reason about concurrent accesses, in the same way in the Actor Model
* of Erlang or Akka.
*
*
The RPC endpoint provides provides {@link #runAsync(Runnable)}, {@link #callAsync(Callable, Time)}
* and the {@link #getMainThreadExecutor()} to execute code in the RPC endpoint's main thread.
*/
public abstract class RpcEndpoint implements RpcGateway {
protected final Logger log = LoggerFactory.getLogger(getClass());
// ------------------------------------------------------------------------
/** RPC service to be used to start the RPC server and to obtain rpc gateways. */
private final RpcService rpcService;
/** Unique identifier for this rpc endpoint. */
private final String endpointId;
/** Interface to access the underlying rpc server. */
protected final RpcServer rpcServer;
/** A reference to the endpoint's main thread, if the current method is called by the main thread. */
final AtomicReference currentMainThread = new AtomicReference<>(null);
/** The main thread executor to be used to execute future callbacks in the main thread
* of the executing rpc server. */
private final MainThreadExecutor mainThreadExecutor;
/**
* Initializes the RPC endpoint.
*
* @param rpcService The RPC server that dispatches calls to this RPC endpoint.
* @param endpointId Unique identifier for this endpoint
*/
protected RpcEndpoint(final RpcService rpcService, final String endpointId) {
this.rpcService = checkNotNull(rpcService, "rpcService");
this.endpointId = checkNotNull(endpointId, "endpointId");
this.rpcServer = rpcService.startServer(this);
this.mainThreadExecutor = new MainThreadExecutor(rpcServer);
}
/**
* Initializes the RPC endpoint with a random endpoint id.
*
* @param rpcService The RPC server that dispatches calls to this RPC endpoint.
*/
protected RpcEndpoint(final RpcService rpcService) {
this(rpcService, UUID.randomUUID().toString());
}
/**
* Returns the rpc endpoint's identifier.
*
* @return Rpc endpoint's identifier.
*/
public String getEndpointId() {
return endpointId;
}
// ------------------------------------------------------------------------
// Start & shutdown & lifecycle callbacks
// ------------------------------------------------------------------------
/**
* Starts the rpc endpoint. This tells the underlying rpc server that the rpc endpoint is ready
* to process remote procedure calls.
*
* IMPORTANT: Whenever you override this method, call the parent implementation to enable
* rpc processing. It is advised to make the parent call last.
*
* @throws Exception indicating that something went wrong while starting the RPC endpoint
*/
public void start() throws Exception {
rpcServer.start();
}
/**
* Stops the rpc endpoint. This tells the underlying rpc server that the rpc endpoint is
* no longer ready to process remote procedure calls.
*/
protected final void stop() {
rpcServer.stop();
}
/**
* User overridable callback.
*
*
This method is called when the RpcEndpoint is being shut down. The method is guaranteed
* to be executed in the main thread context and can be used to clean up internal state.
*
*
IMPORTANT: This method should never be called directly by the user.
*
* @return Future which is completed once all post stop actions are completed. If an error
* occurs this future is completed exceptionally
*/
public abstract CompletableFuture postStop();
/**
* Triggers the shut down of the rpc endpoint. The shut down is executed asynchronously.
*
* In order to wait on the completion of the shut down, obtain the termination future
* via {@link #getTerminationFuture()}} and wait on its completion.
*/
public final void shutDown() {
rpcService.stopServer(rpcServer);
}
// ------------------------------------------------------------------------
// Basic RPC endpoint properties
// ------------------------------------------------------------------------
/**
* Returns a self gateway of the specified type which can be used to issue asynchronous
* calls against the RpcEndpoint.
*
*
IMPORTANT: The self gateway type must be implemented by the RpcEndpoint. Otherwise
* the method will fail.
*
* @param selfGatewayType class of the self gateway type
* @param type of the self gateway to create
* @return Self gateway of the specified type which can be used to issue asynchronous rpcs
*/
public C getSelfGateway(Class selfGatewayType) {
if (selfGatewayType.isInstance(rpcServer)) {
@SuppressWarnings("unchecked")
C selfGateway = ((C) rpcServer);
return selfGateway;
} else {
throw new RuntimeException("RpcEndpoint does not implement the RpcGateway interface of type " + selfGatewayType + '.');
}
}
/**
* Gets the address of the underlying RPC endpoint. The address should be fully qualified so that
* a remote system can connect to this RPC endpoint via this address.
*
* @return Fully qualified address of the underlying RPC endpoint
*/
@Override
public String getAddress() {
return rpcServer.getAddress();
}
/**
* Gets the hostname of the underlying RPC endpoint.
*
* @return Hostname on which the RPC endpoint is running
*/
@Override
public String getHostname() {
return rpcServer.getHostname();
}
/**
* Gets the main thread execution context. The main thread execution context can be used to
* execute tasks in the main thread of the underlying RPC endpoint.
*
* @return Main thread execution context
*/
protected MainThreadExecutor getMainThreadExecutor() {
return mainThreadExecutor;
}
/**
* Gets the endpoint's RPC service.
*
* @return The endpoint's RPC service
*/
public RpcService getRpcService() {
return rpcService;
}
/**
* Return a future which is completed with true when the rpc endpoint has been terminated.
* In case of a failure, this future is completed with the occurring exception.
*
* @return Future which is completed when the rpc endpoint has been terminated.
*/
public CompletableFuture getTerminationFuture() {
return rpcServer.getTerminationFuture();
}
// ------------------------------------------------------------------------
// Asynchronous executions
// ------------------------------------------------------------------------
/**
* Execute the runnable in the main thread of the underlying RPC endpoint.
*
* @param runnable Runnable to be executed in the main thread of the underlying RPC endpoint
*/
protected void runAsync(Runnable runnable) {
rpcServer.runAsync(runnable);
}
/**
* Execute the runnable in the main thread of the underlying RPC endpoint, with
* a delay of the given number of milliseconds.
*
* @param runnable Runnable to be executed
* @param delay The delay after which the runnable will be executed
*/
protected void scheduleRunAsync(Runnable runnable, Time delay) {
scheduleRunAsync(runnable, delay.getSize(), delay.getUnit());
}
/**
* Execute the runnable in the main thread of the underlying RPC endpoint, with
* a delay of the given number of milliseconds.
*
* @param runnable Runnable to be executed
* @param delay The delay after which the runnable will be executed
*/
protected void scheduleRunAsync(Runnable runnable, long delay, TimeUnit unit) {
rpcServer.scheduleRunAsync(runnable, unit.toMillis(delay));
}
/**
* Execute the callable in the main thread of the underlying RPC service, returning a future for
* the result of the callable. If the callable is not completed within the given timeout, then
* the future will be failed with a {@link TimeoutException}.
*
* @param callable Callable to be executed in the main thread of the underlying rpc server
* @param timeout Timeout for the callable to be completed
* @param Return type of the callable
* @return Future for the result of the callable.
*/
protected CompletableFuture callAsync(Callable callable, Time timeout) {
return rpcServer.callAsync(callable, timeout);
}
// ------------------------------------------------------------------------
// Main Thread Validation
// ------------------------------------------------------------------------
/**
* Validates that the method call happens in the RPC endpoint's main thread.
*
* IMPORTANT: This check only happens when assertions are enabled,
* such as when running tests.
*
*
This can be used for additional checks, like
*
{@code
* protected void concurrencyCriticalMethod() {
* validateRunsInMainThread();
*
* // some critical stuff
* }
* }
*/
public void validateRunsInMainThread() {
assert currentMainThread.get() == Thread.currentThread();
}
// ------------------------------------------------------------------------
// Utilities
// ------------------------------------------------------------------------
/**
* Executor which executes runnables in the main thread context.
*/
protected static class MainThreadExecutor implements Executor {
private final MainThreadExecutable gateway;
MainThreadExecutor(MainThreadExecutable gateway) {
this.gateway = Preconditions.checkNotNull(gateway);
}
@Override
public void execute(@Nonnull Runnable runnable) {
gateway.runAsync(runnable);
}
}
}