Backend Development 16 min read

Understanding RPC and gRPC: Concepts, Frameworks, and a Practical Java Implementation

This article explains the fundamentals of Remote Procedure Call (RPC), compares popular RPC frameworks such as gRPC, Thrift, Dubbo and Spring Cloud, details gRPC’s architecture, Protocol Buffers serialization, HTTP/2 advantages, and provides a step‑by‑step Java demo with full source code and performance benchmarks.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Understanding RPC and gRPC: Concepts, Frameworks, and a Practical Java Implementation

Remote Procedure Call (RPC) is a protocol that makes remote service invocation appear as a local method call by abstracting transport, serialization, and communication details. It enables developers to build independent services that can be called transparently across network boundaries.

Common RPC frameworks include gRPC (Google’s open‑source, language‑neutral system), Thrift (cross‑language framework), Dubbo (distributed service framework), and Spring Cloud (microservice tooling). These frameworks simplify service discovery, load balancing, and fault tolerance.

The typical RPC call flow involves a client stub packaging the request, sending it to the server, the server stub decoding and invoking the local service, then returning the result through the same stub layers, making the entire process invisible to the developer.

gRPC builds on this concept with high performance, using HTTP/2 for multiplexed streams, header compression, and server push, and Protocol Buffers (ProtoBuf) for compact binary serialization. It supports many languages (C++, Java, Go, Python, etc.) and generates client/server code from .proto IDL files.

Key gRPC features:

Cross‑language support via generated stubs.

IDL‑driven service definition using proto3.

HTTP/2 transport offering multiplexing, flow control, and binary framing.

Efficient serialization with ProtoBuf (binary data, 1/3‑1/10 the size of JSON/XML).

Simple installation and high scalability (millions of RPCs per second).

Protocol Buffers provide a language‑agnostic IDL, a compiler that generates data classes, and fast binary serialization, which dramatically reduces payload size and improves parsing speed compared to JSON or XML.

gRPC’s design on HTTP/2 brings additional benefits such as reduced bandwidth, lower CPU usage, and fewer TCP connections, making it suitable for mobile and high‑throughput server environments.

Performance tests show ProtoBuf can be up to five times faster than JSON, with significantly lower request latency and smaller payloads.

Practical gRPC Demo (Java)

Project structure includes a Maven pom.xml with dependencies for grpc-netty-shaded , grpc-protobuf , and grpc-stub , plus the protobuf-maven-plugin to compile .proto files.

Example helloworld.proto :

syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
package helloworld;
service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest { string name = 1; }
message HelloReply { string message = 1; }

Client implementation ( HelloWorldClient.java ) creates a ManagedChannel , a blocking stub, and calls sayHello with a HelloRequest , handling responses and shutdown.

public class HelloWorldClient {
  private final ManagedChannel channel;
  private final GreeterGrpc.GreeterBlockingStub blockingStub;
  public HelloWorldClient(String host, int port) {
    channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext(true).build();
    blockingStub = GreeterGrpc.newBlockingStub(channel);
  }
  public void greet(String name) {
    HelloRequest request = HelloRequest.newBuilder().setName(name).build();
    HelloReply response = blockingStub.sayHello(request);
    System.out.println("Message from gRPC-Server: " + response.getMessage());
  }
  public static void main(String[] args) throws InterruptedException {
    HelloWorldClient client = new HelloWorldClient("127.0.0.1", 50051);
    client.greet(args.length > 0 ? args[0] : "world");
    client.channel.shutdownNow();
  }
}

Server implementation ( HelloWorldServer.java ) starts a gRPC server on port 50051, registers a GreeterImpl that overrides sayHello to return a greeting message.

public class HelloWorldServer {
  private Server server;
  private void start() throws IOException {
    server = ServerBuilder.forPort(50051).addService(new GreeterImpl()).build().start();
    Runtime.getRuntime().addShutdownHook(new Thread(() -> { server.shutdown(); }));
  }
  private class GreeterImpl extends GreeterGrpc.GreeterImplBase {
    @Override
    public void sayHello(HelloRequest req, StreamObserver
responseObserver) {
      HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
      responseObserver.onNext(reply);
      responseObserver.onCompleted();
    }
  }
  public static void main(String[] args) throws IOException, InterruptedException {
    HelloWorldServer server = new HelloWorldServer();
    server.start();
    server.server.awaitTermination();
  }
}

Running the server prints a start‑up log, and the client prints the greeting received from the server. The source code is available on GitHub.

The article concludes with a reminder that the content is a refreshed version of a previous post, invites readers to follow the author’s public account for PDF collections, and encourages likes, shares, and follows.

JavamicroservicesBackend DevelopmentRPCgRPCProtocol Buffers
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.