杰瑞科技汇

protobuf rpc java 如何高效实现与优化?

Of course! Here’s a comprehensive guide to implementing RPC (Remote Procedure Call) in Java using Protocol Buffers (Protobuf), covering everything from the basics to a complete, runnable example.

protobuf rpc java 如何高效实现与优化?-图1
(图片来源网络,侵删)

We will use gRPC, which is the modern, high-performance RPC framework developed by Google that uses Protobuf as its Interface Definition Language (IDL).


Why Use gRPC with Protobuf?

  • Performance: Protobuf serializes data into a compact binary format, which is much smaller and faster to process than text-based formats like JSON or XML.
  • Strongly-Typed API: The .proto file acts as a strict contract between the client and server. Both sides are generated from the same file, eliminating mismatched API issues.
  • Code Generation: Protobuf compilers generate type-safe client and server code in multiple languages (Java, Python, Go, C++, etc.), saving you from writing and maintaining boilerplate network code.
  • Multi-lingual: The same service definition can be used to generate clients and servers in different languages.
  • Rich Ecosystem: gRPC features pluggable support for load balancing, tracing, authentication, and more.

The Core Components of a gRPC Application

  1. Service Definition: A .proto file that defines the service, its methods, and the request/response message types.
  2. Generated Code: Java classes created from the .proto file. This includes:
    • Base classes for your service (e.g., GreeterGrpc.GreeterImplBase).
    • Model classes for all your messages (e.g., HelloRequest, HelloReply).
  3. Server Implementation: A Java class that extends the generated service base class and implements the business logic for each RPC method.
  4. Client Implementation: A Java client that uses the generated stub to make calls to the server.

Step-by-Step Example: A Simple Greeter Service

Let's build a simple "Greeter" service that takes a HelloRequest and returns a HelloReply.

Prerequisites

  • Java 8 or newer
  • Maven (or Gradle, but we'll use Maven here)
  • Protocol Buffers Compiler (protoc) - Download instructions

Step 1: Set Up Your Maven Project

Create a new Maven project with the following pom.xml. This file includes the necessary dependencies for grpc, protobuf, and a Maven plugin to automatically compile .proto files.

<!-- pom.xml -->
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>grpc-java-example</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <grpc.version>1.58.0</grpc.version>
        <protobuf.version>3.24.4</protobuf.version>
    </properties>
    <dependencies>
        <!-- gRPC dependencies -->
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty-shaded</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        <!-- Needed for Java 9+ -->
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>annotations-api</artifactId>
            <version>6.0.53</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.7.0</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.6.1</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Step 2: Define the Service in a .proto File

Create the source directory src/main/proto and add a file named greeter.proto.

protobuf rpc java 如何高效实现与优化?-图2
(图片来源网络,侵删)
// src/main/proto/greeter.proto
syntax = "proto3";
// The package name should match your Maven groupId
package com.example;
// Define the service
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}
// The response message containing the greetings
message HelloReply {
  string message = 1;
}

Step 3: Generate the Java Code

Now, run the Maven compile command. The protobuf-maven-plugin will automatically execute protoc to generate the Java files.

mvn clean compile

You will find the generated Java files in target/generated-sources/protobuf/java/ and target/generated-sources/protobuf/grpc/. Your IDE (like IntelliJ or Eclipse) should automatically pick up these source directories.

  • Messages: com.example.HelloRequest.java, com.example.HelloReply.java
  • Service Base: com.example.GreeterGrpc.java (contains the base class GreeterImplBase and the client stub GreeterGrpc.GreeterBlockingStub)

Step 4: Implement the Server

Create a new Java class for your server implementation. This class will extend the generated GreeterImplBase and override the sayHello method.

// src/main/java/com/example/GrpcServer.java
package com.example;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
public class GrpcServer {
    private Server server;
    private void start() throws IOException {
        /* The port on which the server should run */
        int port = 50051;
        server = ServerBuilder.forPort(port)
                .addService(new GreeterServiceImpl())
                .build()
                .start();
        System.out.println("Server started, listening on " + port);
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            // Use stderr here since the logger may have been reset by its JVM shutdown hook.
            System.err.println("*** shutting down gRPC server since JVM is shutting down");
            GrpcServer.this.stop();
            System.err.println("*** server shut down");
        }));
    }
    private void stop() {
        if (server != null) {
            server.shutdown();
        }
    }
    /**
     * Await termination on the main thread since the grpc library uses daemon threads.
     */
    private void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
            server.awaitTermination();
        }
    }
    /**
     * Main launches the server from the command line.
     */
    public static void main(String[] args) throws IOException, InterruptedException {
        final GrpcServer server = new GrpcServer();
        server.start();
        server.blockUntilShutdown();
    }
    // Implement the service
    static class GreeterServiceImpl extends GreeterGrpc.GreeterImplBase {
        @Override
        public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
            System.println("Received request from: " + request.getName());
            String message = "Hello " + request.getName();
            HelloReply reply = HelloReply.newBuilder().setMessage(message).build();
            responseObserver.onNext(reply);
            responseObserver.onCompleted();
        }
    }
}

Step 5: Implement the Client

Create a client that connects to the server and calls the SayHello method.

protobuf rpc java 如何高效实现与优化?-图3
(图片来源网络,侵删)
// src/main/java/com/example/GrpcClient.java
package com.example;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
public class GrpcClient {
    public static void main(String[] args) {
        String target = "localhost:50051";
        // Create a channel to the server
        ManagedChannel channel = ManagedChannelBuilder.forTarget(target)
                // Channels are secure by default (via SSL/TLS). Disable this for local dev.
                .usePlaintext()
                .build();
        try {
            // Create a blocking (synchronous) stub
            GreeterGrpc.GreeterBlockingStub blockingStub = GreeterGrpc.newBlockingStub(channel);
            // Prepare the request
            HelloRequest request = HelloRequest.newBuilder().setName("gRPC Java Client").build();
            // Call the RPC method and get the response
            HelloReply response = blockingStub.sayHello(request);
            // Print the response
            System.out.println("Response from server: " + response.getMessage());
        } finally {
            // ManagedChannel should be shutdown on application exit
            channel.shutdownNow();
        }
    }
}

Step 6: Run the Application

  1. Run the Server: Execute the main method in GrpcServer.java. You should see the output:

    Server started, listening on 50051
  2. Run the Client: Execute the main method in GrpcClient.java. You should see the output:

    Response from server: Hello gRPC Java Client
  3. Check the Server Console: You should also see this new output in the server's console:

    Received request from: gRPC Java Client

Congratulations! You have successfully implemented a gRPC client-server application in Java using Protocol Buffers.


Advanced gRPC Concepts

The example above uses unary RPCs (one request, one response). gRPC supports more complex interaction patterns defined in your .proto file:

Server Streaming RPC

The server sends back a stream of messages after receiving a client's single request.

In greeter.proto:

rpc LotsOfReplies (HelloRequest) returns (stream HelloReply) {}

Server Implementation:

@Override
public void lotsOfReplies(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
    String name = request.getName();
    for (int i = 0; i < 5; i++) {
        String message = "Hello " + name + " - " + i;
        HelloReply reply = HelloReply.newBuilder().setMessage(message).build();
        responseObserver.onNext(reply);
    }
    responseObserver.onCompleted();
}

Client Streaming RPC

The client sends a stream of messages to the server, and the server responds with a single message.

In greeter.proto:

rpc LotsOfGreetings (stream HelloRequest) returns (HelloReply) {}

Server Implementation:

@Override
public StreamObserver<HelloRequest> lotsOfGreetings(StreamObserver<HelloReply> responseObserver) {
    return new StreamObserver<HelloRequest>() {
        StringBuilder namesBuilder = new StringBuilder();
        @Override
        public void onNext(HelloRequest value) {
            namesBuilder.append(value.getName()).append(", ");
            System.out.println("Got name: " + value.getName());
        }
        @Override
        public void onError(Throwable t) {
            t.printStackTrace();
        }
        @Override
        public void onCompleted() {
            String message = "Hello " + namesBuilder.substring(0, namesBuilder.length() - 2);
            HelloReply reply = HelloReply.newBuilder().setMessage(message).build();
            responseObserver.onNext(reply);
            responseObserver.onCompleted();
        }
    };
}

Bidirectional Streaming RPC

Both the client and server can send messages to each other independently. The order of messages is preserved within each stream.

In greeter.proto:

rpc BidiHello (stream HelloRequest) returns (stream HelloReply) {}

Server Implementation:

@Override
public StreamObserver<HelloRequest> bidiHello(StreamObserver<HelloReply> responseObserver) {
    return new StreamObserver<HelloRequest>() {
        @Override
        public void onNext(HelloRequest value) {
            String message = "Hello " + value.getName();
            responseObserver.onNext(HelloReply.newBuilder().setMessage(message).build());
        }
        @Override
        public void onError(Throwable t) {
            t.printStackTrace();
        }
        @Override
        public void onCompleted() {
            responseObserver.onCompleted();
        }
    };
}
分享:
扫描分享到社交APP
上一篇
下一篇