创建时间: 2019/11/16 19:37
作者: sunpengwei1992@aliyun.com

都说grpc是跨语言的一个rpc框架,当团队内部有多种流行编程语言时,那么grpc可以为他们提供通信,今天我们就通过一个Hello World来看看Java和Go是怎么通信的,一起实践吧,只有亲身实践才能更好的掌握,理解。

下文所有程序源代码地址如下

Java:https://github.com/sunpengwei1992/java_grpc
Go:https://github.com/sunpengwei1992/go_common/tree/master/grpc

我们以Go作为服务端,Java作为客户端

Go实现服务端

准备好Go版的proto文件

  1. syntax = "proto3";
  2. package proto;
  3. //接口请求入参
  4. message HelloRequest{
  5. string request = 1;
  6. }
  7. //接口返回出参
  8. message HelloResponse{
  9. string response = 1;
  10. }
  11. //定义接口
  12. service HelloService{
  13. //一个简单的rpc
  14. rpc HelloWorld(HelloRequest) returns (HelloResponse){}
  15. //一个服务器端流式rpc
  16. rpc HelloWorldServerStream(HelloRequest) returns (stream HelloResponse){}
  17. //一个客户端流式rpc
  18. rpc HelloWorldClientStream(stream HelloRequest) returns (HelloResponse){}
  19. //一个客户端和服务器端双向流式rpc
  20. rpc HelloWorldClientAndServerStream(stream HelloRequest)
  21. returns (stream HelloResponse){}
  22. }

下载依赖的moudle,如下地址

  1. github.com/golang/protobuf v1.3.2
  2. google.golang.org/grpc v1.23.1

执行如下命令,生成proto.pb.go文件

  1. protoc --goout=plugins=grpc:. helloworld.proto

服务端实现接口,提供服务

  1. type HelloServiceServer struct {
  2. }
  3. func (*HelloServiceServer) HelloWorld(ctx context.Context,
  4. req *pb.HelloRequest) (*pb.HelloResponse, error) {
  5. //打印入参
  6. log.Printf("%v", req.Request)
  7. //响应服务
  8. return &pb.HelloResponse{Response: "hello my is gRpcServer"}, nil
  9. }

编写服务端代码,并启动服务端

  1. func StartServer() {
  2. lis, err := net.Listen("tcp", "127.0.0.1:8090")
  3. if err != nil {
  4. log.Fatalf("failed to listen: %v", err)
  5. }
  6. //创建一个grpc服务器对象
  7. gRpcServer := grpc.NewServer()
  8. pb.RegisterHelloServiceServer(gRpcServer, &impl.HelloServiceServer{})
  9. //开启服务端
  10. gRpcServer.Serve(lis)
  11. }

到次为止,Go服务端启动完成,接下来我们准备Java客户端代码

Java实现客户端

准备好Java版的proto文件,和Go版的区别是多了一些Java的选项,其余的不能改变

  1. syntax = "proto3";
  2. package proto; //包名和go中的必须一致
  3. option java_generic_services = true;
  4. option java_multiple_files = true;
  5. option java_package = "com.spw.proto";
  6. option java_outer_classname = "HelloWorldProto";
  7. //接口请求入参
  8. message HelloRequest{
  9. string request = 1;
  10. }
  11. //接口返回出参
  12. message HelloResponse{
  13. string response = 1;
  14. }
  15. //定义接口
  16. service HelloService{
  17. //一个简单的rpc
  18. rpc HelloWorld(HelloRequest) returns (HelloResponse){}
  19. //一个服务器端流式rpc
  20. rpc HelloWorldServerStream(HelloRequest) returns (stream HelloResponse){}
  21. //一个客户端流式rpc
  22. rpc HelloWorldClientStream(stream HelloRequest) returns (HelloResponse){}
  23. //一个客户端和服务器端双向流式rpc
  24. rpc HelloWorldClientAndServerStream(stream HelloRequest) returns (stream HelloResponse){}
  25. }

新建一个maven项目,配置pom文件,依赖的jar包如下

  1. <dependencies>
  2. <dependency>
  3. <groupId>io.grpc</groupId>
  4. <artifactId>grpc-protobuf</artifactId>
  5. <version>1.23.1</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>io.grpc</groupId>
  9. <artifactId>grpc-stub</artifactId>
  10. <version>1.23.1</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>io.grpc</groupId>
  14. <artifactId>grpc-netty-shaded</artifactId>
  15. <version>1.23.1</version>
  16. </dependency>
  17. </dependencies>

编译配置如下

  1. <build>
  2. <plugins>
  3. <plugin>
  4. <groupId>org.springframework.boot</groupId>
  5. <artifactId>spring-boot-maven-plugin</artifactId>
  6. </plugin>
  7. <plugin>
  8. <groupId>org.xolstice.maven.plugins</groupId>
  9. <artifactId>protobuf-maven-plugin</artifactId>
  10. <version>0.5.1</version>
  11. <configuration>
  12. <!--proto编译器 os.detected.classifier,获取操作系统,这个属性是由
  13. ${os.detected.name}-${os.detected.arch}一起得来的-->
  14. <protocArtifact>
  15. com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}
  16. </protocArtifact>
  17. <pluginId>grpc-java</pluginId>
  18. <!--grpc-java代码生成工具-->
  19. <pluginArtifact>
  20. io.grpc:protoc-gen-grpc-java:1.23.1:exe:${os.detected.classifier}
  21. </pluginArtifact>
  22. </configuration>
  23. <executions>
  24. <execution>
  25. <goals>
  26. <goal>compile</goal>
  27. <goal>compile-custom</goal>
  28. </goals>
  29. </execution>
  30. </executions>
  31. </plugin>
  32. </plugins>
  33. <!--用于根据.proto 文件生成 protobuf 文件-->
  34. <extensions>
  35. <extension>
  36. <groupId>kr.motd.maven</groupId>
  37. <artifactId>os-maven-plugin</artifactId>
  38. <version>1.5.0.Final</version>
  39. </extension>
  40. </extensions>
  41. </build>

开始根据proto文件编译生成java文件,如下图所示,依次点击红色的插件
gRPC-Go和Java的一次HelloWorld(十) - 图1
编写客户端文件,连接Go的服务端,发起请求

  1. //通过netty创建通道
  2. ManagedChannel channel = NettyChannelBuilder.forAddress("127.0.0.1", 8090)
  3. .negotiationType(NegotiationType.PLAINTEXT)
  4. .build();
  5. //获取客户端存根对象
  6. HelloServiceGrpc.HelloServiceBlockingStub blockingStub = HelloServiceGrpc.newBlockingStub(channel);
  7. //创建入参
  8. HelloRequest helloRequest = HelloRequest.newBuilder().setRequestBytes(ByteString.copyFromUtf8("hello grpc")).build();
  9. //调用服务端
  10. HelloResponse helloResponse = blockingStub.helloWorld(helloRequest);
  11. //打印响应
  12. System.out.println(helloResponse.getResponse());

我们以Java作为服务端,Go作为客户端

Java实现服务端

上面Java客户端生成的文件不要懂,另写一个服务的实现类,代码如下

  1. public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase {
  2. /**
  3. * <pre>
  4. *一个简单的rpc
  5. * </pre>
  6. */
  7. @Override
  8. public void helloWorld(HelloRequest request,
  9. io.grpc.stub.StreamObserver<HelloResponse>responseObserver) {
  10. responseObserver.onNext(HelloResponse.newBuilder()
  11. .setResponse("hello my is java server").build());
  12. responseObserver.onCompleted();
  13. }
  14. }

写一个main方法启动服务端,代码如下

  1. public static void main(String[] args) {
  2. try {
  3. CountDownLatch countDownLatch = new CountDownLatch(1);
  4. //启动服务端
  5. Server server = ServerBuilder.forPort(8090)
  6. //添加自己的服务实现类
  7. .addService(new HelloServiceImpl()).build().start();
  8. countDownLatch.await();
  9. } catch (IOException e) {
  10. } catch (InterruptedException e) {
  11. }
  12. }

Go作为客户端

上面go服务端生成的proto文件依然不要变,实现自己的客户端代码,如下

  1. func StartClient() {
  2. conn, err := grpc.Dial("127.0.0.1:8090", grpc.WithInsecure())
  3. if err != nil {
  4. fmt.Println(err)
  5. return
  6. }
  7. //创建客户端存根对象
  8. c := pb.NewHelloServiceClient(conn)
  9. //调用服务
  10. res, err := c.HelloWorld(context.Background(),
  11. new(pb.HelloRequest), grpc.EmptyCallOption{})
  12. fmt.Println(res, err)
  13. defer conn.Close()
  14. }

总结

通过一个HelloWorld的案例带领大家实践多语言通过grpc通信,真实场景中,往往非常复杂,还需要大家多多研究,比如,负载均衡,限流,服务降级,protobuffer文件管理,版本升级等各种问题都需要考虑,grpc的专栏也就到这里了,希望大家通过这十篇文章能有有所收获,更加深入的需要大家在实践中自己摸索,思考,总结。想深入交流的可以留言获取我的邮箱地址。

欢迎大家关注微信公众号:“golang那点事”,更多精彩期待你的到来
GoLang公众号.jpg