ProtoBuff之GRPC(流式)

本文详细介绍并演示了GRPC的四种通信模式:单向阻塞数据传递、单向服务器端流式响应、单向客户端流式请求及双向流式请求和响应。通过配置Maven的pom.xml文件,编写proto文件,实现服务端与客户端的通信,并提供完整的代码示例。

有了前面的基础 我们已经构建了服务端和客户端连接的工具类  也已经熟悉了通过maven去处理对应的proto文件  

如果有不是很清楚的可以看看前面的基础内容  或者有不足之处欢迎指正!

--------------------------------------------------------------------------------------------------------------------------------------------

今天我们来熟悉一下4种GRPC的通信方式 我们还是先配置下maven的pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>modle</groupId>
    <artifactId>modle</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <modules>
        <module>ServerGrpc</module>
        <module>ClientGrpc</module>
        <module>Demo</module>
    </modules>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <grpc.version>1.20.0</grpc.version>
        <os.plugin.version>1.5.0.Final</os.plugin.version>
        <protobuf.plugin.version>0.5.1</protobuf.plugin.version>
        <protoc.version>3.5.1</protoc.version>
    </properties>
 <dependencies>
        <dependency>
            <groupId>model.grpc.util</groupId>
            <artifactId>GrpcUtil</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty-shaded</artifactId>
            <version>1.20.0</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>1.20.0</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>1.20.0</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.29.Final</version>
        </dependency>
  <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>1.24</version>
        </dependency>
  <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java-util</artifactId>
            <version>3.7.1</version>
        </dependency>
    </dependencies>
 <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-bom</artifactId>
                <version>${grpc.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>${os.plugin.version}</version>
            </extension>
        </extensions>
 <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
            <plugin>
  <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>${protobuf.plugin.version}</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>
                    <attachProtoSources>false</attachProtoSources>
                    <clearOutputDirectory>false</clearOutputDirectory>
                    <outputDirectory>${basedir}/src/main/java</outputDirectory>
                </configuration>
<executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

其次我们开始编写对应的proto文件

syntax = "proto3";

package Demo05GRPC.proto;

option java_package = "Demo05GRPC.proto";

option java_outer_classname = "StudentProto";

option java_multiple_files = true;


service StudentRpcService {

    //单向阻塞数据传递
    rpc getRealName (StudentRequest) returns (StudentResponse) {
    };

    //单向服务器端流式响应
    rpc getRealByAge (ByAgeRequest) returns (stream StudentByAgeResponse) {
    };

    //单向客户端端流式请求
    rpc getStudentByAge (stream ByAgeRequest) returns (StudentListResponse) {
    };

    //双向流式请求和响应
    rpc getStudentByBItalk (stream StreamRequest) returns (stream StreamResponse) {
    };
}

message StudentRequest {
    string name = 1;
}

message StudentResponse {
    string realName = 1;
}

message ByAgeRequest {
    string age = 1;
}

message StudentByAgeResponse {
    string name = 1;
    string age = 2;
}

message StudentListResponse {
    repeated StudentByAgeResponse response = 1;
}


message StreamRequest {
    string request_info = 1;
}


message StreamResponse {
    string response_info = 1;
}

通过maven编译和生成对应的java类  如下所示

然后我们编写对应的GRPC生成的远程服务的实现类

ServerStudentImp(注意这个类启动服务器时候需要加载)

package Demo05GRPC;

import Demo05GRPC.proto.*;
import io.grpc.stub.StreamObserver;

import java.util.UUID;


public class ServerStudentImp extends StudentRpcServiceGrpc.StudentRpcServiceImplBase {



    //单向阻塞数据传递
    @Override
    public void getRealName(StudentRequest request, StreamObserver<StudentResponse> responseObserver) {
        responseObserver.onNext(StudentResponse.newBuilder().setRealName("张三").build());
        responseObserver.onNext(StudentResponse.newBuilder().setRealName("李四").build());
        responseObserver.onCompleted();
    }

    //单向服务器端流式响应
    @Override
    public void getRealByAge(ByAgeRequest request, StreamObserver<StudentByAgeResponse> responseObserver) {
        responseObserver.onNext(StudentByAgeResponse.newBuilder().setName("李四").build());
        responseObserver.onNext(StudentByAgeResponse.newBuilder().setAge("22").build());

        responseObserver.onNext(StudentByAgeResponse.newBuilder().setName("战三").build());
        responseObserver.onNext(StudentByAgeResponse.newBuilder().setAge("25").build());
        responseObserver.onCompleted();
    }

    //单向客户端端流式请求
    @Override
    public StreamObserver<ByAgeRequest> getStudentByAge(StreamObserver<StudentListResponse> responseObserver) {

        return new StreamObserver<ByAgeRequest>() {
            //请求是流式  请求一次  返回一次
            @Override
            public void onNext(ByAgeRequest byAgeRequest) {
                byAgeRequest.getAge();
            }

            @Override
            public void onError(Throwable throwable) {

            }
            @Override
            public void onCompleted() {
                StudentByAgeResponse                 response=StudentByAgeResponse.newBuilder().setAge("22").setName("张三").build();
                StudentByAgeResponse response1=StudentByAgeResponse.newBuilder().setAge("23").setName("王五").build();
                StudentByAgeResponse respons2=StudentByAgeResponse.newBuilder().setAge("24").setName("赵6").build();
                StudentListResponse listResponse=StudentListResponse.newBuilder().addResponse(response).addResponse(response1).addResponse(respons2).build();
                responseObserver.onNext(listResponse);
                responseObserver.onCompleted();
            }
        };
    }

  //双向流式请求和响应
    @Override
    public StreamObserver<StreamRequest> getStudentByBItalk(StreamObserver<StreamResponse> responseObserver) {

        return new StreamObserver<StreamRequest>() {
            @Override
            public void onNext(StreamRequest streamRequest) {
                responseObserver.onNext(StreamResponse.newBuilder().setResponseInfo(UUID.randomUUID()+"").build());
            }

            @Override
            public void onError(Throwable throwable) {
                System.out.println(throwable.getMessage());
            }

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

最后就是我们的客户端和服务端的测试代码了

服务端如下:

package Demo05GRPC;
import io.grpc.Server;
import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder;

import java.io.IOException;

public class ServerGrpc {

    private Server server;

    public void start() throws IOException {
        NettyServerBuilder serverBuilder=NettyServerBuilder.forPort(8002);
        serverBuilder.addService(new ServerStudentImp());//将所有的远程服务类加进去管理
        //我这里只有一个实现类

        this.server = serverBuilder.build();
        this.server.start();
    }



    public void stop(){
        if(null !=this.server){
            this.server.shutdown();
        }
    }

 /**
     * 阻塞等待RPC服务器停止
     */
    public void blockUntilShutdown() {
        if (server != null) {
            try {
                server.awaitTermination();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }



    public static void main(String[] args) throws IOException {
        ServerGrpc serverGrpc=new ServerGrpc();
        serverGrpc.start();
        serverGrpc.blockUntilShutdown();
    }



}

客户端代码:

package Demo05GRPC;

import Demo05GRPC.proto.*;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;

public class GrpcClient {


    public static void main(String[] args) {
        ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost"
                , 8002).usePlaintext(true).build();
        /*StudentRpcServiceGrpc.StudentRpcServiceBlockingStub stub = StudentRpcServiceGrpc.newBlockingStub(managedChannel);
        java.util.Iterator<Demo05GRPC.proto.StudentByAgeResponse> response = stub.getRealByAge(ByAgeRequest.newBuilder().setAge("222").build());

        while (response.hasNext()){
            StudentByAgeResponse response1= response.next();
            System.out.println(response1.getAge());
            System.out.println(response1.getName());
        }*/
  /*StreamObserver<StudentListResponse> streamObserver=  new StreamObserver<StudentListResponse>() {
            @Override
            public void onNext(StudentListResponse value) {
                value.getResponseList().forEach(e->{
                    System.out.println(e.getName());
                    System.out.println(e.getAge());
                });
            }
            @Override
            public void onError(Throwable throwable) {

            }
            @Override
            public void onCompleted() {

            }
        };
 StudentRpcServiceGrpc.StudentRpcServiceStub stub1=StudentRpcServiceGrpc.newStub(managedChannel);
        StreamObserver<Demo05GRPC.proto.ByAgeRequest> response1= stub1.getStudentByAge(streamObserver);
        response1.onNext(ByAgeRequest.newBuilder().setAge("22").build());
        response1.onNext(ByAgeRequest.newBuilder().setAge("23").build());
        response1.onNext(ByAgeRequest.newBuilder().setAge("24").build());
        response1.onNext(ByAgeRequest.newBuilder().setAge("25").build());
        response1.onNext(ByAgeRequest.newBuilder().setAge("26").build());
        response1.onCompleted();

        try {
            Thread.sleep(5000);
        }catch (Exception e){
            e.getMessage();
        }

        }*/
  StudentRpcServiceGrpc.StudentRpcServiceStub stub1 = StudentRpcServiceGrpc.newStub(managedChannel);

        StreamObserver<Demo05GRPC.proto.StreamRequest> requestStreamObserver = stub1.getStudentByBItalk(new StreamObserver<StreamResponse>() {
            @Override
            public void onNext(StreamResponse streamResponse) {
                System.out.println(streamResponse.getResponseInfo());
            }

            @Override
            public void onError(Throwable throwable) {

            }

            @Override
            public void onCompleted() {

            }
        });
 for (int i = 0; i < 10; i++) {
            requestStreamObserver.onNext(StreamRequest.newBuilder().setRequestInfo(System.currentTimeMillis() + "").build());
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.getMessage();
            }
        }

        try {
            Thread.sleep(5000);
        } catch (Exception e) {
            e.getMessage();
        }
    }
}

看到这里 我们可以对照之前写的工具类 已经封装好了  结合项目只需要将我们远程服务的所有实现在初始化的时候调用NettyServerBuilder.addService()就可以使用我们的grpc 服务与服务之间的轻松访问。

### gRPC 流式通信在跨区域分布式系统中的最佳实践 #### 1. ### gRPC 流式通信简介 gRPC 是一种现代的远程过程调用框架,支持多种编程语言和平台。它利用 Protocol Buffers(Protobuf)作为接口定义语言(IDL),并通过 HTTP/2 协议传输数据[^1]。相比传统的 RESTful API 和 JSON 序列化方法,gRPC 提供更高的效率、更低的延迟以及更强的功能扩展能力。 #### 2. ### 使用 gRPC 进行百万级数据汇总的优势 针对跨省市区县的大规模数据汇总需求,采用基于 gRPC流式通信具有如下显著优势: - **高效压缩与序列化**:Protobuf 默认启用二进制编码方案,相较于文本型格式如 XML 或 JSON 更加紧凑轻便,有效减少了网络流量开销。 - **双向流支持**:允许服务器端持续推送新到达的数据至客户端,而无需频繁建立连接请求;同时也让客户端能够批量上传本地缓存的结果集给中心节点处理。 - **错误恢复机制**:即使某个环节发生异常中断,也可以快速定位失败位置重新发起尝试而不影响整体流程进度。 ```protobuf syntax = "proto3"; service AggregationService { rpc StreamData(stream DataChunk) returns (AggregatedResult) {} } message DataChunk { int64 id = 1; bytes payload = 2; // Serialized data chunk content. } message AggregatedResult { repeated Summary summaries = 1; message Summary { string regionCode = 1; double totalValue = 2; } } ``` #### 3. ### 结合 Java 服务的具体实施方案 为了适应复杂的业务逻辑并保障系统的稳定性可靠性,在实际开发过程中可以从以下几个方面入手: ##### A. ### 设计合理的 IDL 文件结构 按照不同层次划分实体对象关系图谱,明确指出各个字段含义及其约束条件。例如上面展示的例子中 `DataChunk` 表示每次发送过来的小片段原始资料包,其中包含了唯一标识符 (`id`) 和真实负载数据(`payload`) 。与此同时,返回结果也封装成易于理解的形式以便后续进一步分析计算。 ##### B. ### 构建健壮的服务端拦截器体系 通过自定义实现服务端拦截器来增强安全性验证、日志记录等功能模块。特别是当面临高并发访问压力测试期间,可以借助此工具监控各项指标表现情况及时调整资源配置策略[^3]。 ```java public class LoggingServerInterceptor implements ServerInterceptor { @Override public <ReqT, RespT> Listener<RespT> interceptCall( MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel channel) { long startTimeMillis = System.currentTimeMillis(); return new ForwardingListener.SimpleForwardingListener<>(delegate) { @Override public void onMessage(RespT response) { super.onMessage(response); log.info("{} took {} ms", method.getFullMethodName(), System.currentTimeMillis() - startTimeMillis); } }; } } ``` ##### C. ### 整合多数据库读写分离技术 考虑到单体数据库难以承受如此巨大的查询负担,有必要引入分库分表理念将热点数据分散存储到独立实例当中去。这样不仅可以提升检索速度还能减轻主备复制带来的额外成本支出。另外还可以考虑运用 Apache Kafka 等消息队列中间件充当缓冲区角色平滑过渡突发高峰时刻的压力峰值[^2]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值