gRPC

本文介绍了Google的gRPC框架,它基于HTTP/2和Protocol Buffers进行数据传输。首先,文章讲解了Protocol Buffers的基础,包括如何定义消息和服务,并阐述了其与XML的区别。接着,通过一个实例展示了gRPC的使用流程,包括.proto文件的编译、服务端和客户端程序的编写及运行。最后,提到了gRPC的四种通信方式,并预告了未来将深入探讨gRPC的源码分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

gRPC是Google开源的一个RPC框架,基于HTTP2.0传输数据,数据序列化框架为Google开源的Protocol Buffers

一、先简单介绍下Protocol Buffers

同样作为数据序列化框架,Protocol Buffers比XML更小、更快、更易于编程,它将消息和服务定义在.proto文件中,语言无关,在Java中用它自己的编译器将.proto文件直接编译成Java类

1.定义消息
syntax = "proto3";

message SearchRequest {
  required string query = 1;
  optional int32 page_number = 2;
  optional int32 result_per_page = 3;
}

指定元素类型:可以指定元素类型为基本类型,也可以指定为enum或者其他message

指定元素代号:在二进制格式的消息中标识元素,是从1开始的数字

指定元素约束:默认情况下元素可以出现0次或1次;repeated:元素可以出现任意次

添加注释:与Java注释相同,//或者/* ……*/

添加reserved元素:弃用某些元素名或元素代号

message Foo {
  reserved 2, 15, 9 to 11;
  reserved "foo", "bar";
}

元素基本类型和Java类型的对应关系:double->double, float->float, int32->int, int64->long, bool->boolean, string->String, bytes->BytesString

枚举类型:

message SearchRequest {
  required string query = 1;
  optional int32 page_number = 2;
  optional int32 result_per_page = 3 [default = 10];
  enum Corpus {
    //option allow_alias = true;
    //OTHER = 6;    allow_alias为true可以指定不同枚举元素为相同的值
    UNIVERSAL = 0;
    WEB = 1;
    IMAGES = 2;
    LOCAL = 3;
    NEWS = 4;
    PRODUCTS = 5;
    VIDEO = 6;
  }
  Corpus corpus = 4;
}

注意枚举类型的元素代码从0开始,枚举元素也可以弃用某些元素名或元素代码

enum Foo {
  reserved 2, 15, 9 to 11, 40 to max;
  reserved "FOO", "BAR";
}

默认值:string->empty string, bytes->empty bytes, numeric->0, bool->false, enum->第一个枚举元素

导入其他消息定义:使用public关键字可以传递导入

// old.proto
import public "new.proto";
import "other.proto";
// client.proto
import "old.proto";
// You use definitions from old.proto and new.proto, but not other.proto

消息可以嵌套定义

message SearchResponse {
  message Result {
    string url = 1;
    string title = 2;
    repeated string snippets = 3;
  }
  repeated Result results = 1;
}
2.定义服务
service SearchService {
  rpc Search (SearchRequest) returns (SearchResponse);
}

二、从实例出发了解gRPC

gRPC的使用流程是这样的:
1.在.proto文件中定义消息体和服务,规定客户端要发送的消息和服务器的通信方式
2.编译.proto文件,生成Java类
3.编写Server和Client端程序,定义通信方法
4.运行服务器程序,监听请求
5.运行客户端程序,发送请求消息,接收返回消息

Protocol Buffers和gRPC都需要自己的编译器,先添加maven依赖

<properties>
    <grpc.version>1.13.1</grpc.version>
    <protobuf.version>3.5.1</protobuf.version>
    <protoc.version>3.5.1-1</protoc.version>
</properties>

<dependencies>
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-netty</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>com.google.protobuf</groupId>
        <artifactId>protobuf-java-util</artifactId>
        <version>${protobuf.version}</version>
    </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>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <goal>compile-custom</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

参考gRPC官网,了解到gRPC有四种通信方式
1.客户端发单个消息,服务器发单个消息
2.客户端发单个消息,服务器发消息流
3.客户端发消息流,服务器发单个消息
4.客户端发消息流,服务器发消息流
于是我们在.proto文件中定义四个方法,模拟这四种通信方式

syntax = "proto3";

option java_package = "learn_grpc";

service Service {
    rpc SayHello (SayHelloRequest) returns (SayHelloReply) {
    }
    rpc GetServerPeopleList (GetServerPeopleListRequest) returns (stream
    GetServerPeopleListReply) {
    }
    rpc CountClientPeople (stream CountClientPeopleRequest) returns (CountClientPeopleReply) {
    }
    rpc Chat (stream ChatContent) returns (stream ChatContent) {
    }
}

message SayHelloRequest {
    string name = 1;
    string helloWords = 2;
}

message SayHelloReply {
    string reply = 1;
}

message GetServerPeopleListRequest {

}

message GetServerPeopleListReply {
    string name = 1;
}

message CountClientPeopleRequest {

}

message CountClientPeopleReply {
    int32 count = 1;
}

message ChatContent {
    string content = 1;
}

编译.proto文件,生成了对应的ServiceGrpc.class文件
新建Server和Client文件

public class ServiceServer {

    private final Server server;
    private final int port;

    //初始化server,指定端口号和服务
    public ServiceServer(int port) {
        this.port = port;
        server = ServerBuilder.forPort(port).addService(new Service()).build();
    }

    public void start() throws IOException, InterruptedException {
        server.start();
        System.out.println("server started");
        blockUntilShutdown();
    }

    public void stop() {
        server.shutdown();
        System.out.println("server stopped");
    }

    //服务器启动后进入等待状态
    public void blockUntilShutdown() throws InterruptedException {
        System.out.println("server blocked");
        server.awaitTermination();
    }

    //定义服务,实现方法
    static class Service extends ServiceGrpc.ServiceImplBase {

        //单个消息-单个消息
        @Override
        public void sayHello(LearnGrpc.SayHelloRequest request,
                StreamObserver<LearnGrpc.SayHelloReply> responseObserver) {
            String name = request.getName();
            //onNext方法里填写要返回的消息
            responseObserver.onNext(LearnGrpc.SayHelloReply.newBuilder()
                    .setReply("Welcome, " + name + "!").build());
            responseObserver.onCompleted();
        }

        //单个消息-消息流
        @Override
        public void getServerPeopleList(LearnGrpc.GetServerPeopleListRequest request,
                StreamObserver<LearnGrpc.GetServerPeopleListReply> responseObserver) {
            //多次执行onNext方法,返回消息流
            responseObserver
                    .onNext(LearnGrpc.GetServerPeopleListReply.newBuilder().setName("彭于晏").build());
            responseObserver
                    .onNext(LearnGrpc.GetServerPeopleListReply.newBuilder().setName("张震").build());
            responseObserver
                    .onNext(LearnGrpc.GetServerPeopleListReply.newBuilder().setName("靳东").build());
            responseObserver.onCompleted();
        }

        //消息流-单个消息
        @Override
        public StreamObserver<LearnGrpc.CountClientPeopleRequest> countClientPeople(
                final StreamObserver<LearnGrpc.CountClientPeopleReply> responseObserver) {
            //初始化请求流
            return new StreamObserver<LearnGrpc.CountClientPeopleRequest>() {

                int count = 0;

                //编写对请求流中每个请求的操作
                public void onNext(LearnGrpc.CountClientPeopleRequest countClientPeopleRequest) {
                    count++;
                }

                public void onError(Throwable throwable) {

                }

                //请求流发送完成
                public void onCompleted() {
                    //onNext方法里填写要返回的消息
                    responseObserver.onNext(
                            LearnGrpc.CountClientPeopleReply.newBuilder().setCount(count).build());
                    responseObserver.onCompleted();
                }
            };
        }

        //消息流-消息流
        @Override
        public StreamObserver<LearnGrpc.ChatContent>
                chat(final StreamObserver<LearnGrpc.ChatContent> responseObserver) {
            return new StreamObserver<LearnGrpc.ChatContent>() {
                //每接受一个请求就返回一个响应
                public void onNext(LearnGrpc.ChatContent chatContent) {
                    System.out.println(chatContent.getContent());
                    responseObserver.onNext(LearnGrpc.ChatContent.newBuilder()
                            .setContent("Hi, I'm server!").build());
                }

                public void onError(Throwable throwable) {

                }

                public void onCompleted() {
                    responseObserver.onCompleted();
                }
            };
        }
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        ServiceServer serviceServer = new ServiceServer(50001);
        serviceServer.start();
    }
}
public class ServiceClient {

    private final ManagedChannel channel;
    private final ServiceGrpc.ServiceBlockingStub blockingStub;
    private final ServiceGrpc.ServiceStub asynStub;

    //用host和端口号构造通道,初始化阻塞stub和异步stub,分别用于发送单个消息和消息流
    public ServiceClient(String host, int port) {
        channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
        blockingStub = ServiceGrpc.newBlockingStub(channel);
        asynStub = ServiceGrpc.newStub(channel);
    }

    public void sayHello() {
        System.out.println("Method sayHello starts");
        LearnGrpc.SayHelloRequest request = LearnGrpc.SayHelloRequest.newBuilder().setName("client")
                .setHelloWords("hello").build();
        //直接接收单个消息返回值
        LearnGrpc.SayHelloReply reply = blockingStub.sayHello(request);
        System.out.println("Reply: " + reply.getReply());
        System.out.println("Method sayHello ends");
    }

    public void getServerPeopleList() {
        System.out.println("Method getServerPeopleList starts");
        System.out.println("People list:");
        LearnGrpc.GetServerPeopleListRequest request = LearnGrpc.GetServerPeopleListRequest
                .newBuilder().build();
        //用迭代器接收消息流
        Iterator<LearnGrpc.GetServerPeopleListReply> list = blockingStub
                .getServerPeopleList(request);
        while (list.hasNext()) {
            LearnGrpc.GetServerPeopleListReply reply = list.next();
            System.out.println(reply.getName());
        }
        System.out.println("Method getServerPeopleList ends");
    }

    public void countClientPeople() throws InterruptedException {
        System.out.println("Method countClientPeople starts");
        final CountDownLatch finishLatch = new CountDownLatch(1);
        LearnGrpc.CountClientPeopleRequest request = LearnGrpc.CountClientPeopleRequest.newBuilder()
                .build();
        StreamObserver<LearnGrpc.CountClientPeopleReply> responseObserver = new StreamObserver<LearnGrpc.CountClientPeopleReply>() {

            //onNext接收返回的单个消息
            public void onNext(LearnGrpc.CountClientPeopleReply countClientPeopleReply) {
                System.out.println("Client people count:");
                System.out.println(countClientPeopleReply.getCount());
            }

            public void onError(Throwable throwable) {

            }

            //接收完毕后打开障栅,结束方法
            public void onCompleted() {
                System.out.println("Method countClientPeople ends");
                finishLatch.countDown();
            }
        };
        StreamObserver<LearnGrpc.CountClientPeopleRequest> requestObserver = asynStub
                .countClientPeople(responseObserver);
        //多次发送消息,构成消息流
        requestObserver.onNext(request);
        requestObserver.onNext(request);
        requestObserver.onNext(request);
        requestObserver.onCompleted();
        //发送结束后利用障栅进行阻塞,等待服务器返回
        finishLatch.await();
    }

    public void chat() throws InterruptedException {
        System.out.println("Method chat starts");
        final CountDownLatch finishLatch = new CountDownLatch(1);
        LearnGrpc.ChatContent request1 = LearnGrpc.ChatContent.newBuilder()
                .setContent("Hello, I'm client!").build();
        LearnGrpc.ChatContent request2 = LearnGrpc.ChatContent.newBuilder()
                .setContent("Hello, I'm client!").build();
        StreamObserver<LearnGrpc.ChatContent> requestObserver = asynStub.chat(new StreamObserver<LearnGrpc.ChatContent>() {
            //接收到一个返回消息就调用一次onNext方法,用来接收消息流
            public void onNext(LearnGrpc.ChatContent chatContent) {
                System.out.println(chatContent.getContent());
            }

            public void onError(Throwable throwable) {

            }

            public void onCompleted() {
                System.out.println("Method chat ends");
                finishLatch.countDown();
            }
        });
        //多次调用onNext方法,发送消息流
        requestObserver.onNext(request1);
        requestObserver.onNext(request2);
        requestObserver.onCompleted();
        //发送结束后利用障栅进行阻塞,等待服务器返回
        finishLatch.await();
    }

    public static void main(String[] args) throws InterruptedException {
        ServiceClient serviceClient = new ServiceClient("localhost", 50001);
        serviceClient.sayHello();
        serviceClient.getServerPeopleList();
        serviceClient.countClientPeople();
        serviceClient.chat();
    }
}

先运行服务器,得到以下输出:

server started
server blocked

再运行客户端的四个方法,得到以下输出:

Method sayHello starts
Reply: Welcome, client!
Method sayHello ends
Method getServerPeopleList starts
People list:
彭于晏
张震
靳东
Method getServerPeopleList ends
Method countClientPeople starts
Client people count:
3
Method countClientPeople ends
Method chat starts
Hi, I'm server!
Hi, I'm server!
Method chat ends

通过以上实例,可以了解gRPC的基本使用方法
后续将会对gRPC再进行源码层面的剖析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值