超高性能rpc框架之gRPC 快速整合gRPC+nacos+springCloud

什么是gRPC

gRPC是一个超高性能的RPC框架,对市面上主流语言都提供了支持。

  • gRPC可通过protobuf来定义接口,可以对接口的约束更加严格

  • 使用protobuf序列化,大幅减小传输数据量,从而对功耗,带宽,性能都有显著提升

  • 基于http2标准设计,支持双向流通讯

  • 支持异步请求,异步返回

使用场景

  • 需要对接口有更加严格的管控,如对公司外部提供接口,我们不希望客户端随意传递数据,这时我们就可以使用gRPC来对接口约束。
  • 对传输性能有更高的要求,如果我们传输的消息体过大,或调度过于频繁不希望影响服务器的性能时,也可以考虑使用,使用gRPC时我们的消息体比JSON或者文本传输数据量要小的多。

proto语法

.proto TypeNotesC++ TypeJava TypePython Type[2]Go TypeRuby TypeC# TypePHP Type
doubledoubledoublefloatfloat64Floatdoublefloat
floatfloatfloatfloatfloat32Floatfloatfloat
int32使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代int32intintint32Fixnum 或者 Bignum(根据需要)intinteger
uint32使用变长编码uint32intint/longuint32Fixnum 或者 Bignum(根据需要)uintinteger
uint64使用变长编码uint64longint/longuint64Bignumulonginteger/string
sint32使用变长编码,这些编码在负值时比int32高效的多int32intintint32Fixnum 或者 Bignum(根据需要)intinteger
sint64使用变长编码,有符号的整型值。编码时比通常的int64高效。int64longint/longint64Bignumlonginteger/string
fixed32总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。uint32intintuint32Fixnum 或者 Bignum(根据需要)uintinteger
fixed64总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。uint64longint/longuint64Bignumulonginteger/string
sfixed32总是4个字节int32intintint32Fixnum 或者 Bignum(根据需要)intinteger
sfixed64总是8个字节int64longint/longint64Bignumlonginteger/string
boolboolbooleanboolboolTrueClass/FalseClassboolboolean
string一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。stringStringstr/unicodestringString (UTF-8)stringstring
bytes可能包含任意顺序的字节数据。stringByteStringstr[]byteString (ASCII-8BIT)ByteStringstring

当我们需要表示一个集合时使用repeated关键字

message GetByUidParams{
  repeated string uid = 1;
}

表示kv存储结构可使用map关键字

map<key_type,value_type> field_name = num;

message UserInfo{
  string uid = 1;
  string avatar = 2;
  string phone = 3;
  string nickName = 4;
  uint64 authTime = 5;
  uint64 loginTime = 6;
  uint64 recommendId = 7;
  string realName = 8;
  string email = 9;
}
message UserInfoMap {
    map<string,UserInfo> infos = 1;
    map<string,string> infomap = 2;
}

proto option选项

nametypedesc
java_multiple_filesbool为True每一个message文件都会有一个单独的class文件;否则,message全部定义在outerclass文件里
java_packagestr该字段是option的,用于标识生成的java文件的package。如果没有指定,则使用proto里定义的package,如果package也没有指定,那就会生成在根目录下;
java_outer_classnamestr该字段是option的,用于指定proto文件生成的java类的outerclass类名。什么是outerclass?简单来说就是用一个class文件来定义所有的message对应的java类。这个class就是outerclass;如果没有指定,默认是proto文件的驼峰式

异常处理

当我们调用onError方法时表示服务端异常返回,此时不需要再调用onCompleted方法

responseObserver.onError(Status.NOT_FOUND.augmentDescription("user not fund").asException());

我们在client端只需要捕获StatusRuntimeException异常即可,该类继承RuntimeException

该异常类包含的Statusmessage都可以帮助我们做业务操作

整合Spring cloud+gRPC+Nacos

我们使用SpringCloud作为项目载体帮我们管理Bean,使用gRPC进行服务间通讯,使用Nacos做注册中心来实现gRPC的负载

注:Nacos2.0支持的gRPC

Nacos2.0版本相比1.X新增了gRPC的通信方式,因此需要增加2个端口。新增端口是在配置的主端口(server.port)基础上,进行一定偏移量自动生成。

端口与主端口的偏移量描述
98481000客户端gRPC请求服务端端口,用于客户端向服务端发起连接和请求
98491001服务端gRPC请求服务端端口,用于服务间同步等

准备工作

首先准备一个SpringCloud项目的基础脚手架

引入grpc相关依赖,用于生成java端代码

<properties>
    <!-- 依赖相关配置 -->
    <io.grpc.version>1.30.0</io.grpc.version>
    <!-- 插件相关配置 -->
    <os-maven-plugin.version>1.6.2</os-maven-plugin.version>
    <protobuf-maven-plugin.version>0.6.1</protobuf-maven-plugin.version>
</properties>
<dependencies>
    <!-- 引入 gRPC Protobuf 依赖,因为使用它作为序列化库 -->
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-protobuf</artifactId>
        <version>${io.grpc.version}</version>
    </dependency>
    <!-- 引入 gRPC Stub 依赖,因为使用它作为 gRPC 客户端库 -->
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-stub</artifactId>
        <version>${io.grpc.version}</version>
    </dependency>
</dependencies>
<build>
    <extensions>
        <!-- os-maven-plugin 插件,从 OS 系统中获取参数 -->
        <extension>
            <groupId>kr.motd.maven</groupId>
            <artifactId>os-maven-plugin</artifactId>
            <version>${os-maven-plugin.version}</version>
        </extension>
    </extensions>
    <plugins>
        <!-- protobuf-maven-plugin 插件,通过 protobuf 文件,生成 Service 和 Message 类 -->
        <plugin>
            <groupId>org.xolstice.maven.plugins</groupId>
            <artifactId>protobuf-maven-plugin</artifactId>
            <version>${protobuf-maven-plugin.version}</version>
            <configuration>
                <pluginId>grpc-java</pluginId>
                <protocArtifact>com.google.protobuf:protoc:3.9.1:exe:${os.detected.classifier}</protocArtifact>
                <pluginArtifact>io.grpc:protoc-gen-grpc-java:${io.grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <goal>compile-custom</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

新建目录/src/main/proto 攥写proto文件

UserService.proto

syntax = "proto3";
option java_multiple_files = true;

package com.test.grpc.api;

message UserGetRequest {
    int32 id = 1;
}

message UserGetResponse {
    int32 id = 1;
    string name = 2;
    int32 gender = 3;
}

message UserCreateRequest {
    string name = 1;
    int32 gender = 2;
}

message UserCreateResponse {
    int32 id = 1;
}

service UserService {
    rpc get(UserGetRequest) returns (UserGetResponse);
    rpc create(UserCreateRequest) returns (UserCreateResponse);
    rpc getIter(UserGetRequest) returns (stream UserGetResponse);   # 流返回
    rpc callAStream(stream UserGetRequest) returns (stream UserGetResponse);  # 双向流通讯
}

生成java端代码

mvn clean compile

生成的代码可在target目录下看到

image-20211012115632695

服务端

引入服务端依赖

<!-- 引入 gRPC Server Starter 依赖,实现对 gRPC 的自动配置 -->
<dependency>
    <groupId>net.devh</groupId>
    <artifactId>grpc-server-spring-boot-starter</artifactId>
    <version>2.12.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>com.example</groupId>
    <artifactId>api</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--必备: 注册中心客户端-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--如没有使用sentinel则需要引入hystrix-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.1.3.RELEASE</version>
</dependency>

配置文件

grpc:
  server:
    port: 3333
server:
  port: 2223
spring:
  application:
    name: test-server
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        enabled: true

实现UserService

@GrpcService
public class UserServiceImpl extends UserServiceGrpc.UserServiceImplBase {

    @Override
    public void get(UserGetRequest request, StreamObserver<UserGetResponse> responseObserver) {
        responseObserver.onNext(UserGetResponse.newBuilder().setName("aa").setGender(1).build());
        responseObserver.onCompleted();
    }

    @Override
    public void create(UserCreateRequest request, StreamObserver<UserCreateResponse> responseObserver) {
        responseObserver.onNext(UserCreateResponse.newBuilder().setId(1).build());
        responseObserver.onCompleted();
    }

    @SneakyThrows
    @Override
    public void getIter(UserGetRequest request, StreamObserver<UserGetResponse> responseObserver) {
        responseObserver.onNext(UserGetResponse.newBuilder().setId(1).build());
        TimeUnit.SECONDS.sleep(1);
        responseObserver.onNext(UserGetResponse.newBuilder().setId(2).build());
        TimeUnit.SECONDS.sleep(3);
        responseObserver.onNext(UserGetResponse.newBuilder().setId(3).build());
        responseObserver.onCompleted();
    }

    @Override
    public StreamObserver<UserGetRequest> callAStream(StreamObserver<UserGetResponse> responseObserver) {
        return new StreamObserver<UserGetRequest>() {
            @Override
            public void onNext(UserGetRequest value) {
                responseObserver.onNext(UserGetResponse.newBuilder().setId(value.getId()).build());
                System.out.println(value.toString());
            }

            @Override
            public void onError(Throwable t) {
                t.printStackTrace();
            }

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

至此,一个基于gRPC的springCloud服务就完成了

启动服务,在nacos服务详情中就可以看到该服务的注册信息

image-20211012141706549

客户端

引入客户端依赖

<dependency>
    <groupId>net.devh</groupId>
    <artifactId>grpc-client-spring-boot-starter</artifactId>
    <version>2.12.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>com.example</groupId>
    <artifactId>api</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--必备: 注册中心客户端-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.1.3.RELEASE</version>
</dependency>

配置文件

server:
  port: 2222
grpc:
  client:
    test-server:  # 服务名称
      negotiation-type: plaintext
      enableKeepAlive: true
      keepAliveWithoutCalls: true
spring:
  application:
    name: test-client
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        namespace:

调用gRPC服务

@RestController
@Slf4j
public class TestController {

    // 阻塞gRPC
    @GrpcClient("test-server")
    private UserServiceGrpc.UserServiceBlockingStub userServiceBlockingStub;
    // 非阻塞gRPC 获得Future对象
    @GrpcClient("test-server")
    private UserServiceGrpc.UserServiceFutureStub userServiceFutureStub;
    // 双向流通讯
    @GrpcClient("test-server")
    private UserServiceGrpc.UserServiceStub userServiceStub;

    @GetMapping("test1")
    public String test1() {
        // 构建请求数据
        UserGetRequest request = UserGetRequest.newBuilder().setId(1).build();
        // 执行gRPC请求
        UserGetResponse userGetResponse = userServiceBlockingStub.get(request);
        return userGetResponse.toString();
    }

    @SneakyThrows
    @GetMapping("test2")
    public String test2() {
        // 构建请求数据
        UserGetRequest request = UserGetRequest.newBuilder().setId(1).build();
        // 执行gRPC请求
        ListenableFuture<UserGetResponse> future = userServiceFutureStub.get(request);
        return future.get().toString();
    }

    @GetMapping("test3")
    public String test3() {
        // 构建请求数据
        UserGetRequest request = UserGetRequest.newBuilder().setId(1).build();
        // 执行gRPC请求
        Iterator<UserGetResponse> iter = userServiceBlockingStub.getIter(request);
        // 阻塞等待
        while (iter.hasNext()) {
            System.out.println(iter.next().toString());
        }
        return "1";
    }

    private StreamObserver<UserGetRequest> requestObserver = null;

    @GetMapping("test4")
    public String test4() {
        // 创建连接
        ClientCallStreamObserver<UserGetResponse> responseObserver = new ClientCallStreamObserver<UserGetResponse>() {
            @Override
            public void onNext(UserGetResponse value) {
                log.info("on next:{}", value.toString());
            }

            @Override
            public void onError(Throwable t) {
                t.printStackTrace();
            }

            @Override
            public void onCompleted() {
                requestObserver.onCompleted();
            }

            @Override
            public boolean isReady() {
                return true;
            }

            @Override
            public void setOnReadyHandler(Runnable onReadyHandler) {

            }

            @Override
            public void disableAutoInboundFlowControl() {

            }

            @Override
            public void request(int count) {

            }

            @Override
            public void setMessageCompression(boolean enable) {

            }

            @Override
            public void cancel(@Nullable String message, @Nullable Throwable cause) {
                log.info("cancel:{}", message);
            }
        };
        this.requestObserver = userServiceStub.callAStream(responseObserver);
        return "1";
    }

    @GetMapping("test5")
    public String test5() {
        // 发送消息
        requestObserver.onNext(UserGetRequest.newBuilder().setId(1).build());
        return "1";
    }

}

启动客户端测试5个接口


至此我们就学会了如何整合Spring cloud+gRPC+Nacos

  • 6
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
Java + gRPC + grpc-gateway 的实践主要分为以下几个步骤: 1. 定义 proto 文件 在 proto 文件中定义需要调用的服务以及方法,同时指定请求和响应的数据类型。例如: ``` syntax = "proto3"; package example; service ExampleService { rpc ExampleMethod (ExampleRequest) returns (ExampleResponse) {} } message ExampleRequest { string example_field = 1; } message ExampleResponse { string example_field = 1; } ``` 2. 使用 protoc 编译 proto 文件 使用 protoc 编译 proto 文件,生成 Java 代码。例如: ``` protoc --java_out=./src/main/java ./example.proto ``` 3. 实现 gRPC 服务 在 Java 代码中实现定义的 gRPC 服务,例如: ``` public class ExampleServiceImpl extends ExampleServiceGrpc.ExampleServiceImplBase { @Override public void exampleMethod(ExampleRequest request, StreamObserver<ExampleResponse> responseObserver) { // 实现具体逻辑 ExampleResponse response = ExampleResponse.newBuilder().setExampleField("example").build(); responseObserver.onNext(response); responseObserver.onCompleted(); } } ``` 4. 启动 gRPC 服务器 使用 gRPC 提供的 ServerBuilder 构建 gRPC 服务器,并启动服务器。例如: ``` Server server = ServerBuilder.forPort(8080).addService(new ExampleServiceImpl()).build(); server.start(); ``` 5. 集成 grpc-gateway 使用 grpc-gateway 可以将 gRPC 服务转换为 HTTP/JSON API。在 proto 文件中添加以下内容: ``` import "google/api/annotations.proto"; service ExampleService { rpc ExampleMethod (ExampleRequest) returns (ExampleResponse) { option (google.api.http) = { post: "/example" body: "*" }; } } ``` 在 Java 代码中添加以下内容: ``` Server httpServer = ServerBuilder.forPort(8081).addService(new ExampleServiceImpl()).build(); httpServer.start(); String grpcServerUrl = "localhost:8080"; String httpServerUrl = "localhost:8081"; ProxyServerConfig proxyConfig = new ProxyServerConfig(grpcServerUrl, httpServerUrl, "/example"); HttpProxyServer httpProxyServer = new HttpProxyServer(proxyConfig); httpProxyServer.start(); ``` 6. 测试 使用 HTTP/JSON API 调用 gRPC 服务,例如: ``` POST http://localhost:8081/example Content-Type: application/json { "example_field": "example" } ``` 以上就是 Java + gRPC + grpc-gateway 的实践步骤。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值