1 简介gRPC在不同语言间是如何连接的
2 安装Protoc buffer
下载链接:https://github.com/protocolbuffers/protobuf/releases
Windows下建议直接下载可执行文件:
下载完成后放到指定目录,配置环境变量:
验证:
如果出现如下异常:
解决方式:
把protoc.exe拷贝到C:\Windows\System32
3 Go使用gRPC进行Go程序之间的通信
项目结构:
下载依赖:
go get google.golang.org/protobuf/runtime/protoimpl@v1.26.0 go get google.golang.org/grpc go get google.golang.org/protobuf
go.mod文件:
module grpc_go go 1.16 require ( google.golang.org/grpc v1.43.0 // indirect google.golang.org/protobuf v1.26.0 // indirect )
protoc文件:
syntax = "proto3"; //指定语法格式 package proto; //指定生成的包名字 option java_package = "org.ymx.proto"; option go_package = "/"; // 定义gRPC服务接口 service HelloService { // 接口的具体方法 rpc SayHello(HelloRequest) returns (HelloReply) {} } // 接口的请求参数类型 message HelloRequest { string name = 1; } // 接口的响应参数类型 message HelloReply { string message = 1; }
到proto目录下进行编译,编译完成后会出现指定的go文件:
C:\Users\17122\Desktop\grpc_demo\grpc_go\protoc> protoc -I . --go_out=plugins=grpc:. hello.proto
如果出现异常:
'protoc-gen-go' 不是内部或外部命令,也不是可运行的程序 或批处理文件。 --go_out: protoc-gen-go: Plugin failed with status code 1.
解决方式:
下载这个项目,到protoc-gen-go目录下,go build -o protoc-gen-go.exe main.go ,生成protoc-gen-go.exe文件
再将protoc-gen-go.exe拷贝到C:\Windows\System32
grpc客户端,main.go
package main import ( "context" "fmt" "google.golang.org/grpc" _ "grpc_go/proto" __ "grpc_go/proto" "log" ) func main() { //1 配置grpc服务端的端口作为客户端的监听 conn, err := grpc.Dial(":6666", grpc.WithInsecure()) if err != nil { log.Fatalf("正在监听服务端 : %v\n", err) } defer conn.Close() //2 实例化 UserInfoService 服务的客户端 client := __.NewHelloServiceClient(conn) //3 调用grpc服务 req := new (__.HelloRequest) req.Name = "YMX" resp, err := client.SayHello(context.Background(), req) if err != nil { log.Fatalf("请求错误 : %v\n", err) } fmt.Printf("响应内容 : %v\n", resp) }
grpc服务端,main.go
import ( "context" "fmt" "google.golang.org/grpc" __ "grpc_go/proto" "log" "net" ) //定义服务端 实现 约定的接口 type HelloServiceServer struct{} var u = HelloServiceServer{} //实现 interface func (s *HelloServiceServer) SayHello(ctx context.Context, req *__.HelloRequest) (resp *__.HelloReply, err error) { name := req.Name if name == "YMX" { resp = &__.HelloReply{Message: "Hello YMX"} } else { resp = &__.HelloReply{Message: "Hi NoYMX"} } err = nil return resp, nil } //启动服务 func main() { //1 添加监听的端口 port := ":6666" l, err := net.Listen("tcp", port) if err != nil { log.Fatalf("端口监听错误 : %v\n", err) } fmt.Printf("正在监听: %s 端口\n", port) //2 启动grpc服务 s := grpc.NewServer() //3 将UserInfoService服务注册到gRPC中,注意第二个参数是接口类型的变量,需要取地址传参 __.RegisterHelloServiceServer(s, &u) s.Serve(l) }
启动服务端和客户端,进行测试:
4 Java使用gRPC进行Java程序之间的通信
项目结构:
pom依赖
<?xml version="1.0" encoding="UTF-8"?> <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>org.ymx</groupId> <artifactId>grpc_java</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-all</artifactId> <version>1.43.0</version> </dependency> </dependencies> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.4.1.Final</version> </extension> </extensions> <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.5.0</version> <configuration> <protocArtifact>com.google.protobuf:protoc:3.0.0:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.0.0:exe:${os.detected.classifier}</pluginArtifact> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
protoc文件内容:
syntax = "proto3"; //指定语法格式 package proto; //指定生成的包名字; option java_multiple_files = true; option java_package = "org.ymx.proto"; option go_package = "/"; option java_outer_classname = "Hello"; option objc_class_prefix = "YMX"; service HelloService { rpc SayHello(HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; }
进行编译,首先利用protobuf进行编译:
然后再利用maven进行编译:
grpc服务端代码:
package org.ymx; import io.grpc.ServerBuilder; import io.grpc.stub.StreamObserver; import org.ymx.proto.HelloReply; import org.ymx.proto.HelloRequest; import org.ymx.proto.HelloServiceGrpc; import java.io.IOException; /** * @desc: grpc服务端 * @author: YanMingXin * @create: 2021/12/18-14:52 **/ public class Server { private final static int port = 5555; private io.grpc.Server server; private void start() throws IOException { server = ServerBuilder.forPort(port) .addService(new HelloServiceImpl()) .build() .start(); System.out.println("service start..."); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { System.err.println("*** shutting down gRPC server since JVM is shutting down"); Server.this.stop(); System.err.println("*** server shut down"); } }); } private void stop() { if (server != null) { server.shutdown(); } } private void blockUntilShutdown() throws InterruptedException { if (server != null) { server.awaitTermination(); } } public static void main(String[] args) throws IOException, InterruptedException { final Server server = new Server(); server.start(); server.blockUntilShutdown(); } /** * 实现 定义一个实现服务接口的类 */ private class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase { @Override public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) { System.out.println("service:" + req.getName()); HelloReply reply = HelloReply.newBuilder().setMessage(("Hello: " + req.getName())).build(); responseObserver.onNext(reply); responseObserver.onCompleted(); } } }
grpc客户端代码:
package org.ymx; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import org.ymx.proto.HelloReply; import org.ymx.proto.HelloRequest; import org.ymx.proto.HelloServiceGrpc; import java.util.concurrent.TimeUnit; /** * @desc: grpc客户端 * @author: YanMingXin * @create: 2021/12/18-14:52 **/ public class Client { private final ManagedChannel channel; private final HelloServiceGrpc.HelloServiceBlockingStub blockingStub; public Client(String host, int port) { channel = ManagedChannelBuilder.forAddress(host, port) .usePlaintext() .build(); blockingStub = HelloServiceGrpc.newBlockingStub(channel); } public void shutdown() throws InterruptedException { channel.shutdown().awaitTermination(5, TimeUnit.SECONDS); } public void hello(String name) { HelloRequest request = HelloRequest.newBuilder().setName(name).build(); HelloReply response = blockingStub.sayHello(request); System.out.println(response.getMessage()); } public static void main(String[] args) { Client client = new Client("127.0.0.1", 5555); for (int i = 0; i < 5; i++) { if (i < 3) { client.hello("ZS"); } else { client.hello("YMX"); } } } }
启动测试:
5 使用gRPC进行Go和Java程序间的通信
5.1 使用Java作为服务端,Go作为客户端
修改客户端端口:
Java服务端代码,其他代码不变:
/** * @desc: grpc服务端 * @author: YanMingXin * @create: 2021/12/18-14:52 **/ public class Server { private final static int port = 5555; private io.grpc.Server server; private void start() throws IOException { server = ServerBuilder.forPort(port) .addService(new HelloServiceImpl()) .build() .start(); System.out.println("service start..."); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { System.err.println("*** shutting down gRPC server since JVM is shutting down"); Server.this.stop(); System.err.println("*** server shut down"); } }); } private void stop() { if (server != null) { server.shutdown(); } } private void blockUntilShutdown() throws InterruptedException { if (server != null) { server.awaitTermination(); } } public static void main(String[] args) throws IOException, InterruptedException { final Server server = new Server(); server.start(); server.blockUntilShutdown(); } /** * 实现 定义一个实现服务接口的类 */ private class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase { @Override public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) { System.out.println("service:" + req.getName()); HelloReply reply = HelloReply.newBuilder().setMessage(("Hello: " + req.getName())).build(); responseObserver.onNext(reply); responseObserver.onCompleted(); } } }
Go客户端代码,其他代码不变:
import ( "context" "fmt" "google.golang.org/grpc" _ "grpc_go/proto" __ "grpc_go/proto" "log" ) func main() { //1 配置grpc服务端的端口作为客户端的监听 conn, err := grpc.Dial(":5555", grpc.WithInsecure()) if err != nil { log.Fatalf("正在监听服务端 : %v\n", err) } defer conn.Close() //2 实例化 UserInfoService 服务的客户端 client := __.NewHelloServiceClient(conn) //3 调用grpc服务 req := new (__.HelloRequest) req.Name = "YMX" resp, err := client.SayHello(context.Background(), req) if err != nil { log.Fatalf("请求错误 : %v\n", err) } fmt.Printf("响应内容 : %v\n", resp) }
测试:
5.2 使用Go作为服务端,Java作为客户端
修改客户端端口:
Go服务端代码,其他代码不变:
import ( "context" "fmt" "google.golang.org/grpc" __ "grpc_go/proto" "log" "net" ) //定义服务端 实现 约定的接口 type HelloServiceServer struct{} var u = HelloServiceServer{} //实现 interface func (s *HelloServiceServer) SayHello(ctx context.Context, req *__.HelloRequest) (resp *__.HelloReply, err error) { name := req.Name if name == "YMX" { resp = &__.HelloReply{Message: "Hello YMX"} } else { resp = &__.HelloReply{Message: "Hi NoYMX"} } err = nil return resp, nil } //启动服务 func main() { //1 添加监听的端口 port := ":6666" l, err := net.Listen("tcp", port) if err != nil { log.Fatalf("端口监听错误 : %v\n", err) } fmt.Printf("正在监听: %s 端口\n", port) //2 启动grpc服务 s := grpc.NewServer() //3 将UserInfoService服务注册到gRPC中,注意第二个参数是接口类型的变量,需要取地址传参 __.RegisterHelloServiceServer(s, &u) s.Serve(l) }
Java客户端代码,其他代码不变:
/** * @desc: grpc客户端 * @author: YanMingXin * @create: 2021/12/18-14:52 **/ public class Client { private final ManagedChannel channel; private final HelloServiceGrpc.HelloServiceBlockingStub blockingStub; public Client(String host, int port) { channel = ManagedChannelBuilder.forAddress(host, port) .usePlaintext() .build(); blockingStub = HelloServiceGrpc.newBlockingStub(channel); } public void shutdown() throws InterruptedException { channel.shutdown().awaitTermination(5, TimeUnit.SECONDS); } public void hello(String name) { HelloRequest request = HelloRequest.newBuilder().setName(name).build(); HelloReply response = blockingStub.sayHello(request); System.out.println(response.getMessage()); } public static void main(String[] args) { Client client = new Client("127.0.0.1", 6666); for (int i = 0; i < 5; i++) { if (i < 3) { client.hello("ZS"); } else { client.hello("YMX"); } } } }
测试:
6 总结
- 存放protoc的文件目录尽量用proto命名
源码获取方式:关注下方公众号,回复【0701】