1. 程式人生 > >通過例項帶你學習RPC框架gRPC

通過例項帶你學習RPC框架gRPC

宣告: CSDN原創投稿,未經許可,禁止任何形式的轉載。
作者:趙昌峻,碼農、全棧工程師,長期關注雲端計算、大資料、移動網際網路相關技術,從事IT行業十載,有5年的企業級應用開發經歷、3年的雲平臺(openstack)研發經歷,目前從事移動APP的研發,堅持每天快樂的程式設計。

什麼是gRPC?

在知乎上有這樣一個問題:誰能用通俗的語言解釋一下什麼是 RPC 框架?,各路大神講的都很到位,這裡我就不詳細解釋了,gRPC就是其中的一種RPC框架。

gRPC.png

如上圖所示,在gRPC中,客戶端應用程式可以就像呼叫本地物件方法一樣直接呼叫不同伺服器上的應用程式方法,使您更容易建立分散式應用程式和服務。與許多RPC系統一樣,gRPC基於定義服務的思想,定義可以遠端呼叫的方法,包括方法的引數和返回型別。在伺服器端,伺服器實現此介面並執行一個gRPC伺服器來處理客戶端呼叫。在客戶端,客戶端有一個“存根stub”(簡稱為某些語言的客戶端),提供與伺服器相同的方法。所有的資料傳輸都使用protobuf。

gRPC服務的定義

syntax = "proto3";

option go_package = "user";
option java_package = "com.ylifegroup.protobuf";

enum PhoneType {
    HOME = 0;
    WORK = 1;
    OTHER = 2;
}

message ProtobufUser {
  int32 id = 1;
  string name = 2;
  message Phone{
  PhoneType phoneType = 1;
  string phoneNumber = 2;
  }
  repeated Phone phones = 3
; } message AddPhoneToUserRequest{ int32 uid = 1; PhoneType phoneType = 2; string phoneNumber = 3; } message AddPhoneToUserResponse{ bool result = 1; } service PhoneService { rpc addPhoneToUser(AddPhoneToUserRequest) returns (AddPhoneToUserResponse); }

增加的內容很簡單,定義了一個關於電話本的服務PhoneBookService,服務包括一個把電話增加到指定聯絡人的方法addPhoneToUser,同時定義了方法的引數AddPhoneToUserRequest和返回值AddPhoneToUserResponse。

gRPC服務的伺服器端實現

為了方便我們後面講解微服務架構,從本節開始我們使用Java作為我們的開發語言。

我們的Demo將使用Gradle自動化構建工具,這裡不會深入講解Gradle的用法,建議沒有使用過Gradle的可以從官方網站開始:https://gradle.org

可以使用下面的命令生成Java專案的基本結構:

mkdir gRPCDemo
cd gRPCDemo
gradle init --type=java-library

初始化後,目錄結構如下所示:

└── gRPCDemo
    ├── build.gradle
    ├── gradle
    │   └── wrapper
    │       ├── gradle-wrapper.jar
    │       └── gradle-wrapper.properties
    ├── gradlew
    ├── gradlew.bat
    ├── settings.gradle
    └── src
        ├── main
        │   └── java
        │       └── Library.java
        └── test
            └── java
                └── LibraryTest.java

先刪除沒用的Library.javaLibraryTest.java

然後修改配置檔案build.gradle,如下所示:

/*
 * This build file was auto generated by running the Gradle 'init' task
 * by 'ChangjunZhao' at '16-12-27 下午3:25' with Gradle 3.1
 *
 * This generated file contains a sample Java project to get you started.
 * For more details take a look at the Java Quickstart chapter in the Gradle
 * user guide available at https://docs.gradle.org/3.1/userguide/tutorial_java_projects.html
 */

// Apply the java plugin to add support for Java
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'com.google.protobuf'

repositories {
    // Use 'jcenter' for resolving your dependencies.
    // You can declare any Maven/Ivy/file repository here.
    jcenter()
}

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.0'
    }
}

sourceSets {
    main {
        java{
            srcDir 'gen/main/java'
            srcDir 'gen/main/grpc'
        }
        proto {
            srcDir 'src/main/proto'
        }
    }
}

jar {
    from {
        configurations.runtime.collect {
            it.isDirectory() ? it : zipTree(it)
        }
        configurations.compile.collect {
            it.isDirectory() ? it : zipTree(it)
        }
    }
    manifest {
        attributes 'Main-Class': 'com.ylifegroup.protobuf.server.GRpcServer'
    }
}

protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:3.1.0"
    }
    plugins {
        grpc {
            artifact = 'io.grpc:protoc-gen-grpc-java:1.0.3'
        }
    }
    generatedFilesBaseDir = "$projectDir/gen/"
    generateProtoTasks {
        all()*.plugins {
            grpc {}
        }
    }
}

dependencies {
    compile 'io.grpc:grpc-netty:1.0.3'
    compile 'io.grpc:grpc-protobuf:1.0.3'
    compile 'io.grpc:grpc-stub:1.0.3'
    // The production code uses the SLF4J logging API at compile time
    compile 'org.slf4j:slf4j-api:1.7.21'

    // Declare the dependency for your favourite test framework you want to use in your tests.
    // TestNG is also supported by the Gradle Test task. Just change the
    // testCompile dependency to testCompile 'org.testng:testng:6.8.1' and add
    // 'test.useTestNG()' to your build script.
    testCompile 'junit:junit:4.12'
}

eclipse {
  classpath {
    defaultOutputDir = file('build/eclipse/bin')
  }
}

clean {
    delete protobuf.generatedFilesBaseDir
}

我們使用了grpc官方提供的gradle外掛com.google.protobuf,它可是相當牛B,可以幫我們自動生成grpc相關的程式碼。

為了完成伺服器端的開發,增加了grpc相關的三個依賴:

io.grpc:grpc-netty:1.0.3
io.grpc:grpc-protobuf:1.0.3
io.grpc:grpc-stub:1.0.3

在protobuf的配置部分,我們使用grpc程式碼生成的外掛,以便自動幫我們生成grpc伺服器端的程式碼(介面),具體的實現還需要我們自己去搞定。

在原始檔配置部分sourceSets,我們指定了proto檔案的目錄src/main/proto,同時把protobuf自動生成程式碼所在兩個目錄(預設)gen/main/java,gen/main/grpc增加到Java的原始檔目錄,這樣在編譯的時候才會編譯這兩個目錄下的Java類。

OK,基本配置就這些,把上一節的gRPC服務定義檔案phonebook.proto放到我們專案的src/main/proto目錄(如果不存在自己手動建立)下,執行如下命令:

./gradlew build

gradle會幫你自動生了protobuf和grpc相關的檔案到如下目錄:

├── gen
│   └── main
│       ├── grpc
│       │   └── com
│       │       └── ylifegroup
│       │           └── protobuf
│       │               └── PhoneServiceGrpc.java
│       └── java
│           └── com
│               └── ylifegroup
│                   └── protobuf
│                       └── Phonebook.java

OK,剩下的工作大部分需要手動完成了,執行如下命令,生成Eclipse相關的配置檔案:

./gradlew eclipse

然後用Eclipse開啟gRPCDemo專案。

我們新建三個package:

com.ylifegroup.protobuf.service //用於放gRPC服務的實現類
com.ylifegroup.protobuf.server //用於放gRPC伺服器的實現類
com.ylifegroup.protobuf.client //用於放gRPC客戶端demo的相關類。

首先我們需要寫程式碼來實現將電話增加到使用者的邏輯,我們在com.ylifegroup.protobuf.service包下新建一個類叫PhoneServiceImp,它只需要繼承protoc-gen-grpc外掛幫我們自己生成的一個grpc實現類PhoneServiceGrpc.PhoneServiceImplBase,並實現相關方法即可,所有程式碼如下所示:

package com.ylifegroup.protobuf.service;

import com.ylifegroup.protobuf.PhoneServiceGrpc;
import com.ylifegroup.protobuf.Phonebook.AddPhoneToUserRequest;
import com.ylifegroup.protobuf.Phonebook.AddPhoneToUserResponse;

import io.grpc.stub.StreamObserver;

public class PhoneServiceImp extends PhoneServiceGrpc.PhoneServiceImplBase{

    @Override
    public void addPhoneToUser(AddPhoneToUserRequest request, StreamObserver<AddPhoneToUserResponse> responseObserver) {
        // TODO Auto-generated method stub
        AddPhoneToUserResponse response = null;
        if(request.getPhoneNumber().length() == 11 ){
            System.out.printf("uid = %s , phone type is %s, nubmer is %s\n", request.getUid(), request.getPhoneType(), request.getPhoneNumber());
            response = AddPhoneToUserResponse.newBuilder().setResult(true).build();
        }else{
            System.out.printf("The phone nubmer %s is wrong!\n",request.getPhoneNumber());
            response = AddPhoneToUserResponse.newBuilder().setResult(false).build();
        }
        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }

}

程式碼很簡單,我們只是檢查手機號是不是11位,如果是把客戶端的請求引數打印出來,給客戶端返回true,如果不是11位,提示手機號錯誤,給客戶端返回false。

程式碼很簡單,這裡就不詳細解釋了。

接下來,我們需要將我們實現的服務釋出成grpc服務,這裡有很多實現方法,我們就使用grpc官方提供的用netty實現伺服器程式碼,還記得在gradle的配置檔案裡我們增加了io.grpc:grpc-netty:1.0.3依賴嗎?就是用在這的。

在com.ylifegroup.protobuf.server包下新建GRpcServer類,程式碼如下所示(大部分程式碼參考grpc官方的helloworld):

package com.ylifegroup.protobuf.server;

import io.grpc.Server;
import io.grpc.ServerBuilder;
import java.io.IOException;
import java.util.logging.Logger;
import com.ylifegroup.protobuf.service.PhoneServiceImp;

public class GRpcServer {
    private static final Logger logger = Logger.getLogger(GRpcServer.class.getName());

    private Server server;

    private void start() throws IOException {
        /* The port on which the server should run */
        int port = 50051;
        server = ServerBuilder
                .forPort(port)
                .addService(new PhoneServiceImp())
                .build()
                .start();
        logger.info("Server started, listening on " + port);
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                System.err.println("*** shutting down gRPC server since JVM is shutting down");
                GRpcServer.this.stop();
                System.err.println("*** server shut down");
            }
        });
    }

    private void stop() {
        if (server != null) {
            server.shutdown();
        }
    }

    /**
     * Await termination on the main thread since the grpc library uses daemon
     * threads.
     */
    private void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
            server.awaitTermination();
        }
    }

    /**
     * Main launches the server from the command line.
     */
    public static void main(String[] args) throws IOException, InterruptedException {
        final GRpcServer server = new GRpcServer();
        server.start();
        server.blockUntilShutdown();
    }
}

詳細的我就不解釋了,一看程式碼就能明白,只需要把我們的電話本服務PhoneServiceImp增加到grpc伺服器,然後就能對外提供電話本的服務了。

gRPC服務的客戶端實現

客戶端的實現也很簡單,在com.ylifegroup.protobuf.client包下新建GRpcClient類,程式碼如下:

package com.ylifegroup.protobuf.client;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;

import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.ylifegroup.protobuf.PhoneServiceGrpc;
import com.ylifegroup.protobuf.Phonebook.AddPhoneToUserRequest;
import com.ylifegroup.protobuf.Phonebook.AddPhoneToUserResponse;
import com.ylifegroup.protobuf.Phonebook.PhoneType;

public class GRpcClient {

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

    private final ManagedChannel channel;

    private final PhoneServiceGrpc.PhoneServiceBlockingStub blockingStub;

    /** Construct client connecting to gRPC server at {@code host:port}. */
    public GRpcClient(String host, int port) {
        ManagedChannelBuilder<?> channelBuilder = ManagedChannelBuilder.forAddress(host, port).usePlaintext(true);
        channel = channelBuilder.build();
        blockingStub = PhoneServiceGrpc.newBlockingStub(channel);
    }

    public void shutdown() throws InterruptedException {
        channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
    }

    /** add phone to user. */
    public void addPhoneToUser(int uid, PhoneType phoneType, String phoneNubmer) {
        logger.info("Will try to add phone to user " + uid);
        AddPhoneToUserRequest request = AddPhoneToUserRequest.newBuilder().setUid(uid).setPhoneType(phoneType)
                .setPhoneNumber(phoneNubmer).build();
        AddPhoneToUserResponse response;
        try {
            response = blockingStub.addPhoneToUser(request);
        } catch (StatusRuntimeException e) {
            logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
            return;
        }
        logger.info("Result: " + response.getResult());
    }

    public static void main(String[] args) throws Exception {
        GRpcClient client = new GRpcClient("localhost", 50051);
        try {
            client.addPhoneToUser(1, PhoneType.WORK, "13888888888");
        } finally {
            client.shutdown();
        }
    }
}

程式碼也很簡單,自己看吧,總結下來呼叫gRPC服務包括以下幾步:

  • 指定伺服器端的ip和埠,構建一個ManagedChannel。
  • 構造gRPC請求相關引數。
  • 建立blockingStub(回到開始去看看那張圖,就更好理解stub了)來呼叫相關的遠端方法。
  • 接收伺服器端返回結果。

這其中大部分的程式碼protoc-gen-grpc都已經幫您生成,使用起來很方便。

讓它跑起來

我們在專案根目錄執行如下命令:

./gradlew jar

會在build/libs/目錄下生成一個可直接執行的jar包:gRPCDemo.jar。

我們執行如下命令即可啟動我們的gRPC服務:

java -jar build/libs/gRPCDemo.jar

如下圖所示:

gRPC_server.png

我們在eclipse中執行GRpcClient類,您將看到:

phone_service_true.png

看到了嗎?伺服器端返回”true”。

把程式碼中的8去掉一個,再次執行,如下圖所示:

phone_service_false.png

伺服器端返回了false,說明我們的服務還是靠譜的,哈哈。

這只是我們的第一個grpc版本,以方便你更好的理解grpc,後面我將結合spring boot講解微服務架構,那就更牛B了。

相關推薦

通過例項學習RPC框架gRPC

宣告: CSDN原創投稿,未經許可,禁止任何形式的轉載。 作者:趙昌峻,碼農、全棧工程師,長期關注雲端計算、大資料、移動網際網路相關技術,從事IT行業十載,有5年的企業級應用開發經歷、3年的雲平臺(openstack)研發經歷,目前從事移動APP的研發,

微信小程式不支援圖表工具,通過例項瞭解繪製方案

作者:musiq1989,前端開發工程師,專注於前端技術研究和內容分享,Github地址:https://github.com/xiaolin3303。 責編:陳秋歌,關注微信開發等領域,尋求報道或者投稿請發郵件至chenqg#csdn.net。 歡

一文實現RPC框架

想要獲取更多文章可以訪問我的部落格 - 程式碼無止境。 現在大部分的網際網路公司都會採用微服務架構,但具體實現微服務架構的方式有所不同,主流上分為兩種,一種是基於Http協議的遠端呼叫,另外一種是基於RPC方式的呼叫。兩種方式都有自己的代表框架,前者是著名的Spring Cloud,後者則是有阿里巴巴開源

5分鐘學習瀏覽器8大數據存儲技術

clas 上大 獲取 message logs 發送請求 我們 不兼容 class 瀏覽器的緩存機制 HTTP文件緩存、LocalStorage、 sessionStorage、cookie、indexDB、webSQL 、CatheStorage、Applicatio

CI框架基本配置/教學習CI框架codelgniter

for 框架 base_url sys 就是 body 用戶手冊 我想 應該 CI框架現在中國可以說還是不成熟,不像thinkphp那樣有那麽多的中文手冊,在國內,很多國人英語都很爛,CI現在教程還是不多。大家心裏都存在這嚴重想法 CI 框架現在中國可以說還是不成熟,不像

學習多線程編程

線程 多線程 進程 Linux 線程概念 定義 線程就是進程內部的執行流,一個進程至少有一個線程,線程擁有自己的私有資源同時也會和進程共享資源。 線程獨有的資源 線程描述符 寄存器 線程棧 errno 信號掩碼 實時調度策略 線程和進程共享的資源 全局變量 堆 代碼段 文件描述符表 進程

螞蟻技術專家:一篇文章學習分布式事務

自己實現 鎖定 ons 快速 事務管理器 獲取 currency 定性 能力 小螞蟻說: 分布式事務是企業集成中的一個技術難點,也是每一個分布式系統架構中都會涉及到的一個東西,特別是在這幾年越來越火的微服務架構中,幾乎可以說是無法避免,本文就圍繞分布式事務各方面與大家進行介

循序漸進學習時間複雜度和空間複雜度。

本文字數:4894 字 閱讀本文大概需要:13 分鐘   寫在之前  我們都知道,對於同一個問題來說,可以有多種解決問題的演算法。儘管演算法不是唯一的,但是對於問題本身來說相對好的演算法還是存在的,這裡可能有人會問區分好壞的標準是什

循序漸進學習時間復雜度和空間復雜度。

準備 日常 數據結構 media 輸入數據 有時 學習過程 幫助 和平 本文字數:4894 字 閱讀本文大概需要:13 分鐘 寫在之前 我們都知道,對於同一個問題來說,可以有多種解決問題的算法。盡管算法不是唯一的,但是對於問題本身來說相對好的算法還是存在的,這

【javascript實現】幾道題目學習二叉搜尋樹

二叉樹大家都知道,二叉搜尋樹滿足以下特徵: 節點的左子樹只包含小於當前節點的數 節點的右子樹只包含大於當前節點的數 所有左子樹和右子樹自身必須也是二叉搜尋樹 二叉搜尋樹也叫二叉排序樹,中序遍歷二叉搜尋樹的結果就是一次遞增的遍歷。 一、二叉搜尋樹的建立 相關題目:lee

Spring Security開發安全的REST服務 大神學習後端安全開發實戰分享

===============課程目錄=============== ├1-1+導學.mp4 ├2-1 開發環境安裝.mp4 ├2-2 程式碼結構介紹.mp4 ├2-3 Hello Spring Security.mp4 ├3-1+Restful簡介.mp4 ├3-10 使用多執行緒提高REST服務

阿里老司機使用Spring框架快速搭建Web工程專案

演講嘉賓簡介 呂德慶(花名:嵛山), 阿里巴巴高階開發工程師,武漢大學地信碩士,有豐富的系統開發經驗,目前就職於阿里巴巴程式碼中心團隊,負責後端開發。 本文首先將介紹Spring框架的相關概念,其次將藉助Spring Web示例工程帶大家學習如何快速開發Spring Web應用。 一、Sprin

Ted 學習資料結構 之 二叉堆(Binary Heap)

二叉堆(Binary Heap) (1)structure property Heap(堆)是一個除了底層節點外的完全填滿的二叉樹,底層可以不完全,左到右填充節點。(a heap is a binar

google開源RPC框架gRPC:安裝

gRPC是一個有google最先開發的RPC(Remote Procedure Call:遠端過程呼叫)框架,並在15年初開源。是一個比較新的東西,因為工作需要進行了一些研究,這裡做一個記錄。 RPC的一些內容 RPC 的全稱是 Remote Procedure Call

五個小案例學習火熱的Vue.js

vue.js背後的理念是提供儘可能簡單的API,在檢視(HTML)和模型(javascript物件)建立實時的雙向繫結機制。正如你在下面的例子中所看到的,這個框架使用起來非常的高效且不影響任何功能。 開始起步 安裝Vue.js最簡單的方式是用一個<script>

三個例項走進web過濾器Filter API

web過濾器   過濾器是指攔截請求,並對傳給被請求資源的ServletRequest或ServletResponse進行處理的一個物件。 過濾器可用於登入、加密和解密、對話檢查、圖片轉換等待。過濾器可以配置攔截一個或者多個資源 1.Filter API 過濾器必須實

高效傳輸資料格式以及基於HTTP2的RPC框架---gRPC的使用

ProtoBuffer的介紹 google有一款非常高效的資料傳輸格式框架ProtoBuffer。在java中使用protobuffer作為序列化效率比jdk自身的serializable介面效率高的多(github上有個對於序列號效能的研究https://g

例項瞭解Retrofit 2.0的使用,分享目前開發Retrofit遇到的坑和心得

新增依賴 app/build.gradle 1 compile 'com.squareup.retrofit2:retrofit:2.0.0-beta3' 介面呼叫 1 2 3 4 5 6 Retrofit retrofit = new Retr

rpc框架--grpc-java

grpc-java 初探 環境準備 下載protobuf 新建maven專案 使用protoc命令列生成類檔案 安裝protocbuf 安裝步驟略 編譯 protoc-gen-grpc-java外掛 下載grpc-jav

不會框架不要緊,我自定義框架

不會框架不要緊,我帶你自定義框架 前言:這標題說的有點大了,當一回標題黨,之前在學JSP的時候提到了JSTL和EL表示式,由於一直鍾情於Servlet,遲遲沒有更新別的,這回算是跳出來了。這回放個大招,用Spring+SpringMVC+Spring Jdbc Template,實現一個增刪改查加分頁,但重