Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

RPC with Protocol Buffers

4.00/5 (1 vote)
14 Mar 2019CPOL2 min read 3.9K  
RPC with protocol buffers

Just when I thought I covered the topic of RPC in C++ with Thrift, I came across a new and very promising RPC framework: Google’s gRPC. The more I read about it, the more I like it: just like Thrift, it supports many programming languages and internally uses Protocol Buffers to encode messages. So I decided to give it a try and see how easily I could create a simple client and serve programs…

Just like with Thrift, the first step is to define your RPC service. Here’s my definition of a simple service with one RPC method which accepts an input parameter and provides a return value:

C++
syntax = "proto3";

message Input
{
	string input_msg = 1;
}

message Output
{
	string output_msg = 1;
}

service gRPCService
{
	rpc hello(Input) returns (Output) {}
}

I saved it as grpc_service.proto and now was the time to generate the client and server stubs. Unlike Thrift, this is a two step process:

  1. You must generate the protocol buffer files, and
  2. generate the gRPC client/server stubs.

Both actions are done with protoc compiler and a custom plugin that comes with gRPC.

gRPC code generation steps:

protoc -I . –cpp_out=. grpc_service.proto
protoc -I . –grpc_out=. –plugin=protoc-gen-grpc=/usr/local/bin/grpc_cpp_plugin grpc_service.proto

This will compile our service definition file and produce 4 output files: .h and .cc for protocol buffer messages, and .h and .cc for gRPC client/server stubs.

Alright, that was easy. Let’s see what it takes to implement a gRPC client program… after 15 minutes reading through the documentation, I came up with the following:

C++
#include <iostream>
#include <grpc/grpc.h>
#include <grpc++/channel.h>
#include <grpc++/client_context.h>
#include <grpc++/create_channel.h>
#include <grpc++/security/credentials.h>
#include "grpc_service.grpc.pb.h"

using namespace std;
using namespace grpc;

class gRPCClient
{
public:
	gRPCClient(shared_ptr<Channel> channel)
	: stub(gRPCService::NewStub(channel)) {}

	string hello(const std::string& msg)
	{
		Input input;
		Output output;
		ClientContext context;

		input.set_input_msg(msg);

		Status status = stub->hello(&context, input, &output);
		if (status.ok())
		{
			return output.output_msg();
		}
		else
		{
			cout << status.error_code() << ": " << status.error_message() << endl;
			return "RPC failed";
		}
	}

private:
	unique_ptr<gRPCService::Stub> stub;
};

int main(int argc, char** argv)
{
	gRPCClient client(grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()));
	string reply = client.hello("gRPC client says hi!");
	cout << "Client received: " << reply << endl;

	return 1;
}

Little more code needed to construct and invoke the RPC method as compared to Thrift, but not too bad. The extra lines are mostly around creating the protocol buffer message objects and setting their properties.

The corresponding gRPC server code is pretty much just as easy to implement as with Thrift. Here’s the simple server that prints out the message it receives and sends back a reply:

C++
#include <iostream>
#include <grpc/grpc.h>
#include <grpc++/server.h>
#include <grpc++/server_builder.h>
#include <grpc++/server_context.h>
#include <grpc++/security/server_credentials.h>
#include "grpc_service.grpc.pb.h"

using namespace std;
using namespace grpc;

class gRPCServiceImpl final : public gRPCService::Service
{
public:
	Status hello(ServerContext* context, const Input* in, Output* out) override
	{
		cout << in->input_msg() << endl;
		out->set_output_msg("gRPC server says hi!");
		return Status::OK;
	}
};

int main(int argc, char** argv)
{
	gRPCServiceImpl service;
	ServerBuilder builder;
	builder.AddListeningPort("0.0.0.0:50051", grpc::InsecureServerCredentials());
	builder.RegisterService(&service);
	unique_ptr<Server> server(builder.BuildAndStart());
	cout << "Server starting..." << endl;
	server->Wait();

	return 1;
}

So there you have it folks! It is very easy to get started with this framework.

As far as which one you should prefer (Thrift vs gRPC), I can’t honestly say. I don’t know enough about them to claim one is better than the other. What I can say is that Thrift is a more mature framework that has been around for longer. You decide. 🙂

P.S. On my Mac, I was able to use Homebrew package manager to install the required headers/libraries/executables for gRPC. My Ubuntu 18.04 Linux has libgrpc available in its online repository. I also verified that Microsoft’s vcpkg has ports available for Windows.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)