目标
从头一步步创建 java 版的 gRPC 示例项目,体验 gRPC 的开发流程。
步骤说明
- 使用 protobuf 定义 grpc service、request 和 response type。
- 使用 protobuf compiler 生成 server 和 client 相关代码。
- 创建 server 应用,实现 service 接口,创建 grpc server。
- 创建 client 应用,使用生成的 stub 进行 rpc 调用。
详细步骤
(1)创建项目
创建项目文件夹,准备好初始化的文件和目录:
$ mkdir grpc-project
$ cd grpc-project
$ touch pom.xml
$ mkdir -p src/main/java
$ mkdir -p src/main/proto
结构如下:
├── pom.xml
└── src
└── main
├── java
└── proto
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.grpc.example</groupId>
<artifactId>grpcjava</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>grpc-java-demo</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<grpc.version>1.15.0</grpc.version>
<protobuf.version>3.5.1</protobuf.version>
<protoc.version>3.5.1-1</protoc.version>
<netty.tcnative.version>2.0.7.Final</netty.tcnative.version>
</properties>
<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>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-alts</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-testing</artifactId>
<version>${grpc.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<version>${netty.tcnative.version}</version>
</dependency>
<dependency>
<groupId>com.google.api.grpc</groupId>
<artifactId>proto-google-common-protos</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>${protobuf.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.5.0.Final</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.5.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
<outputDirectory>${project.build.sourceDirectory}</outputDirectory>
<clearOutputDirectory>false</clearOutputDirectory>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.4.1</version>
<executions>
<execution>
<id>enforce</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireUpperBoundDeps />
</rules>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
(2)定义 proto
$ vim src/main/proto/HelloService.proto
内容:
syntax = "proto3";
option java_multiple_files = true;
package com.example.grpc.gencode;
message HelloRequest {
string firstName = 1;
string lastName = 2;
}
message HelloResponse {
string greeting = 1;
}
service HelloService {
rpc hello(HelloRequest) returns (HelloResponse);
}
生成代码:
$ mvn clean compile
项目结构:
├── pom.xml
├── src
│ └── main
│ ├── java
│ │ └── com
│ │ └── example
│ │ └── grpc
│ │ └── gencode
│ │ ├── HelloRequest.java
│ │ ├── HelloRequestOrBuilder.java
│ │ ├── HelloResponse.java
│ │ ├── HelloResponseOrBuilder.java
│ │ ├── HelloServiceGrpc.java
│ │ └── HelloServiceOuterClass.java
│ └── proto
│ └── HelloService.proto
(3)实现 service
$ vim src/main/java/com/example/grpc/HelloServiceImpl.java
代码:
package com.example.grpc;
import com.example.grpc.gencode.HelloRequest;
import com.example.grpc.gencode.HelloResponse;
import com.example.grpc.gencode.HelloServiceGrpc.HelloServiceImplBase;
import io.grpc.stub.StreamObserver;
public class HelloServiceImpl extends HelloServiceImplBase {
@Override
public void hello(
HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
String greeting = new StringBuilder()
.append("Hello, ")
.append(request.getFirstName())
.append(" ")
.append(request.getLastName())
.toString();
HelloResponse response = HelloResponse.newBuilder()
.setGreeting(greeting)
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
(4)创建 server
$ vim src/main/java/com/example/grpc/GrpcServer.java
代码:
package com.example.grpc;
import io.grpc.Server;
import io.grpc.ServerBuilder;
public class GrpcServer {
public static void main(String[] args) {
try {
int port = 50051;
final Server server = ServerBuilder.forPort(port)
.addService(new HelloServiceImpl())
.build()
.start();
System.out.println("Server started, listening on " + port);
server.awaitTermination();
} catch (Exception e) {
e.printStackTrace();
}
}
}
(5)创建 client
$ vim src/main/java/com/example/grpc/GrpcClient.java
代码:
package com.example.grpc;
import java.util.concurrent.TimeUnit;
import com.example.grpc.gencode.HelloRequest;
import com.example.grpc.gencode.HelloResponse;
import com.example.grpc.gencode.HelloServiceGrpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
public class GrpcClient {
public static void main(String[] args) {
ManagedChannel channel = null;
try {
channel = ManagedChannelBuilder.forAddress("localhost", 50051).usePlaintext(true).build();
HelloServiceGrpc.HelloServiceBlockingStub stub = HelloServiceGrpc.newBlockingStub(channel);
HelloResponse helloResponse = stub
.hello(HelloRequest.newBuilder().setFirstName("Baeldung").setLastName("gRPC").build());
System.out.println(helloResponse.getGreeting());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (channel != null) {
try {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
现在的项目结构:
├── pom.xml
├── src
│ └── main
│ ├── java
│ │ └── com
│ │ └── example
│ │ └── grpc
│ │ ├── GrpcClient.java
│ │ ├── GrpcServer.java
│ │ ├── HelloServiceImpl.java
│ │ └── gencode
│ │ ├── HelloRequest.java
│ │ ├── HelloRequestOrBuilder.java
│ │ ├── HelloResponse.java
│ │ ├── HelloResponseOrBuilder.java
│ │ ├── HelloServiceGrpc.java
│ │ └── HelloServiceOuterClass.java
│ └── proto
│ └── HelloService.proto
编译运行
$ mvn clean compile
在一个命令终端中启动 server:
$ mvn exec:java -Dexec.mainClass="com.example.grpc.GrpcServer"
Server started, listening on 50051
server 会阻塞等待。
在另一个命令终端中启动 client:
$ mvn exec:java -Dexec.mainClass="com.example.grpc.GrpcClient"
Hello, Baeldung gRPC
输出后程序结束运行。