In this guide we will create a remote key-value storage using RPC module.
App will have 2 basic operations: put and get and use RPC as a communication protocol.
When writing distributed application the common concern is what protocol to use for communication. There are two main
While HTTP is more popular and well-specified, it has some overhead. When performance is a significant aspect of application,
you should use something faster than HTTP. And for this purpose DataKernel framework has an RPC module which is based on
fast serializers and custom optimized communication protocol, which allows to significantly improve application performance.
Since we have two basic operations to implement (put and get), let’s first write down classes that will be used for
communication between client and server. Specifically, PutRequest, PutResponse, GetRequest and GetResponse.
Instances of these classes will be serialized using fast DataKernel Serializer, which requires some meta information
about this classes, provided with appropriate annotations. The basic rules are:
Use @Serialize annotation with order number on getter of property. Ordering provides better compatibility in case
classes are changed.
Use @Deserialize annotation with property name (which should be same as in getter) in constructor.
Use @SerializeNullable on properties that can have null values.
Thereby, classes for communication should look like following:
Next, let’s write a simple implementation of key-value storage:
3. Create client and server
Now, let’s write down an AbstractModule for RPC server using DataKernel Boot, that will handle “get” and “put” requests
As you can see, in order to properly create RpcServer we should indicate all the classes which will be sent between
client and server, and specify appropriate RequestHandler for each request class.
Since Java 1.8 they can be expressed as lambdas, so they are represented as third arguments in these lines.
Next, create a launcher for RPC server:
Now, let’s write RPC client. In order to create RPC client we should again indicate all of the classes that will be used
for communication and specify RpcStrategy. There is a whole bunch of strategies in RPC module (such as single-server,
first-available, round-robin, sharding and so on). The nice thing about them is that all strategies can be combined. For
example, if you want to dispatch requests between 2 shards, and each shard actually contains main and reserve servers,
you can easily tell RPC client to dispatch request in a proper way using the following code:
But since we have only one server, we will just use single-server strategy:
Let’s also build ClientLauncher. In run() we will consider command line arguments and make appropriate requests
As you can see, sendRequest() method returns a CompletionStage, at which we could listen for its results asynchronously
Congratulation! We’ve finished writing code for this app.
First, launch server.
Open ServerLauncher class and run its main() method.
Then make a “put” request.
Open ClientLauncher class, which is located at datakernel -> examples -> remote-key-value-storage
and set up program arguments to --put key1 value1. For IntelliJ IDEA: Run -> Edit configurations ->
|Run/Debug Configurations -> |Program arguments -> --put key1 value1||. Then run launcher’s main() method.
You will see the following output:
Finally, make a “get” request.
Open ClientLauncher class again, and set up program arguments to --get key1. For IntelliJ IDEA: Run ->
Edit configurations -> |Run/Debug Configurations -> |Program arguments -> --get key1||. Then run main() method of the
You will see the following output:
Congratulations, you’ve just created a remote key-value storage with RPC communication protocol!