PHP (8.1) Async RPC based on Amp
This package can be installed as a Composer dependency.
composer require makaronnik/amphp-rpc
- PHP 8.1+
This is an RPC (remote procedure calls) package that works in asynchronous, non-blocking mode, based on Amp.
Based on ideas implemented in amphp/rpc, but enhanced with advanced functionality useful for communication and load balancing in microservice architecture.
Used in the Amphp Microservice Framework as one of the main methods of inter-service communication. It also serves as the basis for the Amphp Request Proxy package.
- Calling procedures (methods) by the client on a remote server (service), with the possibility of obtaining a result.
- Exceptions that occur on the server during the execution of procedures are caught and transferred to the client, where they can be caught and processed.
- Different response options from the server for various situations (redirect to another host/ip, try again after n seconds, etc.) and the corresponding client reaction. You can implement your own response options and their processing using the interceptor mechanism.
- Cache for
['$host . $className . $methodName' => 'URI']
pairs to store and reuse redirect targets to reduce the number of possible intermediate requests
Remote objects are, in fact, instances of classes that are generated on the side of the RPC server when an RPC request is received from an RPC client. These classes MUST be on the server side and implement their interfaces. The public methods of these interfaces MUST return an Amp\Promise object or an exception will be thrown. These methods are called Remote Procedures. They will be executed on the server as a result of calling similar methods on the proxy object on the client. In order for this to happen, the map between the remote object's interface and its implementation must be registered. This is done using the registerRemoteObject
method of the RpcRegistry object, which is passed to the RpcServer constructor. The identical interface of the remote object MUST be on BOTH the server and the client. An identical interface on the client is needed to create the appropriate proxy for a remote object, on the client side, by calling the createProxy
method on the RpcProxyObjectFactory.
use Amp\Promise;
interface SimpleCalcInterface
{
public function add(int $a, int $b): Promise; // remote procedure add() API
}
use Amp\Promise;
use function Amp\call;
class SimpleCalc implements SimpleCalcInterface
{
public function add(int $a, int $b): Promise // remote procedure add() implementation
{
return call(fn (): int => $a + $b); // returns Promise<int>
}
}
$registry = new RpcRegistry();
$registry->registerRemoteObject(SimpleCalcInterface::class, SimpleCalc::class);
$requestHandler = new RpcRequestHandler(new NativeSerializer(), $registry);
$rpcServer = (new RpcServerFactory(8181, $registry, $requestHandler))->getRpcServer();
yield $rpcServer->start();
$rpcClient = (new RpcClientBuilder('localhost', 8181))->build();
$proxyCalc = yield $proxyObjectsFactory->createProxy($rpcClient, SimpleCalcInterface::class);
$addResult = yield $proxyCalc->add(5, 7); // int: 12
You can find complete examples in the examples and test directories.
-
RpcServer - starts a server serving RPC requests. It is preferable to configure it via RpcServerFactory.
-
RpcRequestHandler - handles RPC requests. Has a
registerInterceptor
method for registering request interceptors that are executed before the main request processing logic. After the request has been processed, it returns the appropriate RPC response. -
RpcRegistry - stores links between interfaces of remote objects and their implementations. If you do not register a remote interface mapping with its implementation, then the server will not know how to handle the RPС request. Passed to the server's and handler's constructors.
-
RpcClient - makes RPC requests to the server. It is preferable to configure it via RpcClientBuilder. Not used directly, passed to the remote object's proxy creation method.
-
RpcResponseHandler - handles responses from the RPC server and returns the result of the RPC request or throws an appropriate exception.
-
RpcProxyObjectFactory - creates a proxy for the remote object. It has a single public method
createProxy
that takes as parameters an RpcClient object and the fully qualified interface name of the target remote object. If the previously generated proxy cannot be found, then the generator is used. -
RpcProxyObjectGenerator - generates a proxy class file for the remote object. It is passed to the factory constructor, where it is used.
-
RpcProxyClassFileLocator - configures a directory path to store generated proxy classes for remote objects. It is passed to the factory constructor, where it is used.
-
SuccessRpcResponseFactory - creates an RPC response containing the serialized result of the RPC request.
-
ThrowableRpcResponseFactory - creates an RPC response containing data about an exception that occurred on the server during the processing of an RPC request.
-
RetryRpcResponseFactory - creates an RPC response that tells the RPC client (handler) to retry the request after a certain period of time.
-
RedirectRpcResponseFactory - creates an RPC response that tells the RPC client (handler) to resubmit the request, but to a different host, or with a different port or path.
-
UnprocessedCallException - thrown if the RPC request was not completed on the RPC server and it can be safely retried.
-
PossiblyProcessedCallException - thrown if the RPC request was partially completed on the RPC server and its retry may be unsafe.
-
ProcessedCallException - thrown if the request was successful on the server, but there was a problem getting or returning the result, and repeating this request is likely to be unsafe.
-
RetriesCountExceededException - thrown if the allowed number of retries has been exceeded. This exception inherits from UnprocessedCallException so the request can be retried safely.
-
RedirectCountExceededException - thrown if the allowed number of redirects has been exceeded. This exception inherits from UnprocessedCallException so the request can be retried safely.
makaronnik/amphp-rpc
follows the semver semantic versioning specification.
The MIT License (MIT). Please see LICENSE
for more information.