GRPC协议

99 阅读4分钟

基本概念

GRPC协议是基于protocol buffer作为接口定义以及数据交互的。一个服务端可以支持不同语言的客户端进行访问。

要用protocol buffers序列化数据,首先要定义结构话的proto file,然后可以使用protocol buffers 编译器生成各种语言的类进行编码使用。

// The greeter service definition.
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;
}

交互方式定义

一元交互

常规的一问一答,然后结束交互。由服务端完成回复后,进行Completed操作。工作中目前使用该方式,感觉与http请求无异。

rpc SayHello(HelloRequest) returns (HelloResponse);

服务端流式

服务端是话唠,不断的回复数据。由服务端完成回复后,进行Completed操作。有点类似于http chunked的方式。

rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);

客户端流式

客户端是话唠,不断的发数据。在客户端完成所有信息上报后,完成Completed操作。

rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);

双向流式交互

双方各聊各的,可以不断的聊各种话题。这个也有用到,与websocket的交互方式比较类似。

rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);

测试Demo

服务端

package com.demo.grpc.client;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.examples.helloworld.GreeterGrpc;
import io.grpc.examples.helloworld.HelloReply;
import io.grpc.examples.helloworld.HelloRequest;
import io.grpc.stub.StreamObserver;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * An example gRPC client that uses ALTS. Shows how to do a Unary RPC. This example can only be run
 * on Google Cloud Platform.
 */
public final class HelloWorldAltsClient {
    private static final Logger logger = Logger.getLogger(HelloWorldAltsClient.class.getName());
    private String serverAddress = "localhost:10001";

    public static void main(String[] args) throws InterruptedException {
        new HelloWorldAltsClient().runBidiHello();
    }

    private void runSayHello() throws InterruptedException {

        ExecutorService executor = Executors.newFixedThreadPool(1);
        ManagedChannel channel = ManagedChannelBuilder.forTarget(serverAddress).usePlaintext() //使用纯文本类型
        .executor(executor).build();
        try {
            GreeterGrpc.GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(channel);
            HelloReply resp = stub.sayHello(HelloRequest.newBuilder().setName("Waldo").build());
            logger.log(Level.INFO, "Got {0}", resp);
        } finally {
            channel.shutdown();
            channel.awaitTermination(1, TimeUnit.SECONDS);
            // Wait until the channel has terminated, since tasks can be queued after the channel is
            // shutdown.
            executor.shutdown();
        }
    }

    private void runLotsOfGreetings() throws InterruptedException {

        ExecutorService executor = Executors.newFixedThreadPool(1);
        ManagedChannel channel = ManagedChannelBuilder.forTarget(serverAddress).usePlaintext() //使用纯文本类型
        .executor(executor).build();
        try {
            StreamObserver<HelloRequest> helloRequestStreamObserver = GreeterGrpc.newStub(channel).lotsOfGreetings(new StreamObserver<HelloReply>() {
                @Override
                public void onNext(HelloReply resp) {
                    logger.log(Level.INFO, "Got {0}", resp);
                }

                @Override
                public void onError(Throwable t) {
                    logger.log(Level.INFO, "onError");
                }

                @Override
                public void onCompleted() {
                    logger.log(Level.INFO, "onCompleted");
                }
            });
            helloRequestStreamObserver.onNext(HelloRequest.newBuilder().setName("hello, this is first.").build());
            helloRequestStreamObserver.onNext(HelloRequest.newBuilder().setName("hello, this is second.").build());

            helloRequestStreamObserver.onCompleted();

        } finally {
            channel.shutdown();
            channel.awaitTermination(1, TimeUnit.SECONDS);
            // Wait until the channel has terminated, since tasks can be queued after the channel is
            // shutdown.
            executor.shutdown();
        }
        }

            private void runBidiHello() throws InterruptedException {

            ExecutorService executor = Executors.newFixedThreadPool(1);
            ManagedChannel channel = ManagedChannelBuilder.forTarget(serverAddress).usePlaintext() //使用纯文本类型
            .executor(executor).build();
            try {
            StreamObserver<HelloRequest> helloRequestStreamObserver = GreeterGrpc.newStub(channel).bidiHello(new StreamObserver<HelloReply>() {
            @Override
            public void onNext(HelloReply resp) {
            logger.log(Level.INFO, "bidiHello Got {0}", resp);
        }

            @Override
            public void onError(Throwable t) {
            logger.log(Level.INFO, "onError");
        }

            @Override
            public void onCompleted() {
            logger.log(Level.INFO, "onCompleted");
        }
        });
            helloRequestStreamObserver.onNext(HelloRequest.newBuilder().setName("hello, this is first.").build());
            helloRequestStreamObserver.onNext(HelloRequest.newBuilder().setName("hello, this is second.").build());

            helloRequestStreamObserver.onCompleted();

        } finally {
            channel.shutdown();
            channel.awaitTermination(1, TimeUnit.SECONDS);
            // Wait until the channel has terminated, since tasks can be queued after the channel is
            // shutdown.
            executor.shutdown();
        }
        }
        }

客户端

package com.demo.grpc.server;

import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.examples.helloworld.GreeterGrpc.GreeterImplBase;
import io.grpc.examples.helloworld.HelloReply;
import io.grpc.examples.helloworld.HelloRequest;
import io.grpc.stub.StreamObserver;

import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * An example gRPC server that uses ALTS. Shows how to do a Unary RPC. This example can only be run
 * on Google Cloud Platform.
 */
public final class HelloWorldAltsServer extends GreeterImplBase {
    private static final Logger logger = Logger.getLogger(HelloWorldAltsServer.class.getName());
    private Server server;
    private int port = 10001;

    public static void main(String[] args) throws IOException, InterruptedException {
        new HelloWorldAltsServer().start(args);
    }

    private void start(String[] args) throws IOException, InterruptedException {
        server = ServerBuilder.forPort(port)
                        .addService(this)
                        .executor(Executors.newFixedThreadPool(1))
                        .build().start();
        logger.log(Level.INFO, "Started on {0}", port);
        server.awaitTermination();
    }

    @Override
    public void sayHello(HelloRequest request, StreamObserver<HelloReply> observer) {
        observer.onNext(HelloReply.newBuilder().setMessage("Hello, " + request.getName()).build());
        observer.onCompleted();
    }

    @Override
    public void lotsOfReplies(HelloRequest request, StreamObserver<HelloReply> observer) {
        observer.onNext(HelloReply.newBuilder().setMessage("Hello1, " + request.getName()).build());
        observer.onNext(HelloReply.newBuilder().setMessage("Hello2, " + request.getName()).build());
        observer.onCompleted();
    }

    @Override
    public StreamObserver<HelloRequest> lotsOfGreetings(StreamObserver<HelloReply> observer) {

        return new io.grpc.stub.StreamObserver<HelloRequest>() {

            @Override
            public void onNext(HelloRequest value) {
                logger.log(Level.INFO, "lotsOfGreetings onNext {0}", value);
            }

            @Override
            public void onError(Throwable t) {
                logger.info("lotsOfGreetings onError");
            }

            /**
             * 需要注意的是,由client端进行completed, server端被动接收这个信息
             */
            @Override
            public void onCompleted() {
                logger.info("lotsOfGreetings onCompleted");
                observer.onNext(HelloReply.newBuilder().setMessage("lotsOfGreetings completed").build());
            }
        };
    }

    @Override
    public StreamObserver<HelloRequest> bidiHello(StreamObserver<HelloReply> observer) {
        return new io.grpc.stub.StreamObserver<HelloRequest>() {

            @Override
            public void onNext(HelloRequest value) {
                logger.log(Level.INFO, "bidiHello onNext {0}", value);
                observer.onNext(HelloReply.newBuilder().setMessage("bidiHello onNext1").build());
                observer.onNext(HelloReply.newBuilder().setMessage("bidiHello onNext2").build());
            }

            @Override
            public void onError(Throwable t) {
                logger.info("bidiHello onError");
                t.printStackTrace();
            }

            /**
             * 需要注意的是,由client端进行completed, server端被动接收这个信息
             */
            @Override
            public void onCompleted() {
                logger.info("bidiHello onCompleted");
                observer.onNext(HelloReply.newBuilder().setMessage("bidiHello completed1").build());
                observer.onNext(HelloReply.newBuilder().setMessage("bidiHello completed2").build());
//                observer.onCompleted();
            }
        };
    }
}

proto文件

// Copyright 2015 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}

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

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

rpc BidiHello(stream HelloRequest) returns (stream 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;
}

踩坑

  1. 使用grpc协议时,定义proto文件,建议明确定义字段、类型,避免模糊类型,如:map、byte等,失去了结构化的意义,通过文件也无法明确看出协议内容。
  2. 多个字段时,定义的协议的字段顺序,客户端与服务端必须一致,否则会出现解析错误。