美文网首页安卓
Android GRPC 最佳实践

Android GRPC 最佳实践

作者: 孙大硕 | 来源:发表于2021-03-20 12:48 被阅读0次

    Android Grpc 最佳实践

    前言:最近老听说rpc,就知道可以代替之前的HTTP框架,像调用本地方法一样请求接口,目前公司内部很多部门也都接入了rpc,下面看一下Android端我是怎么接入的。(其实也是搬砖,很多地方搬得不好)

    1. 工欲善其事,必先利其器

    Grpc 的基础就是利用proto文件生成Java代码,proto文件由接口提供方提供。Android Studio支持“傻瓜式”生成代码,不用安装工具以及打一大长串的命令。

    首先 Preferences -> Plugins 搜索Proto,安装下面这个东西,这个插件提供编辑功能,有代码补全和导航的功能。


    image-20201029193602128.png

    2. 开始配置Gradle

    首先 在 project的build.gradle 的 buildscript 的dependencies 下面添加

    classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'
    

    然后将proto文件拷贝到模块中,我这里放到和Java平级

    image-20201029194246135.png

    然后在 模块的build.gradle 添加

    plugins {
        id 'com.android.library'
        id 'com.google.protobuf' // proto
    }
    

    然后指定proto文件位置

    sourceSets {
            main {
                java {
                    srcDir 'src/main/java'
                }
    
                proto {
                    srcDir 'src/main/proto' // 模块下的proto文件夹
                    include '**/*.proto'
                }
            }
     }
    

    然后和android标签同级,添加以下代码

    protobuf {
        protoc { artifact = 'com.google.protobuf:protoc:3.12.0' } // 相当于proto编译器
        plugins {
            grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.33.0' } // Grpc单独的编译器
    
            javalite {
                artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0' // 官方推荐的方法,Android 适用javalite,相较于java插件,生成的代码更轻量化
            }
        }
        generateProtoTasks {
            all().each { task ->
                task.builtins {
                    java { option 'lite' }
                }
                task.plugins {
                    grpc { // Options added to --grpc_out
                        option 'lite' }
                }
            }
        }
    }
    

    然后再添加这些依赖

    implementation 'io.grpc:grpc-okhttp:1.33.0'
    implementation 'io.grpc:grpc-protobuf-lite:1.33.0'
    implementation 'io.grpc:grpc-stub:1.33.0'
    

    生成的grpc代码里会引用这些库里的代码

    上面这些配置都是自己参考各路大神一点点摸索出来的,可能有不足的地方。

    配置好了,然后开始看看proro文件吧

    3. 开始整proto

    先说一下刚开始安装的插件的用法,在 Preferences->Languages&Frameworks -> Protocol Buffers ,将自己的proto文件的全路径添加到配置里,这样就能导航了,编辑的时候也不会提示错误,如果在自己的proto文件中使用到了其他库里的proto,我的建议是都去下载下来,然后copy到自己的工程目录中。可能是我不会用,否则即使指定了proto的目录,编译时也会提示找不到。


    image-20201029200818132.png

    下面以Jaeger (关于Jaeger在我另外一篇文章有提到)官方提供的proto为例,例如下面的model.proto

    syntax="proto3";
    
    package jaeger.api_v2;
    
    import "gogoproto/gogo.proto";
    import "google/protobuf/timestamp.proto";
    import "google/protobuf/duration.proto";
    
    option go_package = "model";
    option java_package = "io.jaegertracing.api_v2"; // 生成的Java代码包名
    
    enum ValueType {
      STRING  = 0;
      BOOL    = 1;
      INT64   = 2;
      FLOAT64 = 3;
      BINARY  = 4;
    };
    
    message KeyValue {
      option (gogoproto.equal) = true;
      option (gogoproto.compare) = true;
    
      string    key      = 1;
      ValueType v_type    = 2;
      string    v_str     = 3;
      bool      v_bool    = 4;
      int64     v_int64   = 5;
      double    v_float64 = 6;
      bytes     v_binary  = 7;
    }
    
    message Log {
      google.protobuf.Timestamp timestamp = 1 [
        (gogoproto.stdtime) = true,
        (gogoproto.nullable) = false
      ];
      repeated KeyValue fields = 2 [
        (gogoproto.nullable) = false
      ];
    }
    ......
    

    在这个proto中引用了 三个其他的proto文件,这些文件我们在github上可以直接搜到,然后下载下来copy到自己的proto文件夹中(按理说不应该这么做,一种办法是使用命令编译,指定依赖文件,引入的Gradle插件应该也支持,但是不知道怎么用o(╥﹏╥)o),下载下来的proto不用更改任何东西,最多改个名,然后把自己的proto文件里的import改一下,比如上面的可以直接改成“import "gogo.proto";”

    把前面的路径删了就行,如果这些第三方proto还引用了其他的,则再按照这个方法,直到所有proto文件都在自己工程里,这样实在太累了。例如我的工程,对我有用的只有两个proto,我却得引用这么多,谁能有个好办法,麻烦告诉我。


    image-20201029204030640.png

    4. 开始编译

    在 build.gradle添加’com.google.protobuf‘ 就有了generateDebugProto命令,可以在下面的路径里找到

    image-20201029204621066.png

    双击运行,然后切换到Android目录,就能看到生成的代码了

    image-20201029204855968.png

    个人感觉可以把生成的代码拷贝出来(如果proto不经常变的情况),把插件禁用掉,避免每次都生成。

    5. 开始使用Grpc

    一开始不知道哪个类里有grpc请求,后来发现类名就是proto文件里的grpc service 字段后加个Grpc后缀,比如下面

    service CollectorService {
        rpc PostSpans(PostSpansRequest) returns (PostSpansResponse) {
            option (google.api.http) = {
                post: "/api/v2/spans"
                body: "*"
            };
        }
    }
    

    生成的Grpc请求代码就在CollectorServiceGrpc.java中,PostSpans 就是发起请求的方法,PostSpansRequest,就是要传输的数据,生成的Java代码如下:

    public com.sunshuo.grpc.jaeger.Collector.PostSpansResponse postSpans(com.sunshuo.grpc.jaeger.Collector.PostSpansRequest request) {
          return blockingUnaryCall(
              getChannel(), getPostSpansMethod(), getCallOptions(), request);
        }
      }
    

    Grpc的用法也是固定的

    ManagedChannelBuilder mChannelBuilder = ManagedChannelBuilder.forAddress(ip, port); // grpc服务ip地址,port端口号
    //或者 ManagedChannelBuilder.forTarget(url) url 服务器地址
    CollectorServiceGrpc.CollectorServiceBlockingStub mBlockingStub = CollectorServiceGrpc.newBlockingStub(mChannel); // 生成一个远端服务在client的存根,看名称应该是阻塞调用
    Collector.PostSpansRequest request = Collector.PostSpansRequest.newBuilder().setBatch(ConvertUtil.convertBatch(spans, process)).build(); // 构建请求的Bean
    Collector.PostSpansResponse response = mBlockingStub.postSpans(request); //开始请求,并拿到response
    

    6. 总结

    一旦生成了Grpc代码,调用过程是非常简单的。我在使用的时候遇到各种UNAVAILABLE,因为由于一些原因我把生成的代码里的某些JavaBen换成了其他的。最好不要改生成的代码,除非有十足的把握。

    相关文章

      网友评论

        本文标题:Android GRPC 最佳实践

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