天天看點

gRPC在Android中的應用、配置、生成檔案及登入實作gRPC在Android中的應用

gRPC在Android中的應用

gRPC的特性:

gRPC 由 Google 開發,是一款語言中立、平台中立、開源的遠端過程調用(RPC)系統。

基于HTTP/2

HTTP/2 提供了連接配接多路複用、雙向流、伺服器推送、請求優先級、首部壓縮等機制。可以節省帶寬、降低TCP連結次數、節省CPU,幫助移動裝置延長電池壽命等。gRPC 的協定設計上使用了HTTP2 現有的語義,請求和響應的資料使用HTTP Body 發送,其他的控制資訊則用Header 表示。

IDL使用ProtoBuf

gRPC使用ProtoBuf來定義服務,ProtoBuf是由Google開發的一種資料序列化協定(類似于XML、JSON、hessian)。ProtoBuf能夠将資料進行序列化,并廣泛應用在資料存儲、通信協定等方面。壓縮和傳輸效率高,文法簡單,表達力強。

多語言支援

gRPC支援多種語言(C, C++, Python, PHP, Nodejs, C#, Objective-C、Golang、Java),并能夠基于語言自動生成用戶端和服務端功能庫。目前已提供了C版本grpc、Java版本grpc-java 和 Go版本grpc-go,其它語言的版本正在積極開發中,其中,grpc支援C、C++、Node.js、Python、Ruby、Objective-C、PHP和C#等語言,grpc-java已經支援Android開發。

gRPC優缺點:

優點

protobuf二進制消息,性能好/效率高(空間和時間效率都很不錯)

proto檔案生成目标代碼,簡單易用

序列化反序列化直接對應程式中的資料類,不需要解析後在進行映射(XML,JSON都是這種方式)

支援向前相容(新加字段采用預設值)和向後相容(忽略新加字段),簡化更新

支援多種語言(可以把proto檔案看做IDL檔案)

Netty等一些架構內建

缺點:

GRPC尚未提供連接配接池,需要自行實作

尚未提供“服務發現”、“負載均衡”機制

因為基于HTTP2,絕大部多數HTTP Server、Nginx都尚不支援,即Nginx不能将GRPC請求作為HTTP請求來負載均衡,而是作為普通的TCP請求。(nginx1.9版本已支援)

Protobuf二進制可讀性差(貌似提供了Text_Fromat功能)

預設不具備動态特性(可以通過動态定義生成消息類型或者動态編譯支援)

grpc主要使用場景:

1.低延時、高可用的分布式系統;

2.移動端與雲服務端的通訊;

3.使用protobuf,獨立于語言的協定,支援多語言之間的通訊;

4.可以分層擴充,如:身份驗證,負載均衡,日志記錄,監控等。

在微服務場景中使用究竟是否要使用grpc呢?開源社群較為成熟的微服務架構有

dubbo、spring cloud。dubbo雖然在服務治理上做的比較完善,但是不支援跨語言。個人覺得,如果不考慮跨語言問題,選用dubbo。考慮跨語言,可以選用grpc、Thrift,但是grpc、Thrift沒有服務發現和負載均衡機制,一般使用代理轉發負載均衡控制政策。

在移動端app應用場景中,grpc以其優異的性能,因pb和http2的特性,為移動端使用者節省流量、電量。對比傳統的http1.1+json方式,建議可以先嘗試小範圍使用grpc,待系統穩定後,再擴大grpc使用範圍。

Android Studio中gRPC的配置

1.導入相關依賴(小編用的Gradle)可以通過Github上的 https://github.com/grpc/grpc-java 熟悉搭建過程。小編用的這是1.27.0這個版本,小編搭建的時候GitHub還沒有

包,小編就自己引了一個annotations的包,這裡還是推薦用GitHub上的這個吧。

implementation 'io.grpc:grpc-okhttp:1.27.0'
implementation 'io.grpc:grpc-protobuf:1.27.0'
implementation 'io.grpc:grpc-stub:1.27.0'
           

protocbuf依賴

2.安裝插件,在項目的根配置中添加插件,全部配置内容如下:

buildscript {
    repositories {
        google()
        jcenter()
        mavenCentral()
        maven {url 'http://maven.aliyun.com/nexus/content/groups/public/'}
        maven { url 'https://www.jitpack.io' }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.3'
        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'
    }
}
allprojects {
    repositories {
        google()
        jcenter()
    }
}
task clean(type: Delete) {
    delete rootProject.buildDir
}
           

在Model的build.gradle配置内容如下:

apply plugin: 'com.android.application'
apply plugin: 'com.google.protobuf'
android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"
    defaultConfig {
        applicationId "com.example.myapplication"
        minSdkVersion 27
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    sourceSets {
        main {
            proto {
                srcDir 'src/main/proto'
            }
        }
    }
}

allprojects {
    repositories {
        maven { url 'https://www.jitpack.io' }
    }
}
protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:3.11.0"
    }
    plugins {
        grpc {
            artifact = 'io.grpc:protoc-gen-grpc-java:1.27.0'
        }
    }
    generateProtoTasks {
        all().each {
            task ->
                task.plugins {
//                    javalite {}
                    grpc {
                        // Options added to --grpc_out
                        option 'lite'
                    }
                }
                task.builtins {
                    remove java
                }
                task.builtins {
                    java {}
                    // Add cpp output without any option.
                    // DO NOT omit the braces if you want this builtin to be added.
//                    cpp {}
                }
        }
        all()*.plugins { grpc {} }
    }
    generatedFilesBaseDir = "$projectDir/src/generated"
    generateProtoTasks {
        all()*.plugins {
            grpc{
                setOutputSubDir 'java'
            }
        }
    }
}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
    implementation 'com.squareup.picasso:picasso:2.5.2'

    implementation 'io.grpc:grpc-okhttp:1.27.0'
    implementation 'io.grpc:grpc-protobuf:1.27.0'
    implementation 'io.grpc:grpc-stub:1.27.0'

    implementation 'com.google.protobuf:protobuf-java:3.11.0'

    implementation 'com.google.code.findbugs:jsr305:3.0.0'
    implementation 'com.google.guava:guava:20.0'
    implementation 'javax.annotation:javax.annotation-api:1.2'
}
           

以上内容完成後,gRPC插件的配置就已經成功。下一步需要建立proto檔案。

3.在src/main下建立proto檔案夾,建立目錄是根據下面代碼設定的,上面已經添加好了,如果需要修改,可自行修改目錄。

sourceSets {
    main {
        proto {
            srcDir 'src/main/proto'
        }
    }
}
           

建立後如下圖所示:

gRPC在Android中的應用、配置、生成檔案及登入實作gRPC在Android中的應用

4.在proto檔案夾下建立***.proto檔案,Studio中有個插件 Protobuf Support,系統一般會提示安裝的。不提示就自行安裝就好。proto檔案的文法請參考https://github.com/protocolbuffers/protobuf(GitHub上的知識初學者看不懂就多看幾遍,小編也是看了好些次才明白)這裡注意gRPC是使用protoc3的。

syntax = "proto3";
package com.rpc.proto.user;
option  java_package = "com.rpc.proto.user";
option  java_outer_classname = "UserProto";
option  java_multiple_files = true;
service UserRpcServer{
    rpc login(loginRequest) returns(loginResponse){}
}
message loginRequest{
    string username = 1;
    string password = 2;
    int32 power = 3;
}
message loginResponse{
    string state = 1;
}
           

以上是一個使用者的登入功能實作。編寫完成後建構項目,就會生成proto檔案。生成目錄在src/generated/debug/java下。當然這個目錄也可以在build.gradle中進行設定。

5.編寫Android用戶端代碼

public class GrpcChannel {
    public static final String SERVER_IP = "192.168.137.1";
    private static final int PORT = 8282;
    public static ManagedChannel getChannel(){
        return ManagedChannelBuilder.forAddress(SERVER_IP,PORT)
                .usePlaintext().build();
    }
}

public class UserServer {
    private UserRpcServerGrpc.UserRpcServerFutureStub futureStub;
    public UserServer(ManagedChannel channel){
        futureStub = UserRpcServerGrpc.newFutureStub(channel);
    }
    public void login(String user, String password, final myCallBack callBack){
        if ("".equals(user)||user == null||"".equals(password)||password == null)
            throw new NullPointerException("user and password no null!");
        final loginRequest request = loginRequest
                .newBuilder()
                .setUsername(user)
                .setPassword(password)
                .setPower(1)
                .build();
        ListenableFuture<loginResponse> future = futureStub.login(request);
        //建構非阻塞調用,阻塞方式用BlockingStub
        Futures.addCallback(future, new FutureCallback<loginResponse>() {
            @Override
            public void onSuccess(@NullableDecl loginResponse result) {
            	callBack.onSuccess(result.getState());
            }
            @Override
            public void onFailure(Throwable t) {
                callBack.onFailure("網絡異常!");
            }
        }, Executors.newCachedThreadPool());
    }
}
public interface myCallBack {
    void onSuccess(Object object);
    void onFailure(Object object);
}
           

到這裡用戶端的gRPC就基本完成了,需要登陸時直接調用login()方法就可以了。

服務端語言自行選擇吧,這裡就不介紹了,隻要是pb支援的語言都可以的。