grpc四种模式三个客户端以及自定义实现日志链路打印

417 阅读5分钟

模式:普通模式 客户端流 服务端流 双端流

客户端:普通阻塞客户端 完全异步客户端(出入参都是异步) 可阻塞可异步客户端(get,addListener)

客户端请求示例:

package com.aniu.inspection.api.controller;

import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat;
import com.google.protobuf.util.JsonFormat.Printer;
import com.aniu.inspection.grpc.*;
import io.grpc.stub.StreamObserver;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * @author pengyouy
 * @since 2022/7/4
 * 四种模式 三个客户端
 * 普通模式 客户端流 服务端流 双端流
 * 普通阻塞客户端 完全异步客户端(出入参都是异步) 可阻塞可异步客户端(get,addListener)
 */
@RestController
@RequestMapping("test")
@Slf4j
public class HelloController {

    private final Printer printer = JsonFormat.printer().omittingInsignificantWhitespace();

    /**
     * 普通阻塞客户端
     */
    @GrpcClient("inspection-service")
    private ModuleTestGrpc.ModuleTestBlockingStub moduleTestBlockingStub;

    /**
     * 完全的异步客户端
     */
    @GrpcClient("inspection-service")
    private ModuleTestGrpc.ModuleTestStub moduleTestStub;


    /**
     * 可以异步可以同步的客户端
     * get 阻塞Future
     * addListener 异步
     */
    @GrpcClient("inspection-service")
    private ModuleTestGrpc.ModuleTestFutureStub moduleTestFutureStub;


    /**
     * 双端流
     * 客户端多次发送
     * 服务端多次返回
     * 异步客户端+双端流模式
     *
     * @return
     * @throws InterruptedException
     */
    @GetMapping("doubleModule")
    public List doubleModule() throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        ArrayList<Object> objects = new ArrayList<>(16);
        StreamObserver<InspectionPlanListByMasterDTO> inspectionPlanListByMasterDTOStreamObserver = moduleTestStub.inspectionPlanListByMasterIdAndServerStream(new StreamObserver<InspectionPlanByMasterDTO>() {
            @SneakyThrows
            @Override
            public void onNext(InspectionPlanByMasterDTO value) {
                String print = printer.print(value);
                log.info("[收到服务端发来] : " + print);
                objects.add(print);
            }

            @Override
            public void onError(Throwable t) {
                log.error(t.getMessage(), t);
                countDownLatch.countDown();
            }

            @Override
            public void onCompleted() {
                log.debug("服务端 onCompleted");
                countDownLatch.countDown();
            }
        });
        for (Integer integer : Arrays.asList(1, 2)) {
            inspectionPlanListByMasterDTOStreamObserver.onNext(InspectionPlanListByMasterDTO.newBuilder().setMasterPlanId(integer + "").build());
        }
        inspectionPlanListByMasterDTOStreamObserver.onCompleted();
        countDownLatch.await();
        return objects;
    }


    /**
     * 客户端流
     * 客户端多次发送
     * 服务端一次返回
     * 异步客户端+客户端流模式
     * 阻塞http请求知道服务端返回
     *
     * @return
     * @throws InterruptedException
     */
    @GetMapping("clientModule")
    public List clientModule() throws InterruptedException {
        ArrayList<Object> objects = new ArrayList<>(16);
        CountDownLatch countDownLatch = new CountDownLatch(1);
        StreamObserver<InspectionPlanListByMasterDTO> inspectionPlanListByMasterDTOStreamObserver = moduleTestStub.inspectionPlanListByMasterIdAndOnlyClientStream(new StreamObserver<InspectionPlanListByMasterResponse>() {
            @SneakyThrows
            @Override
            public void onNext(InspectionPlanListByMasterResponse value) {
                log.info("[收到服务端发来] : " + value);
                objects.add(printer.print(value));
            }

            @SneakyThrows
            @Override
            public void onError(Throwable t) {
                log.error(t.getMessage(), t);
                countDownLatch.countDown();
            }

            @Override
            public void onCompleted() {
                log.info("服务端 onCompleted");
                countDownLatch.countDown();
            }
        });
        for (Integer integer : Arrays.asList(1, 2)) {
            inspectionPlanListByMasterDTOStreamObserver.onNext(InspectionPlanListByMasterDTO.newBuilder().setMasterPlanId(integer + "").build());
        }
        inspectionPlanListByMasterDTOStreamObserver.onCompleted();
        countDownLatch.await();
        return objects;
    }


    /**
     * 服务端流
     * 客户端一次发送
     * 服务端多次返回
     * 异步客户端+服务端流模式
     *
     * @return
     * @throws InterruptedException
     */
    @GetMapping("serverModule")
    public List serverModule() throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        ArrayList<Object> objects = new ArrayList<>(16);
        List<Integer> integers = Arrays.asList(1, 2);
        for (int i = 0; i < integers.size(); i++) {
            InspectionPlanListByMasterDTO.Builder builder = InspectionPlanListByMasterDTO.newBuilder();
            builder.setMasterPlanId(i + "");
            moduleTestStub.inspectionPlanListByMasterIdAndOnlyServerStream(builder.build(), new StreamObserver<InspectionPlanByMasterDTO>() {
                @SneakyThrows
                @Override
                public void onNext(InspectionPlanByMasterDTO value) {
                    log.info("[收到服务端发来] : " + value);
                    objects.add(printer.print(value));
                }

                @Override
                public void onError(Throwable t) {
                    log.error(t.getMessage(), t);
                    countDownLatch.countDown();
                }

                @Override
                public void onCompleted() {
                    log.info("服务端 onCompleted");
                    countDownLatch.countDown();
                }
            });
        }
        countDownLatch.await();
        return objects;
    }


    /**
     * 普通模式
     * 客户端一次发送
     * 服务端一次返回
     * 多个就循环
     * 异步客户端+普通调用模式
     *
     * @return
     * @throws InterruptedException
     */
    @GetMapping("ordinaryModule")
    public List ordinaryModule() throws InterruptedException {
        ArrayList<Object> objects = new ArrayList<>(16);
        List<Integer> integers = Arrays.asList(1, 2);
        for (int i = 0; i < integers.size(); i++) {
            InspectionPlanListByMasterDTO.Builder builder = InspectionPlanListByMasterDTO.newBuilder();
            builder.setMasterPlanId(i + "");
            moduleTestStub.inspectionPlanListByMasterId(builder.build(), new StreamObserver<InspectionPlanListByMasterResponse>() {
                @SneakyThrows
                @Override
                public void onNext(InspectionPlanListByMasterResponse value) {
                    log.info("[收到服务端发来] : " + value);
                    objects.add(printer.print(value));
                }

                @Override
                public void onError(Throwable t) {
                    log.error(t.getMessage(), t);
                }

                @Override
                public void onCompleted() {
                    log.info("服务端 onCompleted");
                }
            });
        }
        return objects;
    }


    /**
     * 普通阻塞客户端+普通调用模式
     *
     * @return
     * @throws InterruptedException
     * @throws InvalidProtocolBufferException
     */
    @GetMapping("ordinaryModuleBlock")
    public List ordinaryModuleBlock() throws InterruptedException, InvalidProtocolBufferException {
        ArrayList<Object> objects = new ArrayList<>(16);
        List<Integer> integers = Arrays.asList(1, 2);
        for (int i = 0; i < integers.size(); i++) {
            InspectionPlanListByMasterDTO.Builder builder = InspectionPlanListByMasterDTO.newBuilder();
            builder.setMasterPlanId(i + "");
            InspectionPlanListByMasterResponse inspectionPlanListByMasterResponse = moduleTestBlockingStub.inspectionPlanListByMasterId(builder.build());
            String print = printer.print(inspectionPlanListByMasterResponse);
            objects.add(print);
        }
        return objects;
    }

}

服务端代码实例:

package com.aniu.inspection.grpc.server;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.aniu.inspection.grpc.InspectionPlanByMasterDTO;
import com.aniu.inspection.grpc.InspectionPlanListByMasterDTO;
import com.aniu.inspection.grpc.InspectionPlanListByMasterResponse;
import com.aniu.inspection.grpc.ModuleTestGrpc;
import com.aniu.inspection.model.entity.InspectionPlan;
import com.aniu.inspection.model.entity.InspectionPlanRelation;
import com.aniu.inspection.service.IInspectionPlanRelationService;
import com.aniu.inspection.service.IInspectionPlanService;
import io.grpc.stub.StreamObserver;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.server.service.GrpcService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

/**
 * @author pengyouy
 * @since 2022/7/7
 * grpc 模式示例
 */
@GrpcService
@Slf4j
public class ModuleTestGrpcImpl extends ModuleTestGrpc.ModuleTestImplBase {

    @Autowired
    private IInspectionPlanRelationService inspectionPlanRelationService;

    @Autowired
    private IInspectionPlanService inspectionPlanService;

    @Override
    @Transactional(readOnly = true)
    public void inspectionPlanListByMasterId(InspectionPlanListByMasterDTO request, StreamObserver<InspectionPlanListByMasterResponse> responseObserver) {
        InspectionPlanListByMasterResponse.Builder builder = InspectionPlanListByMasterResponse.newBuilder();
        Set<String> planIdList = Optional.of(inspectionPlanRelationService.list(
                Wrappers.<InspectionPlanRelation>lambdaQuery()
                        .select(InspectionPlanRelation::getPlanId)
                        .eq(InspectionPlanRelation::getMasterPlanId, request.getMasterPlanId()))
                .stream().map(InspectionPlanRelation::getPlanId).collect(Collectors.toSet()))
                // 构造一个不存在的id值防止in()
                .filter(f -> f.size() > 0)
                .orElseGet(() -> Collections.singleton(UUID.randomUUID().toString()));
        List<InspectionPlanListByMasterResponse.InspectionPlanList> collect = inspectionPlanService.list(
                Wrappers.<InspectionPlan>lambdaQuery()
                        .orderByDesc(InspectionPlan::getSysCreated)
                        .in(InspectionPlan::getId, planIdList))
                .stream().map(f ->
                        InspectionPlanListByMasterResponse.InspectionPlanList.newBuilder()
                                .setPlanId(f.getId())
                                .setDepartmentId(f.getDepartmentId())
                                .setName(f.getName())
                                .setNumber(f.getNumber())
                                .setYear(f.getYear()).build()).collect(Collectors.toList());
        for (int i = 0; i < collect.size(); i++) {
            builder.addList(i, collect.get(i));
        }
        responseObserver.onNext(builder.build());
        responseObserver.onCompleted();
    }

    @Override
    public void inspectionPlanListByMasterIdAndOnlyServerStream(InspectionPlanListByMasterDTO request, StreamObserver<InspectionPlanByMasterDTO> responseObserver) {
        // 查询主计划和计划的所有关系
        Optional.of(inspectionPlanRelationService.list(Wrappers.<InspectionPlanRelation>lambdaQuery()
                .eq(InspectionPlanRelation::getMasterPlanId, request.getMasterPlanId())))
                .filter(f -> f.size() > 0)
                .map(m -> inspectionPlanService.list(Wrappers.<InspectionPlan>lambdaQuery()
                        .in(InspectionPlan::getId,
                                m.stream().map(InspectionPlanRelation::getPlanId).collect(Collectors.toSet()))))
                .ifPresent(i -> i.forEach(f -> responseObserver.onNext(InspectionPlanByMasterDTO.newBuilder()
                        .setPlanId(f.getId())
                        .setDepartmentId(f.getDepartmentId())
                        .setName(f.getName())
                        .setNumber(f.getNumber())
                        .setYear(f.getYear()).build())));
        responseObserver.onCompleted();
    }

    @Override
    public StreamObserver<InspectionPlanListByMasterDTO> inspectionPlanListByMasterIdAndOnlyClientStream(StreamObserver<InspectionPlanListByMasterResponse> responseObserver) {
        return new StreamObserver<InspectionPlanListByMasterDTO>() {
            final List<InspectionPlan> inspectionPlans = new CopyOnWriteArrayList<>();

            @SneakyThrows
            @Override
            public void onNext(InspectionPlanListByMasterDTO value) {
                log.info("[收到客户端消息]: " + value);
                // 查询主计划和计划的所有关系
                Optional.of(inspectionPlanRelationService.list(Wrappers.<InspectionPlanRelation>lambdaQuery()
                        .eq(InspectionPlanRelation::getMasterPlanId, value.getMasterPlanId())))
                        .filter(f -> f.size() > 0)
                        .map(m -> inspectionPlanService.list(Wrappers.<InspectionPlan>lambdaQuery()
                                .in(InspectionPlan::getId,
                                        m.stream().map(InspectionPlanRelation::getPlanId).collect(Collectors.toSet()))))
                        .ifPresent(inspectionPlans::addAll);
            }

            @Override
            public void onError(Throwable t) {
                log.error(t.getMessage(), t);
            }

            @SneakyThrows
            @Override
            public void onCompleted() {
                log.debug("客户端调用参数onCompleted");
                InspectionPlanListByMasterResponse.Builder builder = InspectionPlanListByMasterResponse.newBuilder();
                List<InspectionPlanListByMasterResponse.InspectionPlanList> collect = inspectionPlans.stream().map(f -> InspectionPlanListByMasterResponse.InspectionPlanList.newBuilder()
                        .setPlanId(f.getId())
                        .setDepartmentId(f.getDepartmentId())
                        .setName(f.getName())
                        .setNumber(f.getNumber())
                        .setYear(f.getYear()).build()).collect(Collectors.toList());
                builder.addAllList(collect);
                InspectionPlanListByMasterResponse build = builder.build();
                responseObserver.onNext(build);
                responseObserver.onCompleted();
            }
        };
    }

    @Override
    public StreamObserver<InspectionPlanListByMasterDTO> inspectionPlanListByMasterIdAndServerStream(StreamObserver<InspectionPlanByMasterDTO> responseObserver) {
        return new StreamObserver<InspectionPlanListByMasterDTO>() {
            @Override
            public void onNext(InspectionPlanListByMasterDTO value) {
                log.info("[收到客户端消息]: " + value);
                // 查询主计划和计划的所有关系
                Optional.of(inspectionPlanRelationService.list(Wrappers.<InspectionPlanRelation>lambdaQuery()
                        .eq(InspectionPlanRelation::getMasterPlanId, value.getMasterPlanId())))
                        .filter(f -> f.size() > 0)
                        .map(m -> inspectionPlanService.list(Wrappers.<InspectionPlan>lambdaQuery()
                                .in(InspectionPlan::getId,
                                        m.stream().map(InspectionPlanRelation::getPlanId).collect(Collectors.toSet()))))
                        .ifPresent(i -> i.forEach(f -> responseObserver.onNext(InspectionPlanByMasterDTO.newBuilder()
                                .setPlanId(f.getId())
                                .setDepartmentId(f.getDepartmentId())
                                .setName(f.getName())
                                .setNumber(f.getNumber())
                                .setYear(f.getYear()).build())));
            }

            @Override
            public void onError(Throwable t) {
            }

            @Override
            public void onCompleted() {
                log.debug("客户端调用参数onCompleted");
                responseObserver.onCompleted();
            }
        };
    }


}

服务端流日志traceId以及装饰类

装饰类:
package com.aniu.inspection.grpc.extension;

import cn.hutool.core.util.StrUtil;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import com.google.protobuf.util.JsonFormat;
import io.grpc.stub.StreamObserver;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.slf4j.MDC;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author pengyouy
 * @since 2022/7/6
 * 自定义包装类 用来获取响应参数配合aop打印出参数以及时间
 */
@Slf4j
public class StreamObserverWrapper<v extends Message> implements StreamObserver<v> {

    private static final int ADJ_LEN = 4096;

    private static final String KEY_TRACE_ID = "requestId";

    /**
     * message json打印对象
     */
    private final static JsonFormat.Printer PRINTER = JsonFormat.printer().omittingInsignificantWhitespace();
    /**
     * 被装饰的原对象
     */
    private final StreamObserver<v> own;
    /**
     * 包装保存onNext参数
     * 参数json数据
     * 多次调用使用集合
     */
    private final List<String> nextValueList = new CopyOnWriteArrayList<String>();

    /**
     * 初始化时间
     */
    private final DateTime initTime;
    /**
     * 调用onCompleted 时间
     */
    private DateTime onCompletedTime;

    /**
     * 请求json参数
     */
    private final List<String> carryJsonParamList = new CopyOnWriteArrayList<String>();


    /**
     * 目标方明名称
     */
    private final String methodName;


    /**
     * 输入还是输出
     * request
     * response
     *
     * @see Type
     */
    private Type type = Type.request;

    /**
     * 响应的操作对象
     */
    private StreamObserverWrapper<v> response;


    /**
     * 请求的日志链路Id
     */
    private final String traceId;

    public StreamObserverWrapper(StreamObserver<v> own, String methodName, Type type, String traceId) {
        this.type = type;
        this.methodName = methodName;
        this.own = own;
        initTime = new DateTime();
        this.traceId = traceId;
    }

    public StreamObserverWrapper(StreamObserver<v> own, String methodName, Type type, StreamObserverWrapper<v> response, String traceId) {
        this.type = type;
        this.methodName = methodName;
        this.own = own;
        this.response = response;
        initTime = new DateTime();
        this.traceId = traceId;
    }

    @Override
    public void onNext(v value) {
        try {
            MDC.put(KEY_TRACE_ID, traceId);
            String print = PRINTER.print(value);
            nextValueList.add(print);
            log.debug("nextValue :[{}]", StrUtil.sub(print, 0, getAdjustLength()));
            own.onNext(value);
        } catch (InvalidProtocolBufferException e) {
            log.warn("on next print error:[{}]", e.getMessage(), e);
        } finally {
            MDC.remove(KEY_TRACE_ID);
        }
    }

    @Override
    public void onError(Throwable t) {
        try {
            MDC.put(KEY_TRACE_ID, traceId);
            onCompletedTime = new DateTime();
            Interval interval = new Interval(initTime, onCompletedTime);
            log.info("Call API {} End () Type [{}] CarryParam => {}, RT:{} ms, NextValue => {}, Error:[{}]",
                    methodName, type,
                    StrUtil.sub(carryJsonParamList.toString(), 0, getAdjustLength()),
                    interval.toDurationMillis(),
                    StrUtil.sub(nextValueList.toString(), 0, getAdjustLength()), t.getMessage(), t);
            own.onError(t);
        } finally {
            MDC.remove(KEY_TRACE_ID);
        }

    }

    @Override
    public void onCompleted() {
        try {
            MDC.put(KEY_TRACE_ID, traceId);
            onCompletedTime = new DateTime();
            Interval interval = new Interval(initTime, onCompletedTime);
            if (type == Type.request) {
                // 把客户端流当前获得的所有输入参数传递给response
                response.getCarryJsonParamList().addAll(getNextValueList());
            }
            log.info("Call API {} End () Type [{}] CarryParam => {}, RT:{} ms, NextValue => {}",
                    methodName, type,
                    StrUtil.sub(carryJsonParamList.toString(), 0, getAdjustLength()),
                    interval.toDurationMillis(), StrUtil.sub(nextValueList.toString(), 0, getAdjustLength()));
            own.onCompleted();
        } finally {
            MDC.remove(KEY_TRACE_ID);
        }
    }

    public List<String> getNextValueList() {
        return nextValueList;
    }

    public DateTime getOnCompletedTime() {
        return onCompletedTime;
    }

    public DateTime getInitTime() {
        return initTime;
    }

    public List<String> getCarryJsonParamList() {
        return carryJsonParamList;
    }


    /**
     * 打印的长度
     */
    protected int getAdjustLength() {
        return ADJ_LEN;
    }

    /**
     * 包装的是输入还是输出
     */
    public enum Type {
        /**
         * 代表请求客户端
         */
        request,
        /**
         * 代表响应服务端
         */
        response;
    }
}
日记切面:
package com.aniu.inspection.config.aop;

import com.google.protobuf.Message;
import com.aniu.inspection.grpc.extension.StreamObserverWrapper;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.UUID;

/**
 * @author pengyy
 * @since 2022年7月4日
 * grpc调用的日志切面
 */
@Aspect
@Order(1)
@Component
public class GrpcLogAspect {
    private static final Logger LOGGER = LoggerFactory.getLogger(GrpcLogAspect.class);

    /**
     * 请求日志的链路id key
     */
    private static final String KEY_TRACE_ID = "requestId";

    @Pointcut("execution(* com.aniu.inspection.grpc.server.*.*(..))")
    public void apiLogAop() {
    }

    @Around("apiLogAop()")
    public Object aroundApi(ProceedingJoinPoint point) throws Throwable {
        return log(point);
    }

    private Object log(ProceedingJoinPoint point) throws Throwable {
        // 请求的日志链路Id
        String traceId = UUID.randomUUID().toString();
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        String methodMerge = point.getSignature().getDeclaringTypeName() + "." + point.getSignature().getName();
        final Object[] args = point.getArgs();
        Object response;
        // 获取类上的注解
        GrpcService annotation = point.getTarget().getClass().getAnnotation(GrpcService.class);
        try {
            MDC.put(KEY_TRACE_ID, traceId);
            LOGGER.info("GrpcLogAspect traceId:{},method:{}", traceId, methodMerge);
            if (annotation != null) {
                if (method.getReturnType() == StreamObserver.class
                        && args.length == 1 &&
                        args[0] instanceof StreamObserver) {
                    // 存在客户端流
                    StreamObserverWrapper<Message> streamObserverWrapper = new StreamObserverWrapper<Message>((StreamObserver<Message>) args[0], methodMerge, StreamObserverWrapper.Type.response, traceId);
                    Object[] argsReplace = {streamObserverWrapper};
                    response = point.proceed(argsReplace);
                    if (response instanceof StreamObserver) {
                        StreamObserver<Message> observerWrapper = (StreamObserver<Message>) response;
                        return new StreamObserverWrapper<>(observerWrapper, methodMerge, StreamObserverWrapper.Type.request, streamObserverWrapper, traceId);
                    }
                } else if (method.getReturnType() == void.class
                        && args.length == 2
                        && (args[1] instanceof StreamObserver)) {
                    StreamObserverWrapper<Message> streamObserverWrapper = new StreamObserverWrapper<Message>((StreamObserver<Message>) args[1], methodMerge, StreamObserverWrapper.Type.response, traceId);
                    Object[] argsReplace = {args[0], streamObserverWrapper};
                    return point.proceed(argsReplace);
                }
            }
            // 非grpc调用方法
            return point.proceed();
        } finally {
            MDC.remove(KEY_TRACE_ID);
        }
    }
}

proto文件:

syntax = "proto3";

import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
import "inspectionmasterplan.proto";
option java_multiple_files = true;
option java_package = "com.aniu.inspection.grpc";

package grpc.test;

service ModuleTest{

  // (普通模式 )
  rpc inspectionPlanListByMasterId(InspectionPlanListByMasterDTO) returns(InspectionPlanListByMasterResponse);

  // (服务端流返回)
  rpc inspectionPlanListByMasterIdAndOnlyServerStream(InspectionPlanListByMasterDTO) returns(stream InspectionPlanByMasterDTO);

  // (客户端流返回)
  rpc inspectionPlanListByMasterIdAndOnlyClientStream(stream InspectionPlanListByMasterDTO) returns(InspectionPlanListByMasterResponse);

  // (双服务端流返回)
  rpc inspectionPlanListByMasterIdAndServerStream(stream InspectionPlanListByMasterDTO) returns(stream InspectionPlanByMasterDTO);
}

总结:

1 通过简单的示例描述了 grpc四种模式三个客户端的使用
2.通过包装服务端StreamObserver从而将一次请求的入参出参RT以及请求中的日志链路id串联起来