Thursday, October 8, 2015

Send data between 2 JVMs using protocol buffer RPC

Protocol Buffer is flexible,efficient,automated,inter-operable solution for serialization.It makes writing remote procedure call(rpc) simpler and easy to implement by serializing/deserializing request/response objects. But, it doesn't implement any transport details by itself.
Now, protobuf-socket-rpc is a simple tcp/ip socket based rpc implementation in java, python for protobuf rpc services.

In this blog, we will discuss about implementing a rpc program
1. Creating the request/response object using .proto
2. Creating the Service class using .proto
3. Implementing the Service method
4. Write the Server application
5. Write Client application

Prerequisites:
1. Java installed. If not, install java.
2. Protocol Buffer 2.4.0 installed. If not, install
3. If not maven project download protobuf-java-2.4.1.jar and also download protobuf-socket-rpc-2.0.jar.
If maven project add dependency and download protobuf-socket-rpc-2.0.jar in local machine in directory ${project.basedir}/thirdparty/protobuf-socket-rpc-2.0.jar.
[code language="text"]
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>com.googlecode.protobuf.socketrpc</groupId>
<artifactId>protobuf-socket-rpc</artifactId>
<version>2.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/thirdparty/protobuf-socket-rpc-2.0.jar</systemPath>
</dependency>
</dependencies>
[/code]
Note: protoc-java.jar version should be same as protoc –version. Otherwise error will popup java.lang.UnsupportedOperationException: This is supposed to be overridden by subclasses.

Now, we will be creating a simple greeting service.

Creating the request/response object using .proto.
We will be creating the objects for remote communication. We have named the file as 'testData.proto'.

[code language="text"]
package protobufDemo; //this is protobuf namespace, not java's
option java_package = "com.xxx.protobufferrpc.protobufferrpcData";
option java_outer_classname = "GreetingProtos";
message HelloRequest
{
required string name = 1;
}
message HelloReply
{
required string message = 1;
}
[/code]

After it generate the automatic classes using below command:

[code language="text"]
$ protoc --java_out=. name_of_protoc.proto
[/code]

Creating the Service class using .proto
We will be creating the service class.
[code language="text"]
package protobufDemo;
import "testData.proto";
option java_package = "com.xxx.protobufferrpc.protobufferrpcData";
option java_outer_classname = "MyGreetingService";
option java_generic_services = true; //if you don't do this, protoc wont generate the stubs you need for rpc

service Greeting //In generated class, this class is abstract class that extends service method need to extends this
{
rpc sayHello(HelloRequest) returns (HelloReply);
}
[/code]
After it generate the automatic classes using below command:
[code language="text"]
$ protoc --java_out=. name_of_protoc.proto
[/code]

Implementing the Service method
Now, we will be implementing rpc sayHello method defined above.
[code language="text"]
package com.xxx.protobufferrpc;

import com.google.protobuf.RpcCallback;
import com.google.protobuf.RpcController;
import com.xxx.protobufferrpc.protobufferrpcData.GreetingProtos;
import com.xxx.protobufferrpc.protobufferrpcData.MyGreetingService.Greeting;

public class MyGreetingServiceImpl extends Greeting {

@Override
public void sayHello(RpcController controller, GreetingProtos.HelloRequest request, RpcCallback<GreetingProtos.HelloReply> done) {
GreetingProtos.HelloReply.Builder build= GreetingProtos.HelloReply.newBuilder();
if(request.getName().equalsIgnoreCase("namenode")){
build.setMessage("This is message for namenode only");

}else{
build.setMessage("Please see person sending message");
}
done.run(build.build());
}
}
[/code]

Write the Server application
The server application is listening on port 4446.

[code language="text"]
package com.xxx.protobufferrpc;

import java.util.concurrent.Executors;
import com.googlecode.protobuf.socketrpc.RpcServer;
import com.googlecode.protobuf.socketrpc.ServerRpcConnectionFactory;
import com.googlecode.protobuf.socketrpc.SocketRpcConnectionFactories;

public class ServerCode {
/**
* @param args the command line arguments
*/
public static void main(String[] args)
{
ServerRpcConnectionFactory rpcConnectionFactory = SocketRpcConnectionFactories.createServerRpcConnectionFactory(4446);
RpcServer server = new RpcServer(rpcConnectionFactory, Executors.newFixedThreadPool(5), true);
server.registerService(new MyGreetingServiceImpl());
server.run();
}

}
[/code]

Write Client application
In the client code, we will be passing the IP Address of server machine and port (4446) for calling remote procedure "sayHello".

[code language="text"]
package com.xxx.protobufferrpc;

import com.google.protobuf.RpcCallback;
import com.google.protobuf.RpcChannel;
import com.google.protobuf.RpcController;
import com.googlecode.protobuf.socketrpc.RpcChannels;
import com.googlecode.protobuf.socketrpc.RpcConnectionFactory;
import com.googlecode.protobuf.socketrpc.SocketRpcConnectionFactories;
import com.googlecode.protobuf.socketrpc.SocketRpcController;
import com.xxx.protobufferrpc.protobufferrpcData.GreetingProtos;
import com.xxx.protobufferrpc.protobufferrpcData.MyGreetingService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ProtoClient {
public static void main(String[] args)
{
// Create a thread pool
ExecutorService threadPool = Executors.newFixedThreadPool(1);
// Create channel
String host =args[0]; //IP Address of machine "192.16.42.22";
int port = 4446;
RpcConnectionFactory connectionFactory = SocketRpcConnectionFactories.createRpcConnectionFactory(host, port);
RpcChannel channel = RpcChannels.newRpcChannel(connectionFactory, threadPool);

// Call service
MyGreetingService.Greeting.Stub myService = MyGreetingService.Greeting.newStub(channel);
RpcController controller = new SocketRpcController();

GreetingProtos.HelloRequest.Builder cr = GreetingProtos.HelloRequest.newBuilder();
cr.setName("Hello");
myService.sayHello(controller, cr.build(),
new RpcCallback<GreetingProtos.HelloReply>()
{
public void run(GreetingProtos.HelloReply myResponse)
{
System.out.println("Received Response: " + myResponse);
}
});
// Check success
if (controller.failed())
{
System.err.println(String.format("Rpc failed %s ", controller.errorText()));
}
}

}
[/code]

Now, run the server and client application.

Hope you follow the discussion.

References:
Protocol Socket RPC
Google Protocol Buffer

2 comments:

  1. Nice, but the file "testData.proto" is missing.

    ReplyDelete
  2. Yes, missed the description. The first request/response object created should be named as 'testData.proto'

    ReplyDelete