美文网首页学习微服务架构和实践轻量级微服务架构
Spring Boot 使用 gRPC 轻松调用远程方法

Spring Boot 使用 gRPC 轻松调用远程方法

作者: Anoyi | 来源:发表于2018-04-23 17:56 被阅读1263次

    gRPC 简介

    gRPC 是一个现代开源的高性能 RPC 框架,可以在任何环境下运行。它可以有效地将数据中心内和跨数据中心的服务与可插拔支持进行负载均衡、跟踪、健康检查和认证。它也适用于分布式计算,将不同设备、移动应用程序和浏览器连接到后端服务。

    主要使用场景:

    • 在微服务架构中有效地连接多个服务
    • 将移动设备、浏览器客户端连接到后端服务
    • 生成高效的客户端库

    核心功能:

    • 10 种语言的客户端库支持
    • 高效、简单的服务定义框架
    • 基于 http/2 传输的双向流式传输
    • 可插拔的认证、跟踪、负载均衡和健康检查

    Spring Boot 快速集成 gRPC

    1、获取 spring-boot-starter-grpc 源码

    git clone https://github.com/ChinaSilence/spring-boot-starter-grpc.git
    

    2、安装到本地 Maven 仓库

    mvn install
    

    3、在 Spring Boot 工程中添加依赖

    <dependency>
        <groupId>com.anoyi</groupId>
        <artifactId>spring-boot-starter-grpc</artifactId>
        <version>1.0.0.RELEASE</version>
    </dependency>
    

    4、gRPC 使用说明

    4.1 参数配置说明

    • spring.grpc.enable 是否启用 gRPC 服务端,默认 false
    • spring.grpc.port 监听的端口号
    • spring.grpc.remote-servers 供客户端调用的服务端列表

    4.2 示例:gRPC 服务端,在 application.yml 中添加配置

    spring:
      grpc:
        enable: true
        port: 6565
    

    4.3 示例:gRPC 客户端,在 application.yml 中添加配置

    spring:
      grpc:
        remote-servers:
          - host: localhost
            port: 6565
          - host: 192.168.0.3
            port: 6565
    

    4.4 远程服务调用

    远程服务调用需要知道远程服务的:地址、端口号、服务类、类方法、方法参数,该 starter 定义了 GrpcRequestGrpcResponse

    public class GrpcRequest {
    
        /**
         * service 类名
         */
        private String beanName;
    
        /**
         * service 方法名
         */
        private String methodName;
    
        /**
         * service 方法参数
         */
        private Object[] args;
    
        // 略 setter / getter...
    }
    
    public class GrpcResponse {
    
        /**
         * 响应状态:0 - 成功, 1 - 失败
         */
        private int status;
    
        /**
         * 返回结果
         */
        private Object result;
    
        // 略 setter / getter...
    }
    

    4.4.1 示例:服务端提供服务,与单体 Spring Boot 无差别,即单体 Spring Boot 应用可以无缝集成

    @Service
    public class HelloService{
    
        public String sayHello(){
            return "Hello";
        }
    
        public String say(String words){
            return "Hello " + words;
        }
    
    }
    

    4.4.2 【注解方式】示例:客户端调用服务

    import org.springframework.grpc.annotation.GrpcService;
    
    /**
     * 使用 @GprcService 注解定义远程服务,server 指定远程服务名,必须在 application.yml 中定义才能使用
     * 方法名 、参数 、 返回结果 必须与服务提供方一致
     */
    @GrpcService(server = "localhost")
    public interface HelloService {
    
        public String sayHello();
    
        public String say(String words);
    
    }
    
    

    4.4.3 【非注解方式】示例:客户端调用服务

        public void test(){
            // 构建请求体
            GrpcRequest grpcRequest = new GrpcRequest();
            grpcRequest.setServiceBeanName("helloService");
    
            // 无参方法调用
            grpcRequest.setServiceMethodName("sayHello");
            try {
                // 此处服务提供方需要在配置文件中定义,否则无法调用
                GrpcResponse response = GrpcClient.connect("localhost").handle(grpcRequest);
                if (response.getStatus() == GrpcResponseStatus.SUCCESS.getCode()){
                    System.out.println(response.getResult());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            // 有参方法调用
            grpcRequest.setServiceMethodName("say");
            Object[] args = {"hello"};
            grpcRequest.setArgs(args);
            try {
                // 此处服务提供方需要在配置文件中定义,否则无法调用
                GrpcResponse response = GrpcClient.connect("localhost").handle(grpcRequest);
                if (response.getStatus() == GrpcResponseStatus.SUCCESS.getCode()){
                    System.out.println(response.getResult());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    

    4.4.4 【测试使用】示例:为方便调试,通过原生方式调用远程服务,无需依赖 Spring Boot

    import com.alibaba.fastjson.JSON;
    import com.anoyi.rpc.CommonServiceGrpc;
    import com.anoyi.rpc.GrpcService;
    import io.grpc.ManagedChannel;
    import io.grpc.ManagedChannelBuilder;
    import org.springframework.grpc.service.GrpcRequest;
    
    import java.util.concurrent.TimeUnit;
    
    public class GrpcClient {
    
        private final ManagedChannel channel;
        private final CommonServiceGrpc.CommonServiceBlockingStub blockingStub;
    
        public GrpcClient(String host, int port) {
            this(ManagedChannelBuilder.forAddress(host, port).usePlaintext().build());
        }
    
        private GrpcClient(ManagedChannel channel) {
            this.channel = channel;
            blockingStub = CommonServiceGrpc.newBlockingStub(channel);
        }
    
        public static void main(String[] args) throws Exception {
            GrpcClient client = new GrpcClient("localhost", 6565);
            try {
                for (int i = 0; i < 100; i++) {
                    String words = "world - " + i;
                    client.say(words);
                }
            } finally {
                client.shutdown();
            }
        }
    
        private void shutdown() throws InterruptedException {
            channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
        }
    
        private void say(String words) {
            GrpcRequest grpcRequest = new GrpcRequest();
            grpcRequest.setBeanName("helloService");
            grpcRequest.setMethodName("say");
            Object[] params = {words};
            grpcRequest.setArgs(params);
            System.out.println("远程调用 " + grpcRequest.getServiceBeanName() + "." + grpcRequest.getServiceMethodName() + " ");
            byte[] bytes = ProtobufUtils.serialize(grpcRequest);
            GrpcService.Request request = GrpcService.Request.newBuilder().setRequest(ByteString.copyFrom(bytes)).build();
            GrpcService.Response response = blockingStub.handle(request);
            System.out.println("远程调用结果: " + response.getReponse());
        }
    
    }
    

    相关文档

    相关文章

      网友评论

      • 8f3fce326ad1:请教一下,
        我理解,项目里是将Object序列化为bytes,然后再通过protobuf对bytes进行二进制编码传输
        这样比起直接通过protobuf将已知的Object进行二进制编码,性能的开销会更大吗?
        抑或对于bytes类型,protobuf就直接传输了,没有额外的性能损耗。
        8f3fce326ad1:@Anoyi 那么基于这个架子,在接口升级时,应该不支持PB原有的前后兼容的功能。
        比如基于官方的grpc调用,如果request增加一个optional字段,在handle函数里对应处理一下就好。
        在这个框架里应该是要增加一个新的request、handle函数支持新接口,保留老的request,支持旧接口。
        不知道我理解的对不对?
        Anoyi:@iwanttopee 确实是二次序列化的设计,protobuf 的性能损耗几乎可以忽略不计
      • 年华的乐章:能不能详细讲一下 ProtoBuf 与service 的配合?
        Anoyi:@年华的乐章 又写了一篇 https://anoyi.com/p/260a1ac847b6
      • XWSxws:你好,有没有ios 相关的demo,用grpc怎么传token值,怎么进行双向认证
        XWSxws:@1994_老叶 请问,你们公司用rpc了吗
        1994_老叶:rpc是远程调用程序,目前多用于后端服务之间的相互协作,你IOS端想要调用相关的服务,需要后端给你提供相关服务接口,而你需要向这些接口发送请求(例如http请求),这样进行通信,而不是你直接使用rpc调用后端服务
        Anoyi:@XWSxws 目前这个用于内部服务间通信,其他语言需要看官方文档
      • 淘贝贝:服务方报这个错误,Could not initialize class io.grpc.netty.NettyServerHandler,是依赖不正确吗
        淘贝贝:@Anoyi 可以正常打包,而且grpc端口start正常,远程调用的时候报这样一个错误
        Anoyi:@淘贝贝 能正常打包 starter 吗
      • 科学Jia:刚拉下了你的最新代码…跟追剧一样…是不是用这个rpcclientTest去访问另外已经启动好的grpc的server服务就可以进行测试了?在test路径下可以直接run这个main函数么?第一次见到test路径下有main:flushed:
        Anoyi:@科学Jia 先建一个 Spring Boot 应用,提供服务,然后用 test 类去调用来调试
      • 科学Jia:最近正想弄弄这个:heart_eyes:
        Anoyi:@科学Jia 因为 gRPC 是 CNCF 项目啊
        科学Jia:@Anoyi 你这不都干完的样子了么:stuck_out_tongue_winking_eye: btw,这么多rpc框架为啥只选择了grpc?是因为它用到了真正意义上并发的http/2么?
        Anoyi:@科学Jia 来来来,一起开发:smile:

      本文标题:Spring Boot 使用 gRPC 轻松调用远程方法

      本文链接:https://www.haomeiwen.com/subject/iaellftx.html