grpc双向流式调用——构建一个简单的流数据分析器

本文地址:https://blog.csdn.net/m0_37595562/article/details/81638415
传送门:

关于grpc,之前已经基于循序渐进的原则和大家聊过简单调用,服务端流式调用和客户端流式调用。那么我们今天聊的grpc应该就是双向流式调用了。相对之前的有所复杂,但是其实有了前面服务端流式调用和客户端流式调用的基础,双向流式调用其实只是两者的结合,非常简单。
本次双向流式调用的实验,我们应用场景和之前的客户端流式调用类似,也是客户端传过来一串数据,服务端进行数据的处理,将数据的统计信息作为返回结果返回给客户端。不过这一次我们不再像客户端流式调用那样等待客户端传送完毕再返回结果,而是在客户端每次传过来一个数据时就返回一个结果。
本篇文章主要内容如下:

  1. 创建项目
  2. 定义服务
  3. 创建服务端
  4. 创建客户端
  5. 运行实验

创建项目

这次项目的结构和客户端流式调用中的差不多,项目中主要包含了一个server和一个client模块。如下图所示:
图片.png
然后还是在总的项目的pom.xml文件中添加依赖,这次我们也是添加grpc-all的依赖,较为方便,另外slf4j的依赖用于记录日志。另外,需要注意的是需要加入protobuf的maven编译插件,否则无法通过maven对proto文件进行编译。具体内容如下:

<?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>com.sunny</groupId>
    <artifactId>grpc-bi-stream</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>grpcserver</module>
        <module>grpcclient</module>
    </modules>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <grpc.version>1.12.0</grpc.version>
        <protoc.version>3.5.1-1</protoc.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-all</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.25</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>
</project>

两个模块中的依赖继承项目的依赖即可,本实验中无需增加其他内容。

定义服务

这次定义的服务和客户端流式调用的例子类似,只不过在返回的部分增加了一个stream的关键字,表示服务端返回结果也是流式的。

syntax = "proto3";

//生成多文件,可以将类都生成在一个文件中
option java_multiple_files = false;
//包名
option java_package = "io.grpc.examples.calculate";
//生成的类名
option java_outer_classname = "CalculateProto";

package calculate;

// 定义服务
service CalculateService {
    // 服务中的方法,传过来一个Value类型的流,返回一个Result类型的流信息
    rpc getResult (stream Value) returns (stream Result) {}
}
//定义Value消息类型,用于客户端消息
message Value {
    int32 value = 1;
}
//定义Result消息类型,包含总和,数字数量和平均值,用于服务端消息返回
message Result {
    int32 sum = 1;
    int32 cnt = 2;
    double avg = 3;
}

具体服务内容的解释,大家通过注释或者我的上一篇文章应该很容易就可以看懂了。感兴趣的童鞋可以深入学习一下protocol buffer的语法。值得一提的是,这里Sunny并未选择将类生成多个文件,而是将所有编译得到的类都放到一个文件中。这里给大家展示一下编译得到的类的代码。
CalculateServiceGrpc.java:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package io.grpc.examples.calculate;

import com.google.protobuf.Descriptors.FileDescriptor;
import io.grpc.BindableService;
import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ExperimentalApi;
import io.grpc.MethodDescriptor;
import io.grpc.ServerServiceDefinition;
import io.grpc.ServiceDescriptor;
import io.grpc.MethodDescriptor.MethodType;
import io.grpc.examples.calculate.CalculateProto.Result;
import io.grpc.examples.calculate.CalculateProto.Value;
import io.grpc.protobuf.ProtoFileDescriptorSupplier;
import io.grpc.protobuf.ProtoMethodDescriptorSupplier;
import io.grpc.protobuf.ProtoServiceDescriptorSupplier;
import io.grpc.protobuf.ProtoUtils;
import io.grpc.stub.AbstractStub;
import io.grpc.stub.ClientCalls;
import io.grpc.stub.ServerCalls;
import io.grpc.stub.StreamObserver;
import io.grpc.stub.ServerCalls.BidiStreamingMethod;
import io.grpc.stub.ServerCalls.ClientStreamingMethod;
import io.grpc.stub.ServerCalls.ServerStreamingMethod;
import io.grpc.stub.ServerCalls.UnaryMethod;

public final class CalculateServiceGrpc {
    public static final String SERVICE_NAME = "calculate.CalculateService";
    /** @deprecated */
    @Deprecated
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1901")
    public static final MethodDescriptor<Value, Result> METHOD_GET_RESULT = getGetResultMethodHelper();
    private static volatile MethodDescriptor<Value, Result> getGetResultMethod;
    private static final int METHODID_GET_RESULT = 0;
    private static volatile ServiceDescriptor serviceDescriptor;

    private CalculateServiceGrpc() {
    }

    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1901")
    public static MethodDescriptor<Value, Result> getGetResultMethod() {
        return getGetResultMethodHelper();
    }

    private static MethodDescriptor<Value, Result> getGetResultMethodHelper() {
        MethodDescriptor<Value, Result> getGetResultMethod = getGetResultMethod;
        if (getGetResultMethod == null) {
            Class var1 = CalculateServiceGrpc.class;
            synchronized(CalculateServiceGrpc.class) {
                getGetResultMethod = getGetResultMethod;
                if (getGetResultMethod == null) {
                    getGetResultMethod = getGetResultMethod = MethodDescriptor.newBuilder().setType(MethodType.BIDI_STREAMING).setFullMethodName(MethodDescriptor.generateFullMethodName("calculate.CalculateService", "getResult")).setSampledToLocalTracing(true).setRequestMarshaller(ProtoUtils.marshaller(Value.getDefaultInstance())).setResponseMarshaller(ProtoUtils.marshaller(Result.getDefaultInstance())).setSchemaDescriptor(new CalculateServiceGrpc.CalculateServiceMethodDescriptorSupplier("getResult")).build();
                }
            }
        }

        return getGetResultMethod;
    }

    public static CalculateServiceGrpc.CalculateServiceStub newStub(Channel channel) {
        return new CalculateServiceGrpc.CalculateServiceStub(channel);
    }

    public static CalculateServiceGrpc.CalculateServiceBlockingStub newBlockingStub(Channel channel) {
        return new CalculateServiceGrpc.CalculateServiceBlockingStub(channel);
    }

    public static CalculateServiceGrpc.CalculateServiceFutureStub newFutureStub(Channel channel) {
        return new CalculateServiceGrpc.CalculateServiceFutureStub(channel);
    }

    public static ServiceDescriptor getServiceDescriptor() {
        ServiceDescriptor result = serviceDescriptor;
        if (result == null) {
            Class var1 = CalculateServiceGrpc.class;
            synchronized(CalculateServiceGrpc.class) {
                result = serviceDescriptor;
                if (result == null) {
                    serviceDescriptor = result = ServiceDescriptor.newBuilder("calculate.CalculateService").setSchemaDescriptor(new CalculateServiceGrpc.CalculateServiceFileDescriptorSupplier()).addMethod(getGetResultMethodHelper()).build();
                }
            }
        }

        return result;
    }

    private static final class CalculateServiceMethodDescriptorSupplier extends CalculateServiceGrpc.CalculateServiceBaseDescriptorSupplier implements ProtoMethodDescriptorSupplier {
        private final String methodName;

        CalculateServiceMethodDescriptorSupplier(String methodName) {
            this.methodName = methodName;
        }

        public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() {
            return this.getServiceDescriptor().findMethodByName(this.methodName);
        }
    }

    private static final class CalculateServiceFileDescriptorSupplier extends CalculateServiceGrpc.CalculateServiceBaseDescriptorSupplier {
        CalculateServiceFileDescriptorSupplier() {
        }
    }

    private abstract static class CalculateServiceBaseDescriptorSupplier implements ProtoFileDescriptorSupplier, ProtoServiceDescriptorSupplier {
        CalculateServiceBaseDescriptorSupplier() {
        }

        public FileDescriptor getFileDescriptor() {
            return CalculateProto.getDescriptor();
        }

        public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() {
            return this.getFileDescriptor().findServiceByName("CalculateService");
        }
    }

    private static final class MethodHandlers<Req, Resp> implements UnaryMethod<Req, Resp>, ServerStreamingMethod<Req, Resp>, ClientStreamingMethod<Req, Resp>, BidiStreamingMethod<Req, Resp> {
        private final CalculateServiceGrpc.CalculateServiceImplBase serviceImpl;
        private final int methodId;

        MethodHandlers(CalculateServiceGrpc.CalculateServiceImplBase serviceImpl, int methodId) {
            this.serviceImpl = serviceImpl;
            this.methodId = methodId;
        }

        public void invoke(Req request, StreamObserver<Resp> responseObserver) {
            switch(this.methodId) {
            default:
                throw new AssertionError();
            }
        }

        public StreamObserver<Req> invoke(StreamObserver<Resp> responseObserver) {
            switch(this.methodId) {
            case 0:
                return this.serviceImpl.getResult(responseObserver);
            default:
                throw new AssertionError();
            }
        }
    }

    public static final class CalculateServiceFutureStub extends AbstractStub<CalculateServiceGrpc.CalculateServiceFutureStub> {
        private CalculateServiceFutureStub(Channel channel) {
            super(channel);
        }

        private CalculateServiceFutureStub(Channel channel, CallOptions callOptions) {
            super(channel, callOptions);
        }

        protected CalculateServiceGrpc.CalculateServiceFutureStub build(Channel channel, CallOptions callOptions) {
            return new CalculateServiceGrpc.CalculateServiceFutureStub(channel, callOptions);
        }
    }

    public static final class CalculateServiceBlockingStub extends AbstractStub<CalculateServiceGrpc.CalculateServiceBlockingStub> {
        private CalculateServiceBlockingStub(Channel channel) {
            super(channel);
        }

        private CalculateServiceBlockingStub(Channel channel, CallOptions callOptions) {
            super(channel, callOptions);
        }

        protected CalculateServiceGrpc.CalculateServiceBlockingStub build(Channel channel, CallOptions callOptions) {
            return new CalculateServiceGrpc.CalculateServiceBlockingStub(channel, callOptions);
        }
    }

    public static final class CalculateServiceStub extends AbstractStub<CalculateServiceGrpc.CalculateServiceStub> {
        private CalculateServiceStub(Channel channel) {
            super(channel);
        }

        private CalculateServiceStub(Channel channel, CallOptions callOptions) {
            super(channel, callOptions);
        }

        protected CalculateServiceGrpc.CalculateServiceStub build(Channel channel, CallOptions callOptions) {
            return new CalculateServiceGrpc.CalculateServiceStub(channel, callOptions);
        }

        public StreamObserver<Value> getResult(StreamObserver<Result> responseObserver) {
            return ClientCalls.asyncBidiStreamingCall(this.getChannel().newCall(CalculateServiceGrpc.getGetResultMethodHelper(), this.getCallOptions()), responseObserver);
        }
    }

    public abstract static class CalculateServiceImplBase implements BindableService {
        public CalculateServiceImplBase() {
        }

        public StreamObserver<Value> getResult(StreamObserver<Result> responseObserver) {
            return ServerCalls.asyncUnimplementedStreamingCall(CalculateServiceGrpc.getGetResultMethodHelper(), responseObserver);
        }

        public final ServerServiceDefinition bindService() {
            return ServerServiceDefinition.builder(CalculateServiceGrpc.getServiceDescriptor()).addMethod(CalculateServiceGrpc.getGetResultMethodHelper(), ServerCalls.asyncBidiStreamingCall(new CalculateServiceGrpc.MethodHandlers(this, 0))).build();
        }
    }
}

CalculateProto.java

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package io.grpc.examples.calculate;

import com.google.protobuf.AbstractParser;
import com.google.protobuf.ByteString;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.CodedOutputStream;
import com.google.protobuf.ExtensionRegistry;
import com.google.protobuf.ExtensionRegistryLite;
import com.google.protobuf.GeneratedMessageV3;
import com.google.protobuf.Internal;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import com.google.protobuf.MessageOrBuilder;
import com.google.protobuf.Parser;
import com.google.protobuf.UnknownFieldSet;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.Descriptors.OneofDescriptor;
import com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner;
import com.google.protobuf.GeneratedMessageV3.BuilderParent;
import com.google.protobuf.GeneratedMessageV3.FieldAccessorTable;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;

public final class CalculateProto {
    private static final Descriptor internal_static_calculate_Value_descriptor;
    private static final FieldAccessorTable internal_static_calculate_Value_fieldAccessorTable;
    private static final Descriptor internal_static_calculate_Result_descriptor;
    private static final FieldAccessorTable internal_static_calculate_Result_fieldAccessorTable;
    private static FileDescriptor descriptor;

    private CalculateProto() {
    }

    public static void registerAllExtensions(ExtensionRegistryLite registry) {
    }

    public static void registerAllExtensions(ExtensionRegistry registry) {
        registerAllExtensions((ExtensionRegistryLite)registry);
    }

    public static FileDescriptor getDescriptor() {
        return descriptor;
    }

    static {
        String[] descriptorData = new String[]{"\n\u0016calculateService.proto\u0012\tcalculate\"\u0016\n\u0005Value\u0012\r\n\u0005value\u0018\u0001 \u0001(\u0005\"/\n\u0006Result\u0012\u000b\n\u0003sum\u0018\u0001 \u0001(\u0005\u0012\u000b\n\u0003cnt\u0018\u0002 \u0001(\u0005\u0012\u000b\n\u0003avg\u0018\u0003 \u0001(\u00012J\n\u0010CalculateService\u00126\n\tgetResult\u0012\u0010.calculate.Value\u001a\u0011.calculate.Result\"\u0000(\u00010\u0001B.\n\u001aio.grpc.examples.calculateB\u000eCalculateProtoP\u0000b\u0006proto3"};
        InternalDescriptorAssigner assigner = new InternalDescriptorAssigner() {
            public ExtensionRegistry assignDescriptors(FileDescriptor root) {
                CalculateProto.descriptor = root;
                return null;
            }
        };
        FileDescriptor.internalBuildGeneratedFileFrom(descriptorData, new FileDescriptor[0], assigner);
        internal_static_calculate_Value_descriptor = (Descriptor)getDescriptor().getMessageTypes().get(0);
        internal_static_calculate_Value_fieldAccessorTable = new FieldAccessorTable(internal_static_calculate_Value_descriptor, new String[]{"Value"});
        internal_static_calculate_Result_descriptor = (Descriptor)getDescriptor().getMessageTypes().get(1);
        internal_static_calculate_Result_fieldAccessorTable = new FieldAccessorTable(internal_static_calculate_Result_descriptor, new String[]{"Sum", "Cnt", "Avg"});
    }

    public static final class Result extends GeneratedMessageV3 implements CalculateProto.ResultOrBuilder {
        private static final long serialVersionUID = 0L;
        public static final int SUM_FIELD_NUMBER = 1;
        private int sum_;
        public static final int CNT_FIELD_NUMBER = 2;
        private int cnt_;
        public static final int AVG_FIELD_NUMBER = 3;
        private double avg_;
        private byte memoizedIsInitialized;
        private static final CalculateProto.Result DEFAULT_INSTANCE = new CalculateProto.Result();
        private static final Parser<CalculateProto.Result> PARSER = new AbstractParser<CalculateProto.Result>() {
            public CalculateProto.Result parsePartialFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException {
                return new CalculateProto.Result(input, extensionRegistry, null);
            }
        };

        private Result(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
            super(builder);
            this.memoizedIsInitialized = -1;
        }

        private Result() {
            this.memoizedIsInitialized = -1;
            this.sum_ = 0;
            this.cnt_ = 0;
            this.avg_ = 0.0D;
        }

        public final UnknownFieldSet getUnknownFields() {
            return this.unknownFields;
        }

        private Result(CodedInputStream input, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException {
            this();
            if (extensionRegistry == null) {
                throw new NullPointerException();
            } else {
                int mutable_bitField0_ = false;
                com.google.protobuf.UnknownFieldSet.Builder unknownFields = UnknownFieldSet.newBuilder();

                try {
                    boolean done = false;

                    while(!done) {
                        int tag = input.readTag();
                        switch(tag) {
                        case 0:
                            done = true;
                            break;
                        case 8:
                            this.sum_ = input.readInt32();
                            break;
                        case 16:
                            this.cnt_ = input.readInt32();
                            break;
                        case 25:
                            this.avg_ = input.readDouble();
                            break;
                        default:
                            if (!this.parseUnknownFieldProto3(input, unknownFields, extensionRegistry, tag)) {
                                done = true;
                            }
                        }
                    }
                } catch (InvalidProtocolBufferException var11) {
                    throw var11.setUnfinishedMessage(this);
                } catch (IOException var12) {
                    throw (new InvalidProtocolBufferException(var12)).setUnfinishedMessage(this);
                } finally {
                    this.unknownFields = unknownFields.build();
                    this.makeExtensionsImmutable();
                }

            }
        }

        public static final Descriptor getDescriptor() {
            return CalculateProto.internal_static_calculate_Result_descriptor;
        }

        protected FieldAccessorTable internalGetFieldAccessorTable() {
            return CalculateProto.internal_static_calculate_Result_fieldAccessorTable.ensureFieldAccessorsInitialized(CalculateProto.Result.class, CalculateProto.Result.Builder.class);
        }

        public int getSum() {
            return this.sum_;
        }

        public int getCnt() {
            return this.cnt_;
        }

        public double getAvg() {
            return this.avg_;
        }

        public final boolean isInitialized() {
            byte isInitialized = this.memoizedIsInitialized;
            if (isInitialized == 1) {
                return true;
            } else if (isInitialized == 0) {
                return false;
            } else {
                this.memoizedIsInitialized = 1;
                return true;
            }
        }

        public void writeTo(CodedOutputStream output) throws IOException {
            if (this.sum_ != 0) {
                output.writeInt32(1, this.sum_);
            }

            if (this.cnt_ != 0) {
                output.writeInt32(2, this.cnt_);
            }

            if (this.avg_ != 0.0D) {
                output.writeDouble(3, this.avg_);
            }

            this.unknownFields.writeTo(output);
        }

        public int getSerializedSize() {
            int size = this.memoizedSize;
            if (size != -1) {
                return size;
            } else {
                size = 0;
                if (this.sum_ != 0) {
                    size += CodedOutputStream.computeInt32Size(1, this.sum_);
                }

                if (this.cnt_ != 0) {
                    size += CodedOutputStream.computeInt32Size(2, this.cnt_);
                }

                if (this.avg_ != 0.0D) {
                    size += CodedOutputStream.computeDoubleSize(3, this.avg_);
                }

                size += this.unknownFields.getSerializedSize();
                this.memoizedSize = size;
                return size;
            }
        }

        public boolean equals(Object obj) {
            if (obj == this) {
                return true;
            } else if (!(obj instanceof CalculateProto.Result)) {
                return super.equals(obj);
            } else {
                CalculateProto.Result other = (CalculateProto.Result)obj;
                boolean result = true;
                result = result && this.getSum() == other.getSum();
                result = result && this.getCnt() == other.getCnt();
                result = result && Double.doubleToLongBits(this.getAvg()) == Double.doubleToLongBits(other.getAvg());
                result = result && this.unknownFields.equals(other.unknownFields);
                return result;
            }
        }

        public int hashCode() {
            if (this.memoizedHashCode != 0) {
                return this.memoizedHashCode;
            } else {
                int hash = 41;
                int hash = 19 * hash + getDescriptor().hashCode();
                hash = 37 * hash + 1;
                hash = 53 * hash + this.getSum();
                hash = 37 * hash + 2;
                hash = 53 * hash + this.getCnt();
                hash = 37 * hash + 3;
                hash = 53 * hash + Internal.hashLong(Double.doubleToLongBits(this.getAvg()));
                hash = 29 * hash + this.unknownFields.hashCode();
                this.memoizedHashCode = hash;
                return hash;
            }
        }

        public static CalculateProto.Result parseFrom(ByteBuffer data) throws InvalidProtocolBufferException {
            return (CalculateProto.Result)PARSER.parseFrom(data);
        }

        public static CalculateProto.Result parseFrom(ByteBuffer data, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException {
            return (CalculateProto.Result)PARSER.parseFrom(data, extensionRegistry);
        }

        public static CalculateProto.Result parseFrom(ByteString data) throws InvalidProtocolBufferException {
            return (CalculateProto.Result)PARSER.parseFrom(data);
        }

        public static CalculateProto.Result parseFrom(ByteString data, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException {
            return (CalculateProto.Result)PARSER.parseFrom(data, extensionRegistry);
        }

        public static CalculateProto.Result parseFrom(byte[] data) throws InvalidProtocolBufferException {
            return (CalculateProto.Result)PARSER.parseFrom(data);
        }

        public static CalculateProto.Result parseFrom(byte[] data, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException {
            return (CalculateProto.Result)PARSER.parseFrom(data, extensionRegistry);
        }

        public static CalculateProto.Result parseFrom(InputStream input) throws IOException {
            return (CalculateProto.Result)GeneratedMessageV3.parseWithIOException(PARSER, input);
        }

        public static CalculateProto.Result parseFrom(InputStream input, ExtensionRegistryLite extensionRegistry) throws IOException {
            return (CalculateProto.Result)GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry);
        }

        public static CalculateProto.Result parseDelimitedFrom(InputStream input) throws IOException {
            return (CalculateProto.Result)GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input);
        }

        public static CalculateProto.Result parseDelimitedFrom(InputStream input, ExtensionRegistryLite extensionRegistry) throws IOException {
            return (CalculateProto.Result)GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input, extensionRegistry);
        }

        public static CalculateProto.Result parseFrom(CodedInputStream input) throws IOException {
            return (CalculateProto.Result)GeneratedMessageV3.parseWithIOException(PARSER, input);
        }

        public static CalculateProto.Result parseFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry) throws IOException {
            return (CalculateProto.Result)GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry);
        }

        public CalculateProto.Result.Builder newBuilderForType() {
            return newBuilder();
        }

        public static CalculateProto.Result.Builder newBuilder() {
            return DEFAULT_INSTANCE.toBuilder();
        }

        public static CalculateProto.Result.Builder newBuilder(CalculateProto.Result prototype) {
            return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
        }

        public CalculateProto.Result.Builder toBuilder() {
            return this == DEFAULT_INSTANCE ? new CalculateProto.Result.Builder(null) : (new CalculateProto.Result.Builder(null)).mergeFrom(this);
        }

        protected CalculateProto.Result.Builder newBuilderForType(BuilderParent parent) {
            CalculateProto.Result.Builder builder = new CalculateProto.Result.Builder(parent, null);
            return builder;
        }

        public static CalculateProto.Result getDefaultInstance() {
            return DEFAULT_INSTANCE;
        }

        public static Parser<CalculateProto.Result> parser() {
            return PARSER;
        }

        public Parser<CalculateProto.Result> getParserForType() {
            return PARSER;
        }

        public CalculateProto.Result getDefaultInstanceForType() {
            return DEFAULT_INSTANCE;
        }

        public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder<CalculateProto.Result.Builder> implements CalculateProto.ResultOrBuilder {
            private int sum_;
            private int cnt_;
            private double avg_;

            public static final Descriptor getDescriptor() {
                return CalculateProto.internal_static_calculate_Result_descriptor;
            }

            protected FieldAccessorTable internalGetFieldAccessorTable() {
                return CalculateProto.internal_static_calculate_Result_fieldAccessorTable.ensureFieldAccessorsInitialized(CalculateProto.Result.class, CalculateProto.Result.Builder.class);
            }

            private Builder() {
                this.maybeForceBuilderInitialization();
            }

            private Builder(BuilderParent parent) {
                super(parent);
                this.maybeForceBuilderInitialization();
            }

            private void maybeForceBuilderInitialization() {
                if (CalculateProto.Result.alwaysUseFieldBuilders) {
                    ;
                }

            }

            public CalculateProto.Result.Builder clear() {
                super.clear();
                this.sum_ = 0;
                this.cnt_ = 0;
                this.avg_ = 0.0D;
                return this;
            }

            public Descriptor getDescriptorForType() {
                return CalculateProto.internal_static_calculate_Result_descriptor;
            }

            public CalculateProto.Result getDefaultInstanceForType() {
                return CalculateProto.Result.DEFAULT_INSTANCE;
            }

            public CalculateProto.Result build() {
                CalculateProto.Result result = this.buildPartial();
                if (!result.isInitialized()) {
                    throw newUninitializedMessageException(result);
                } else {
                    return result;
                }
            }

            public CalculateProto.Result buildPartial() {
                CalculateProto.Result result = new CalculateProto.Result(this, null);
                result.sum_ = this.sum_;
                result.cnt_ = this.cnt_;
                result.avg_ = this.avg_;
                this.onBuilt();
                return result;
            }

            public CalculateProto.Result.Builder clone() {
                return (CalculateProto.Result.Builder)super.clone();
            }

            public CalculateProto.Result.Builder setField(FieldDescriptor field, Object value) {
                return (CalculateProto.Result.Builder)super.setField(field, value);
            }

            public CalculateProto.Result.Builder clearField(FieldDescriptor field) {
                return (CalculateProto.Result.Builder)super.clearField(field);
            }

            public CalculateProto.Result.Builder clearOneof(OneofDescriptor oneof) {
                return (CalculateProto.Result.Builder)super.clearOneof(oneof);
            }

            public CalculateProto.Result.Builder setRepeatedField(FieldDescriptor field, int index, Object value) {
                return (CalculateProto.Result.Builder)super.setRepeatedField(field, index, value);
            }

            public CalculateProto.Result.Builder addRepeatedField(FieldDescriptor field, Object value) {
                return (CalculateProto.Result.Builder)super.addRepeatedField(field, value);
            }

            public CalculateProto.Result.Builder mergeFrom(Message other) {
                if (other instanceof CalculateProto.Result) {
                    return this.mergeFrom((CalculateProto.Result)other);
                } else {
                    super.mergeFrom(other);
                    return this;
                }
            }

            public CalculateProto.Result.Builder mergeFrom(CalculateProto.Result other) {
                if (other == CalculateProto.Result.DEFAULT_INSTANCE) {
                    return this;
                } else {
                    if (other.getSum() != 0) {
                        this.setSum(other.getSum());
                    }

                    if (other.getCnt() != 0) {
                        this.setCnt(other.getCnt());
                    }

                    if (other.getAvg() != 0.0D) {
                        this.setAvg(other.getAvg());
                    }

                    this.mergeUnknownFields(other.unknownFields);
                    this.onChanged();
                    return this;
                }
            }

            public final boolean isInitialized() {
                return true;
            }

            public CalculateProto.Result.Builder mergeFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry) throws IOException {
                CalculateProto.Result parsedMessage = null;

                try {
                    parsedMessage = (CalculateProto.Result)CalculateProto.Result.PARSER.parsePartialFrom(input, extensionRegistry);
                } catch (InvalidProtocolBufferException var8) {
                    parsedMessage = (CalculateProto.Result)var8.getUnfinishedMessage();
                    throw var8.unwrapIOException();
                } finally {
                    if (parsedMessage != null) {
                        this.mergeFrom(parsedMessage);
                    }

                }

                return this;
            }

            public int getSum() {
                return this.sum_;
            }

            public CalculateProto.Result.Builder setSum(int value) {
                this.sum_ = value;
                this.onChanged();
                return this;
            }

            public CalculateProto.Result.Builder clearSum() {
                this.sum_ = 0;
                this.onChanged();
                return this;
            }

            public int getCnt() {
                return this.cnt_;
            }

            public CalculateProto.Result.Builder setCnt(int value) {
                this.cnt_ = value;
                this.onChanged();
                return this;
            }

            public CalculateProto.Result.Builder clearCnt() {
                this.cnt_ = 0;
                this.onChanged();
                return this;
            }

            public double getAvg() {
                return this.avg_;
            }

            public CalculateProto.Result.Builder setAvg(double value) {
                this.avg_ = value;
                this.onChanged();
                return this;
            }

            public CalculateProto.Result.Builder clearAvg() {
                this.avg_ = 0.0D;
                this.onChanged();
                return this;
            }

            public final CalculateProto.Result.Builder setUnknownFields(UnknownFieldSet unknownFields) {
                return (CalculateProto.Result.Builder)super.setUnknownFieldsProto3(unknownFields);
            }

            public final CalculateProto.Result.Builder mergeUnknownFields(UnknownFieldSet unknownFields) {
                return (CalculateProto.Result.Builder)super.mergeUnknownFields(unknownFields);
            }
        }
    }

    public interface ResultOrBuilder extends MessageOrBuilder {
        int getSum();

        int getCnt();

        double getAvg();
    }

    public static final class Value extends GeneratedMessageV3 implements CalculateProto.ValueOrBuilder {
        private static final long serialVersionUID = 0L;
        public static final int VALUE_FIELD_NUMBER = 1;
        private int value_;
        private byte memoizedIsInitialized;
        private static final CalculateProto.Value DEFAULT_INSTANCE = new CalculateProto.Value();
        private static final Parser<CalculateProto.Value> PARSER = new AbstractParser<CalculateProto.Value>() {
            public CalculateProto.Value parsePartialFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException {
                return new CalculateProto.Value(input, extensionRegistry, null);
            }
        };

        private Value(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
            super(builder);
            this.memoizedIsInitialized = -1;
        }

        private Value() {
            this.memoizedIsInitialized = -1;
            this.value_ = 0;
        }

        public final UnknownFieldSet getUnknownFields() {
            return this.unknownFields;
        }

        private Value(CodedInputStream input, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException {
            this();
            if (extensionRegistry == null) {
                throw new NullPointerException();
            } else {
                int mutable_bitField0_ = false;
                com.google.protobuf.UnknownFieldSet.Builder unknownFields = UnknownFieldSet.newBuilder();

                try {
                    boolean done = false;

                    while(!done) {
                        int tag = input.readTag();
                        switch(tag) {
                        case 0:
                            done = true;
                            break;
                        case 8:
                            this.value_ = input.readInt32();
                            break;
                        default:
                            if (!this.parseUnknownFieldProto3(input, unknownFields, extensionRegistry, tag)) {
                                done = true;
                            }
                        }
                    }
                } catch (InvalidProtocolBufferException var11) {
                    throw var11.setUnfinishedMessage(this);
                } catch (IOException var12) {
                    throw (new InvalidProtocolBufferException(var12)).setUnfinishedMessage(this);
                } finally {
                    this.unknownFields = unknownFields.build();
                    this.makeExtensionsImmutable();
                }

            }
        }

        public static final Descriptor getDescriptor() {
            return CalculateProto.internal_static_calculate_Value_descriptor;
        }

        protected FieldAccessorTable internalGetFieldAccessorTable() {
            return CalculateProto.internal_static_calculate_Value_fieldAccessorTable.ensureFieldAccessorsInitialized(CalculateProto.Value.class, CalculateProto.Value.Builder.class);
        }

        public int getValue() {
            return this.value_;
        }

        public final boolean isInitialized() {
            byte isInitialized = this.memoizedIsInitialized;
            if (isInitialized == 1) {
                return true;
            } else if (isInitialized == 0) {
                return false;
            } else {
                this.memoizedIsInitialized = 1;
                return true;
            }
        }

        public void writeTo(CodedOutputStream output) throws IOException {
            if (this.value_ != 0) {
                output.writeInt32(1, this.value_);
            }

            this.unknownFields.writeTo(output);
        }

        public int getSerializedSize() {
            int size = this.memoizedSize;
            if (size != -1) {
                return size;
            } else {
                size = 0;
                if (this.value_ != 0) {
                    size += CodedOutputStream.computeInt32Size(1, this.value_);
                }

                size += this.unknownFields.getSerializedSize();
                this.memoizedSize = size;
                return size;
            }
        }

        public boolean equals(Object obj) {
            if (obj == this) {
                return true;
            } else if (!(obj instanceof CalculateProto.Value)) {
                return super.equals(obj);
            } else {
                CalculateProto.Value other = (CalculateProto.Value)obj;
                boolean result = true;
                result = result && this.getValue() == other.getValue();
                result = result && this.unknownFields.equals(other.unknownFields);
                return result;
            }
        }

        public int hashCode() {
            if (this.memoizedHashCode != 0) {
                return this.memoizedHashCode;
            } else {
                int hash = 41;
                int hash = 19 * hash + getDescriptor().hashCode();
                hash = 37 * hash + 1;
                hash = 53 * hash + this.getValue();
                hash = 29 * hash + this.unknownFields.hashCode();
                this.memoizedHashCode = hash;
                return hash;
            }
        }

        public static CalculateProto.Value parseFrom(ByteBuffer data) throws InvalidProtocolBufferException {
            return (CalculateProto.Value)PARSER.parseFrom(data);
        }

        public static CalculateProto.Value parseFrom(ByteBuffer data, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException {
            return (CalculateProto.Value)PARSER.parseFrom(data, extensionRegistry);
        }

        public static CalculateProto.Value parseFrom(ByteString data) throws InvalidProtocolBufferException {
            return (CalculateProto.Value)PARSER.parseFrom(data);
        }

        public static CalculateProto.Value parseFrom(ByteString data, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException {
            return (CalculateProto.Value)PARSER.parseFrom(data, extensionRegistry);
        }

        public static CalculateProto.Value parseFrom(byte[] data) throws InvalidProtocolBufferException {
            return (CalculateProto.Value)PARSER.parseFrom(data);
        }

        public static CalculateProto.Value parseFrom(byte[] data, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException {
            return (CalculateProto.Value)PARSER.parseFrom(data, extensionRegistry);
        }

        public static CalculateProto.Value parseFrom(InputStream input) throws IOException {
            return (CalculateProto.Value)GeneratedMessageV3.parseWithIOException(PARSER, input);
        }

        public static CalculateProto.Value parseFrom(InputStream input, ExtensionRegistryLite extensionRegistry) throws IOException {
            return (CalculateProto.Value)GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry);
        }

        public static CalculateProto.Value parseDelimitedFrom(InputStream input) throws IOException {
            return (CalculateProto.Value)GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input);
        }

        public static CalculateProto.Value parseDelimitedFrom(InputStream input, ExtensionRegistryLite extensionRegistry) throws IOException {
            return (CalculateProto.Value)GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input, extensionRegistry);
        }

        public static CalculateProto.Value parseFrom(CodedInputStream input) throws IOException {
            return (CalculateProto.Value)GeneratedMessageV3.parseWithIOException(PARSER, input);
        }

        public static CalculateProto.Value parseFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry) throws IOException {
            return (CalculateProto.Value)GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry);
        }

        public CalculateProto.Value.Builder newBuilderForType() {
            return newBuilder();
        }

        public static CalculateProto.Value.Builder newBuilder() {
            return DEFAULT_INSTANCE.toBuilder();
        }

        public static CalculateProto.Value.Builder newBuilder(CalculateProto.Value prototype) {
            return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
        }

        public CalculateProto.Value.Builder toBuilder() {
            return this == DEFAULT_INSTANCE ? new CalculateProto.Value.Builder(null) : (new CalculateProto.Value.Builder(null)).mergeFrom(this);
        }

        protected CalculateProto.Value.Builder newBuilderForType(BuilderParent parent) {
            CalculateProto.Value.Builder builder = new CalculateProto.Value.Builder(parent, null);
            return builder;
        }

        public static CalculateProto.Value getDefaultInstance() {
            return DEFAULT_INSTANCE;
        }

        public static Parser<CalculateProto.Value> parser() {
            return PARSER;
        }

        public Parser<CalculateProto.Value> getParserForType() {
            return PARSER;
        }

        public CalculateProto.Value getDefaultInstanceForType() {
            return DEFAULT_INSTANCE;
        }

        public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder<CalculateProto.Value.Builder> implements CalculateProto.ValueOrBuilder {
            private int value_;

            public static final Descriptor getDescriptor() {
                return CalculateProto.internal_static_calculate_Value_descriptor;
            }

            protected FieldAccessorTable internalGetFieldAccessorTable() {
                return CalculateProto.internal_static_calculate_Value_fieldAccessorTable.ensureFieldAccessorsInitialized(CalculateProto.Value.class, CalculateProto.Value.Builder.class);
            }

            private Builder() {
                this.maybeForceBuilderInitialization();
            }

            private Builder(BuilderParent parent) {
                super(parent);
                this.maybeForceBuilderInitialization();
            }

            private void maybeForceBuilderInitialization() {
                if (CalculateProto.Value.alwaysUseFieldBuilders) {
                    ;
                }

            }

            public CalculateProto.Value.Builder clear() {
                super.clear();
                this.value_ = 0;
                return this;
            }

            public Descriptor getDescriptorForType() {
                return CalculateProto.internal_static_calculate_Value_descriptor;
            }

            public CalculateProto.Value getDefaultInstanceForType() {
                return CalculateProto.Value.DEFAULT_INSTANCE;
            }

            public CalculateProto.Value build() {
                CalculateProto.Value result = this.buildPartial();
                if (!result.isInitialized()) {
                    throw newUninitializedMessageException(result);
                } else {
                    return result;
                }
            }

            public CalculateProto.Value buildPartial() {
                CalculateProto.Value result = new CalculateProto.Value(this, null);
                result.value_ = this.value_;
                this.onBuilt();
                return result;
            }

            public CalculateProto.Value.Builder clone() {
                return (CalculateProto.Value.Builder)super.clone();
            }

            public CalculateProto.Value.Builder setField(FieldDescriptor field, Object value) {
                return (CalculateProto.Value.Builder)super.setField(field, value);
            }

            public CalculateProto.Value.Builder clearField(FieldDescriptor field) {
                return (CalculateProto.Value.Builder)super.clearField(field);
            }

            public CalculateProto.Value.Builder clearOneof(OneofDescriptor oneof) {
                return (CalculateProto.Value.Builder)super.clearOneof(oneof);
            }

            public CalculateProto.Value.Builder setRepeatedField(FieldDescriptor field, int index, Object value) {
                return (CalculateProto.Value.Builder)super.setRepeatedField(field, index, value);
            }

            public CalculateProto.Value.Builder addRepeatedField(FieldDescriptor field, Object value) {
                return (CalculateProto.Value.Builder)super.addRepeatedField(field, value);
            }

            public CalculateProto.Value.Builder mergeFrom(Message other) {
                if (other instanceof CalculateProto.Value) {
                    return this.mergeFrom((CalculateProto.Value)other);
                } else {
                    super.mergeFrom(other);
                    return this;
                }
            }

            public CalculateProto.Value.Builder mergeFrom(CalculateProto.Value other) {
                if (other == CalculateProto.Value.DEFAULT_INSTANCE) {
                    return this;
                } else {
                    if (other.getValue() != 0) {
                        this.setValue(other.getValue());
                    }

                    this.mergeUnknownFields(other.unknownFields);
                    this.onChanged();
                    return this;
                }
            }

            public final boolean isInitialized() {
                return true;
            }

            public CalculateProto.Value.Builder mergeFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry) throws IOException {
                CalculateProto.Value parsedMessage = null;

                try {
                    parsedMessage = (CalculateProto.Value)CalculateProto.Value.PARSER.parsePartialFrom(input, extensionRegistry);
                } catch (InvalidProtocolBufferException var8) {
                    parsedMessage = (CalculateProto.Value)var8.getUnfinishedMessage();
                    throw var8.unwrapIOException();
                } finally {
                    if (parsedMessage != null) {
                        this.mergeFrom(parsedMessage);
                    }

                }

                return this;
            }

            public int getValue() {
                return this.value_;
            }

            public CalculateProto.Value.Builder setValue(int value) {
                this.value_ = value;
                this.onChanged();
                return this;
            }

            public CalculateProto.Value.Builder clearValue() {
                this.value_ = 0;
                this.onChanged();
                return this;
            }

            public final CalculateProto.Value.Builder setUnknownFields(UnknownFieldSet unknownFields) {
                return (CalculateProto.Value.Builder)super.setUnknownFieldsProto3(unknownFields);
            }

            public final CalculateProto.Value.Builder mergeUnknownFields(UnknownFieldSet unknownFields) {
                return (CalculateProto.Value.Builder)super.mergeUnknownFields(unknownFields);
            }
        }
    }

    public interface ValueOrBuilder extends MessageOrBuilder {
        int getValue();
    }
}

其中CalculateServiceGrpc中包含了服务端需要实现的具体服务的抽象类,以及客户端可以得到的若干种存根。而CalculateProto中则包括了我们定义的一些数据类型,比如客户端的消息类,以及服务端的返回结果类。

创建服务端

服务端还是分为两个部分,一个部分用于创建一个server实例,并接收客户端的请求,另外一个部分则是具体实现数据统计功能的。创建server实例的代码几乎和客户端流式调用一模一样,不再细说:

package com.sunny;

import io.grpc.Server;
import io.grpc.ServerBuilder;

import java.io.IOException;
import java.util.logging.Logger;

/**
 * @Author zsunny
 * @Date 2018/7/15 16:28
 * @Mail zsunny@yeah.net
 */
public class CalculatorServer {

    private static final Logger logger = Logger.getLogger(CalculatorServer.class.getName());

    private static final int DEFAULT_PORT = 8088;

    private int port;//服务端口号

    private Server server;

    public CalculatorServer(int port) {
        this(port, ServerBuilder.forPort(port));
    }

    public CalculatorServer(int port, ServerBuilder<?> serverBuilder) {
        this.port = port;

        //构造服务器,添加我们实际的服务
        server = serverBuilder.addService(new CalculateServiceImpl()).build();
    }

    private void start() throws IOException {
        server.start();
        logger.info("Server has started, listening on " + port);
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {

                CalculatorServer.this.stop();

            }
        });

    }

    private void stop() {

        if(server != null)
            server.shutdown();

    }

    //阻塞到应用停止
    private void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
            server.awaitTermination();
        }
    }

    public static void main(String[] args) {

        CalculatorServer addtionServer;

        if(args.length > 0){
            addtionServer = new CalculatorServer(Integer.parseInt(args[0]));
        }else{
            addtionServer = new CalculatorServer(DEFAULT_PORT);
        }

        try {
            addtionServer.start();
            addtionServer.blockUntilShutdown();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

实现数据的代码与之前客户端流式的例子略有区别,主要体现在我们返回的requestObserver中。

package com.sunny;

import io.grpc.examples.calculate.CalculateProto;
import io.grpc.examples.calculate.CalculateServiceGrpc.CalculateServiceImplBase;
import io.grpc.stub.StreamObserver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @Author zsunny
 * @Date 2018/7/15 16:28
 * @Mail zsunny@yeah.net
 */
public class CalculateServiceImpl extends CalculateServiceImplBase{

    private Logger log = LoggerFactory.getLogger(CalculateServiceImpl.class);

    @Override
    public StreamObserver<CalculateProto.Value> getResult(StreamObserver<CalculateProto.Result> responseObserver) {
        return new StreamObserver<CalculateProto.Value>() {

            private int sum = 0;
            private int cnt = 0;
            private double avg;

            public void onNext(CalculateProto.Value value) {
                log.info("接收到消息为:{}",value.getValue());
                sum += value.getValue();
                cnt++;
                avg = 1.0*sum/cnt;
                //返回当前统计结果
                CalculateProto.Result response = CalculateProto.Result.newBuilder().setSum(sum).setCnt(cnt).setAvg(avg).build();
                log.info("返回消息为:{}",response);
                responseObserver.onNext(response);
            }

            public void onError(Throwable throwable) {
                log.warn("调用出错:{}",throwable.getMessage());
            }

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

这里,我们在onNext的方法中直接对现有数据进行统计,并利用responseObserver的回调方法onNext对结果进行返回,而不是像客户端流式调用那样,在onCompleted方法中调用responseObserver的回调方法onNext来返回结果。这样的话,就可以做到客户端过来一个请求,服务端对该请求直接进行回复,不必等待客户端调用完毕。

创建客户端

客户端的部分则和客户端流式调用没有太大的区别,只不过,这里Sunny没有选择单独写一个回调进行处理,而是简单地用log来打印数据信息。同样,我们还是生成若干随机整形数据,依次发给服务端,并从服务端得到结果打印。

package com.sunny;


import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.examples.calculate.CalculateProto;
import io.grpc.examples.calculate.CalculateServiceGrpc;
import io.grpc.stub.StreamObserver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;

/**
 * @Author zsunny
 * @Date 2018/7/15 16:28
 * @Mail zsunny@yeah.net
 */
public class CalculatorClient {

    private static final String DEFAULT_HOST = "localhost";

    private static final int DEFAULT_PORT = 8088;

    private static final int VALUE_NUM = 10;

    private static final int VALUE_UPPER_BOUND = 10;

    private static final Logger log = LoggerFactory.getLogger(CalculatorClient.class);

    //这里用异步请求存根
    private CalculateServiceGrpc.CalculateServiceStub calculateServiceStub;

    public CalculatorClient(String host, int port) {

        //使用明文通讯,这里简单化,实际生产环境需要通讯加密
        this(ManagedChannelBuilder.forAddress(host,port).usePlaintext(true).build());

    }

    public CalculatorClient(ManagedChannel managedChannel) {
        this.calculateServiceStub = CalculateServiceGrpc.newStub(managedChannel);
    }

    /**
     * 实际调用部分
     * @param nums 传到服务端的数据流
     */
    public void getResult( List<Integer> nums){

        //判断调用状态。在内部类中被访问,需要加final修饰
        final CountDownLatch countDownLatch = new CountDownLatch(1);

        StreamObserver<CalculateProto.Result> responseObserver = new StreamObserver<CalculateProto.Result>() {
            private int cnt = 0;
            public void onNext(CalculateProto.Result result) {
                //此处直接打印结果,其他也可用回调进行复杂处理
                log.info("第{}次调用得到结果为:{}",++cnt,result);
            }

            public void onError(Throwable throwable) {
                log.warn("调用出错:{}",throwable.getMessage());
                countDownLatch.countDown();
            }

            public void onCompleted() {
                log.info("调用完成");
                countDownLatch.countDown();
            }

        };

        StreamObserver<CalculateProto.Value> requestObserver = calculateServiceStub.getResult(responseObserver);

        for(int num: nums){
            CalculateProto.Value value = CalculateProto.Value.newBuilder().setValue(num).build();
            requestObserver.onNext(value);

            //判断调用结束状态。如果整个调用已经结束,继续发送数据不会报错,但是会被舍弃
            if(countDownLatch.getCount() == 0){
                return;
            }
        }
        //异步请求,无法确保onNext与onComplete的完成先后顺序
        requestObserver.onCompleted();

        try {
            //如果在规定时间内没有请求完,则让程序停止
            if(!countDownLatch.await(5, TimeUnit.MINUTES)){
                log.warn("未在规定时间内完成调用");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }


    public static void main(String[] args) {

        CalculatorClient additionClient = new CalculatorClient(DEFAULT_HOST,DEFAULT_PORT);

        //生成value值
        List<Integer> list = new ArrayList<Integer>();
        Random random = new Random();

        for(int i=0; i<VALUE_NUM; i++){
            //随机数符合 0-VALUE_UPPER_BOUND 均匀分布
            int value = random.nextInt(VALUE_UPPER_BOUND);

//            System.out.println(i + ":" + value);

            list.add(value);
        }

        System.out.println("*************************getting result from server***************************");
        System.out.println();

        additionClient.getResult(list);

    }
}

运行实验

首先我们启动服务端,得到以下内容,成功启动了:

Jul 15, 2018 9:36:38 PM com.sunny.CalculatorServer start
信息: Server has started, listening on 8088

然后我们启动客户端,它会自动创建若干数据(默认10个),发送相应的请求,并打印得到的结果:

*************************getting result from server***************************

[grpc-default-executor-1] INFO com.sunny.CalculatorClient - 第1次调用得到结果为:sum: 7
cnt: 1
avg: 7.0

[grpc-default-executor-1] INFO com.sunny.CalculatorClient - 第2次调用得到结果为:sum: 8
cnt: 2
avg: 4.0

[grpc-default-executor-1] INFO com.sunny.CalculatorClient - 第3次调用得到结果为:sum: 12
cnt: 3
avg: 4.0

[grpc-default-executor-1] INFO com.sunny.CalculatorClient - 第4次调用得到结果为:sum: 12
cnt: 4
avg: 3.0

[grpc-default-executor-1] INFO com.sunny.CalculatorClient - 第5次调用得到结果为:sum: 19
cnt: 5
avg: 3.8

[grpc-default-executor-1] INFO com.sunny.CalculatorClient - 第6次调用得到结果为:sum: 27
cnt: 6
avg: 4.5

[grpc-default-executor-1] INFO com.sunny.CalculatorClient - 第7次调用得到结果为:sum: 30
cnt: 7
avg: 4.285714285714286

[grpc-default-executor-1] INFO com.sunny.CalculatorClient - 第8次调用得到结果为:sum: 36
cnt: 8
avg: 4.5

[grpc-default-executor-1] INFO com.sunny.CalculatorClient - 第9次调用得到结果为:sum: 43
cnt: 9
avg: 4.777777777777778

[grpc-default-executor-1] INFO com.sunny.CalculatorClient - 第10次调用得到结果为:sum: 48
cnt: 10
avg: 4.8

[grpc-default-executor-1] INFO com.sunny.CalculatorClient - 调用完成

Process finished with exit code 0

此时查看服务端的控制台,得到以下信息:

[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 接收到消息为:7
[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 返回消息为:sum: 7
cnt: 1
avg: 7.0

[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 接收到消息为:1
[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 返回消息为:sum: 8
cnt: 2
avg: 4.0

[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 接收到消息为:4
[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 返回消息为:sum: 12
cnt: 3
avg: 4.0

[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 接收到消息为:0
[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 返回消息为:sum: 12
cnt: 4
avg: 3.0

[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 接收到消息为:7
[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 返回消息为:sum: 19
cnt: 5
avg: 3.8

[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 接收到消息为:8
[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 返回消息为:sum: 27
cnt: 6
avg: 4.5

[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 接收到消息为:3
[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 返回消息为:sum: 30
cnt: 7
avg: 4.285714285714286

[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 接收到消息为:6
[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 返回消息为:sum: 36
cnt: 8
avg: 4.5

[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 接收到消息为:7
[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 返回消息为:sum: 43
cnt: 9
avg: 4.777777777777778

[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 接收到消息为:5
[grpc-default-executor-0] INFO com.sunny.CalculateServiceImpl - 返回消息为:sum: 48
cnt: 10
avg: 4.8

至此,本篇文章到此结束,喜欢的童鞋可以点个赞。
欢迎转载,转载时请注明原文地址:https://blog.csdn.net/m0_37595562/article/details/81638415
童鞋们如果有疑问或者想和我交流的话有两种方式:

第一种

评论留言

第二种

邮箱联系:zsunny@yeah.net

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我理解了你的问题。让我们来探讨一下如何使用Java gRPC实现双向流式调用构建一个简单数据分析。 首先,我们需要定义一个 gRPC 服务。假设我们要构建一个数据分析,这个服务可以接收一系列数据,并返回一系列数据分析结果。我们可以这样定义这个服务: ``` service DataAnalyzer { // 客户端发送数据流到服务端,并返回分析结果 rpc Analyze(stream Data) returns (stream Result) {} } // 定义数据和分析结果的消息类型 message Data { string content = 1; } message Result { string analysis = 1; } ``` 接下来,我们需要实现这个服务。我们可以创建一个 Java 类来实现这个服务: ``` public class DataAnalyzerImpl extends DataAnalyzerGrpc.DataAnalyzerImplBase { @Override public StreamObserver<Data> analyze(StreamObserver<Result> responseObserver) { // 创建一个用于处理数据的 StreamObserver StreamObserver<Data> requestObserver = new StreamObserver<Data>() { @Override public void onNext(Data data) { // 处理数据并生成分析结果 Result result = Result.newBuilder() .setAnalysis("Analysis of " + data.getContent()) .build(); // 将分析结果发送给客户端 responseObserver.onNext(result); } @Override public void onError(Throwable t) { // 处理错误 } @Override public void onCompleted() { // 处理完成 responseObserver.onCompleted(); } }; return requestObserver; } } ``` 在这个实现中,我们实现了 `analyze` 方法,它接收一个 `StreamObserver`,并返回一个 `StreamObserver`。我们使用返回的 `StreamObserver` 处理客户端发送的数据。对于每个接收到的数据,我们生成一个分析结果,并将其发送给客户端。 现在我们已经实现了服务端,接下来让我们来实现客户端。我们可以创建一个 Java 类来实现客户端: ``` public class DataAnalyzerClient { public void analyzeData(List<String> data) { // 创建一个用于接收分析结果的 StreamObserver StreamObserver<Result> responseObserver = new StreamObserver<Result>() { @Override public void onNext(Result result) { // 处理分析结果 System.out.println(result.getAnalysis()); } @Override public void onError(Throwable t) { // 处理错误 } @Override public void onCompleted() { // 处理完成 } }; // 创建一个用于发送数据的 StreamObserver StreamObserver<Data> requestObserver = stub.analyze(responseObserver); // 发送数据并等待分析结果 for (String content : data) { Data data = Data.newBuilder() .setContent(content) .build(); requestObserver.onNext(data); } // 完成数据发送 requestObserver.onCompleted(); } } ``` 在这个实现中,我们首先创建两个 `StreamObserver`,一个用于接收分析结果,一个用于发送数据。然后,我们使用 `stub.analyze` 方法获取到一个 `StreamObserver`,并将其传递给用于发送数据的 `StreamObserver`。接着,我们循环发送数据,并等待分析结果。 现在,我们已经实现了一个简单数据分析。我们可以在服务端启动 gRPC 服务,并在客户端发送一些数据来测试这个分析

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值