【GoLang 那點事】gRPC-Go 和 Java 的一次 HelloWorld(十)

a_wei發表於2019-11-20

都說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檔案

syntax = "proto3";
package proto;
//介面請求入參
message HelloRequest{
    string request = 1;
}
//介面返回出參
message HelloResponse{
    string response = 1;
}
//定義介面
service HelloService{
    //一個簡單的rpc
    rpc HelloWorld(HelloRequest) returns (HelloResponse){}
    //一個伺服器端流式rpc
    rpc HelloWorldServerStream(HelloRequest) returns (stream HelloResponse){}
    //一個客戶端流式rpc
    rpc HelloWorldClientStream(stream HelloRequest) returns (HelloResponse){}
    //一個客戶端和伺服器端雙向流式rpc
    rpc HelloWorldClientAndServerStream(stream HelloRequest) 
          returns (stream  HelloResponse){}
}

下載依賴的moudle,如下地址

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

執行如下命令,生成proto.pb.go檔案

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

服務端實現介面,提供服務

type HelloServiceServer struct {
}
func (*HelloServiceServer) HelloWorld(ctx context.Context, 
        req *pb.HelloRequest) (*pb.HelloResponse, error) {
   //列印入參
   log.Printf("%v", req.Request)
   //響應服務
   return &pb.HelloResponse{Response: "hello my is gRpcServer"}, nil
}

編寫服務端程式碼,並啟動服務端

func StartServer() {
   lis, err := net.Listen("tcp", "127.0.0.1:8090")
   if err != nil {
      log.Fatalf("failed to listen: %v", err)
   }
   //建立一個grpc伺服器物件
   gRpcServer := grpc.NewServer()

   pb.RegisterHelloServiceServer(gRpcServer, &impl.HelloServiceServer{})
   //開啟服務端
   gRpcServer.Serve(lis)
}

到次為止,Go服務端啟動完成,接下來我們準備Java客戶端程式碼

Java實現客戶端

準備好Java版的proto檔案,和Go版的區別是多了一些Java的選項,其餘的不能改變

syntax = "proto3";
package proto; //包名和go中的必須一致
option java_generic_services = true;
option java_multiple_files = true;
option java_package = "com.spw.proto";
option java_outer_classname = "HelloWorldProto";
//介面請求入參
message HelloRequest{
    string request = 1;
}
//介面返回出參
message HelloResponse{
    string response = 1;
}
//定義介面
service HelloService{
    //一個簡單的rpc
    rpc HelloWorld(HelloRequest) returns (HelloResponse){}
    //一個伺服器端流式rpc
    rpc HelloWorldServerStream(HelloRequest) returns (stream HelloResponse){}
    //一個客戶端流式rpc
    rpc HelloWorldClientStream(stream HelloRequest) returns (HelloResponse){}
    //一個客戶端和伺服器端雙向流式rpc
    rpc HelloWorldClientAndServerStream(stream HelloRequest) returns (stream HelloResponse){}
}

新建一個maven專案,配置pom檔案,依賴的jar包如下

<dependencies>
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-protobuf</artifactId>
        <version>1.23.1</version>
    </dependency>
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-stub</artifactId>
        <version>1.23.1</version>
    </dependency>
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-netty-shaded</artifactId>
        <version>1.23.1</version>
    </dependency>
</dependencies>

編譯配置如下

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <plugin>
            <groupId>org.xolstice.maven.plugins</groupId>
            <artifactId>protobuf-maven-plugin</artifactId>
            <version>0.5.1</version>
            <configuration>
                <!--proto編譯器  os.detected.classifier,獲取作業系統,這個屬性是由
                ${os.detected.name}-${os.detected.arch}一起得來的-->
                <protocArtifact>
                com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}
                </protocArtifact>
                <pluginId>grpc-java</pluginId>
                <!--grpc-java程式碼生成工具-->
                <pluginArtifact>
                io.grpc:protoc-gen-grpc-java:1.23.1:exe:${os.detected.classifier} 
                </pluginArtifact>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <goal>compile-custom</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
    <!--用於根據.proto 檔案生成 protobuf 檔案-->
    <extensions>
        <extension>
            <groupId>kr.motd.maven</groupId>
            <artifactId>os-maven-plugin</artifactId>
            <version>1.5.0.Final</version>
        </extension>
    </extensions>
</build>

開始根據proto檔案編譯生成java檔案,如下圖所示,依次點選紅色的外掛

【GoLang 那點事】gRPC-Go和Java的一次HelloWorld(十)

編寫客戶端檔案,連線Go的服務端,發起請求

//通過netty建立通道
ManagedChannel channel = NettyChannelBuilder.forAddress("127.0.0.1", 8090)
        .negotiationType(NegotiationType.PLAINTEXT)
        .build();
//獲取客戶端存根物件
HelloServiceGrpc.HelloServiceBlockingStub blockingStub = HelloServiceGrpc.newBlockingStub(channel);
//建立入參
HelloRequest helloRequest = HelloRequest.newBuilder().setRequestBytes(ByteString.copyFromUtf8("hello grpc")).build();
//呼叫服務端
HelloResponse helloResponse = blockingStub.helloWorld(helloRequest);
//列印響應
System.out.println(helloResponse.getResponse());

我們以Java作為服務端,Go作為客戶端

Java實現服務端

上面Java客戶端生成的檔案不要懂,另寫一個服務的實現類,程式碼如下

public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase {
    /**
     * <pre>
     *一個簡單的rpc
     * </pre>
     */
    @Override
    public void helloWorld(HelloRequest request,
               io.grpc.stub.StreamObserver<HelloResponse>responseObserver) {

        responseObserver.onNext(HelloResponse.newBuilder()
                               .setResponse("hello my is java server").build());
        responseObserver.onCompleted();
    }
}

寫一個main方法啟動服務端,程式碼如下

public static void main(String[] args) {
    try {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        //啟動服務端
        Server server = ServerBuilder.forPort(8090)
                    //新增自己的服務實現類
                    .addService(new HelloServiceImpl()).build().start();

        countDownLatch.await();
    } catch (IOException e) {
    } catch (InterruptedException e) {
    }
}
Go作為客戶端

上面go服務端生成的proto檔案依然不要變,實現自己的客戶端程式碼,如下

func StartClient() {
   conn, err := grpc.Dial("127.0.0.1:8090", grpc.WithInsecure())
   if err != nil {
      fmt.Println(err)
      return
   }
   //建立客戶端存根物件
   c := pb.NewHelloServiceClient(conn)
   //呼叫服務
   res, err := c.HelloWorld(context.Background(), 
                            new(pb.HelloRequest), grpc.EmptyCallOption{})
   fmt.Println(res, err)
   defer conn.Close()
}

總結

通過一個HelloWorld的案例帶領大家實踐多語言通過grpc通訊,真實場景中,往往非常複雜,還需要大家多多研究,比如,負載均衡,限流,服務降級,protobuffer檔案管理,版本升級等各種問題都需要考慮,grpc的專欄也就到這裡了,希望大家通過這十篇文章能有有所收穫,更加深入的需要大家在實踐中自己摸索,思考,總結。想深入交流的可以留言獲取我的郵箱地址。

歡迎大家關注微信公眾號:“golang那點事”,更多精彩期待你的到來

【GoLang 那點事】gRPC-Go和Java的一次HelloWorld(十)

那小子阿偉

相關文章