高性能RPC框架gRPC竟恐怖如斯~

来源:https://www.cnblogs.com/Chenjiabing/archive/2022/09/02/16650341.html
-Advertisement-
Play Games

大家好,我是不才陳某~ RPC、gRPC、Thrift、HTTP,大家知道它們之間的聯繫和區別麽?這些都是面試常考的問題,今天帶大家先搞懂 RPC 和 gRPC。 在講述 gRPC 之前,我們需要先搞懂什麼是 RPC。 不 BB,直接上文章目錄: 什麼是 RPC ? RPC(Remote Proce ...


大家好,我是不才陳某~

RPC、gRPC、Thrift、HTTP,大家知道它們之間的聯繫和區別麽?這些都是面試常考的問題,今天帶大家先搞懂 RPC 和 gRPC。

在講述 gRPC 之前,我們需要先搞懂什麼是 RPC。

不 BB,直接上文章目錄:

什麼是 RPC ?

RPC(Remote Procedure Call Protocol)遠程過程調用協議,目標就是讓遠程服務調用更加簡單、透明。

RPC 框架負責屏蔽底層的傳輸方式(TCP 或者 UDP)、序列化方式(XML/Json/ 二進位)和通信細節,服務調用者可以像調用本地介面一樣調用遠程的服務提供者,而不需要關心底層通信細節和調用過程。

為什麼要用 RPC ?

當我們的業務越來越多、應用也越來越多時,自然的,我們會發現有些功能已經不能簡單劃分開來或者劃分不出來。

此時可以將公共業務邏輯抽離出來,將之組成獨立的服務 Service 應用,而原有的、新增的應用都可以與那些獨立的 Service 應用 交互,以此來完成完整的業務功能。

所以我們急需一種高效的應用程式之間的通訊手段來完成這種需求,RPC 大顯身手的時候來了!

常用的 RPC 框架

  • gRPC:一開始由 google 開發,是一款語言中立、平臺中立、開源的遠程過程調用(RPC)系統。
  • Thrift:thrift 是一個軟體框架,用來進行可擴展且跨語言的服務的開發。它結合了功能強大的軟體堆棧和代碼生成引擎,以構建在 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 這些編程語言間無縫結合的、高效的服務。
  • Dubbo:Dubbo 是一個分散式服務框架,以及 SOA 治理方案,Dubbo自2011年開源後,已被許多非阿裡系公司使用。
  • Spring Cloud:Spring Cloud 由眾多子項目組成,如 Spring Cloud Config、Spring Cloud Netflix、Spring Cloud Consul 等,提供了搭建分散式系統及微服務常用的工具。

RPC 的調用流程

要讓網路通信細節對使用者透明,我們需要對通信細節進行封裝,我們先看下一個 RPC 調用的流程涉及到哪些通信細節:

  1. 服務消費方(client)調用以本地調用方式調用服務;
  2. client stub接收到調用後負責將方法、參數等組裝成能夠進行網路傳輸的消息體;
  3. client stub找到服務地址,並將消息發送到服務端;
  4. server stub收到消息後進行解碼;
  5. server stub根據解碼結果調用本地的服務;
  6. 本地服務執行並將結果返回給 server stub;
  7. server stub將返回結果打包成消息併發送至消費方;
  8. client stub接收到消息,併進行解碼;
  9. 服務消費方得到最終結果。

RPC 的目標就是要 2~8 這些步驟都封裝起來,讓用戶對這些細節透明,下麵是網上的另外一幅圖,感覺一目瞭然:

什麼是 gRPC ?

gRPC 是一個高性能、通用的開源 RPC 框架,其由 Google 2015 年主要面向移動應用開發並基於 HTTP/2 協議標準而設計,基於 ProtoBuf 序列化協議開發,且支持眾多開發語言。

由於是開源框架,通信的雙方可以進行二次開發,所以客戶端和伺服器端之間的通信會更加專註於業務層面的內容,減少了對由 gRPC 框架實現的底層通信的關註。

如下圖,DATA 部分即業務層面內容,下麵所有的信息都由 gRPC 進行封裝。

gRPC 的特點

  • 跨語言使用,支持 C++、Java、Go、Python、Ruby、C#、Node.js、Android Java、Objective-C、PHP 等編程語言;
  • 基於 IDL 文件定義服務,通過 proto3 工具生成指定語言的數據結構、服務端介面以及客戶端 Stub;
  • 通信協議基於標準的 HTTP/2 設計,支持雙向流、消息頭壓縮、單 TCP 的多路復用、服務端推送等特性,這些特性使得 gRPC 在移動端設備上更加省電和節省網路流量;
  • 序列化支持 PB(Protocol Buffer)和 JSON,PB 是一種語言無關的高性能序列化框架,基於 HTTP/2 + PB, 保障了 RPC 調用的高性能;
  • 安裝簡單,擴展方便(用該框架每秒可達到百萬個RPC)。

gRPC 交互過程

  • 交換機在開啟 gRPC 功能後充當 gRPC 客戶端的角色,採集伺服器充當 gRPC 伺服器角色;
  • 交換機會根據訂閱的事件構建對應數據的格式(GPB/JSON),通過 Protocol Buffers 進行編寫 proto 文件,交換機與伺服器建立 gRPC 通道,通過 gRPC 協議向伺服器發送請求消息;
  • 伺服器收到請求消息後,伺服器會通過 Protocol Buffers 解譯 proto 文件,還原出最先定義好格式的數據結構,進行業務處理;
  • 數據處理完後,伺服器需要使用 Protocol Buffers 重編譯應答數據,通過 gRPC 協議向交換機發送應答消息;
  • 交換機收到應答消息後,結束本次的 gRPC 交互。

簡單地說,gRPC 就是在客戶端和伺服器端開啟 gRPC 功能後建立連接,將設備上配置的訂閱數據推送給伺服器端。

我們可以看到整個過程是需要用到 Protocol Buffers 將所需要處理數據的結構化數據在 proto 文件中進行定義。

Protocol Buffers

你可以理解 ProtoBuf 是一種更加靈活、高效的數據格式,與 XML、JSON 類似,在一些高性能且對響應速度有要求的數據傳輸場景非常適用。

ProtoBuf 在 gRPC 的框架中主要有三個作用:定義數據結構、定義服務介面,通過序列化和反序列化方式提升傳輸效率。

為什麼 ProtoBuf 會提高傳輸效率呢?

我們知道使用 XML、JSON 進行數據編譯時,數據文本格式更容易閱讀,但進行數據交換時,設備就需要耗費大量的 CPU 在 I/O 動作上,自然會影響整個傳輸速率。

Protocol Buffers 不像前者,它會將字元串進行序列化後再進行傳輸,即二進位數據

可以看到其實兩者內容相差不大,並且內容非常直觀,但是 Protocol Buffers 編碼的內容只是提供給操作者閱讀的,實際上傳輸的並不會以這種文本形式,而是序列化後的二進位數據,位元組數會比 JSON、XML 的位元組數少很多,速率更快。

gPRC 如何支撐跨平臺,多語言呢 ?

Protocol Buffers 自帶一個編譯器也是一個優勢點,前面提到的 proto 文件就是通過編譯器進行編譯的,proto 文件需要編譯生成一個類似庫文件,基於庫文件才能真正開發數據應用。

具體用什麼編程語言編譯生成這個庫文件呢?由於現網中負責網路設備和伺服器設備的運維人員往往不是同一組人,運維人員可能會習慣使用不同的編程語言進行運維開發,那麼 Protocol Buffers 其中一個優勢就能發揮出來——跨語言。

從上面的介紹,我們得出在編碼方面 Protocol Buffers 對比 JSON、XML 的優點:

  • 標準的 IDL 和 IDL 編譯器,這使得其對工程師非常友好;
  • 序列化數據非常簡潔,緊湊,與 XML 相比,其序列化之後的數據量約為 1/3 到 1/10;
  • 解析速度非常快,比對應的 XML 快約 20-100 倍;
  • 提供了非常友好的動態庫,使用非常簡單,反序列化只需要一行代碼。

Protobuf 也有其局限性:

  • 由於 Protobuf 產生於 Google,所以目前其僅支持 Java、C++、Python 三種語言
  • Protobuf 支持的數據類型相對較少,不支持常量類型;
  • 由於其設計的理念是純粹的展現層協議(Presentation Layer),目前並沒有一個專門支持 Protobuf 的 RPC 框架。

Protobuf 適用場景:

  • Protobuf 具有廣泛的用戶基礎,空間開銷小以及高解析性能是其亮點,非常適合於公司內部的對性能要求高的 RPC 調用
  • 由於 Protobuf 提供了標準的 IDL 以及對應的編譯器,其 IDL 文件是參與各方的非常強的業務約束;
  • Protobuf 與傳輸層無關,採用 HTTP 具有良好的跨防火牆的訪問屬性,所以 Protobuf 也適用於公司間對性能要求比較高的場景;
  • 由於其解析性能高,序列化後數據量相對少,非常適合應用層對象的持久化場景;
  • 主要問題在於其所支持的語言相對較少,另外由於沒有綁定的標準底層傳輸層協議,在公司間進行傳輸層協議的調試工作相對麻煩。

基於 HTTP 2.0 標準設計

除了 Protocol Buffers 之外,從交互圖中和分層框架可以看到, gRPC 還有另外一個優勢——它是基於 HTTP 2.0 協議的。

由於 gRPC 基於 HTTP 2.0 標準設計,帶來了更多強大功能,如多路復用、二進位幀、頭部壓縮、推送機制。

這些功能給設備帶來重大益處,如節省帶寬、降低 TCP 連接次數、節省 CPU 使用等,gRPC 既能夠在客戶端應用,也能夠在伺服器端應用,從而以透明的方式實現兩端的通信和簡化通信系統的構建。

HTTP 1.X 定義了四種與伺服器交互的方式,分別為 GET、POST、PUT、DELETE,這些在 HTTP 2.0 中均保留,我們看看 HTTP 2.0 的新特性:雙向流、多路復用、二進位幀、頭部壓縮。

性能對比

與採用文本格式的 JSON 相比,採用二進位格式的 protobuf 在速度上可以達到前者的 5 倍!

Auth0 網站所做的性能測試結果顯示,protobuf 和 JSON 的優勢差異在 Java、Python 等環境中尤為明顯,下圖是 Auth0 在兩個 Spring Boot 應用程式間所做的對比測試結果。

結果顯示,protobuf 所需的請求時間最多只有 JSON 的 20% 左右,即速度是其 5 倍!

下麵看一下性能和空間開銷對比。

從上圖可得出如下結論:

  • XML序列化(Xstream)無論在性能和簡潔性上比較差。
  • Thrift 與 Protobuf 相比在時空開銷方面都有一定的劣勢。
  • Protobuf 和 Avro 在兩方面表現都非常優越。

gRPC 實戰

1. 項目結構

我們先看一下項目結構:

2. 生成 protobuf 文件

文件 helloworld.proto:

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

這裡提供了一個 SayHello() 方法,然後入參為 HelloRequest,返回值為 HelloReply,可以看到 proto 文件只定義了入參和返回值的格式,以及調用的介面,至於介面內部的實現,該文件完全不用關心。

文件 pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>rpc-study</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>grpc-demo</artifactId>

    <dependencies>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty-shaded</artifactId>
            <version>1.14.0</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>1.14.0</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>1.14.0</version>
        </dependency>
    </dependencies>

    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.5.0.Final</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.5.1</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:3.5.1-1:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.14.0:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>6</source>
                    <target>6</target>
                </configuration>
            </plugin>

        </plugins>
    </build>
</project>

這裡面的 build 其實是為了安裝 protobuf 插件,裡面其實有 2 個插件我們需要用到,分別為 protobuf:compile 和 protobuf:compile-javanano,當我們直接執行時,會生成左側文件,其中 GreeterGrpc 提供調用介面,Hello 開頭的文件功能主要是對數據進行序列化,然後處理入參和返回值。

可能有同學會問,你把文件生成到 target 中,我想放到 main.src 中,你可以把這些文件 copy 出來,或者也可以通過工具生成:

3. 服務端和客戶端

文件 HelloWorldClient.java:

public class HelloWorldClient {
    private final ManagedChannel channel;
    private final GreeterGrpc.GreeterBlockingStub blockingStub;
    private static final Logger logger = Logger.getLogger(HelloWorldClient.class.getName());

    public HelloWorldClient(String host,int port){
        channel = ManagedChannelBuilder.forAddress(host,port)
                .usePlaintext(true)
                .build();

        blockingStub = GreeterGrpc.newBlockingStub(channel);
    }


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

    public  void greet(String name){
        HelloRequest request = HelloRequest.newBuilder().setName(name).build();
        HelloReply response;
        try{
            response = blockingStub.sayHello(request);
        } catch (StatusRuntimeException e)
        {
            logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
            return;
        }
        logger.info("Message from gRPC-Server: "+response.getMessage());
    }

    public static void main(String[] args) throws InterruptedException {
        HelloWorldClient client = new HelloWorldClient("127.0.0.1",50051);
        try{
            String user = "world";
            if (args.length > 0){
                user = args[0];
            }
            client.greet(user);
        }finally {
            client.shutdown();
        }
    }
}

這個太簡單了,就是連接服務埠,調用 sayHello() 方法。

文件 HelloWorldServer.java:

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

    private int port = 50051;
    private Server server;

    private void start() throws IOException {
        server = ServerBuilder.forPort(port)
                .addService(new GreeterImpl())
                .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");
                HelloWorldServer.this.stop();
                System.err.println("*** server shut down");
            }
        });
    }

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

    // block 一直到退出程式
    private void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
            server.awaitTermination();
        }
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        final HelloWorldServer server = new HelloWorldServer();
        server.start();
        server.blockUntilShutdown();
    }

    // 實現 定義一個實現服務介面的類
    private class GreeterImpl extends GreeterGrpc.GreeterImplBase {
        @Override
        public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
            HelloReply reply = HelloReply.newBuilder().setMessage(("Hello " + req.getName())).build();
            responseObserver.onNext(reply);
            responseObserver.onCompleted();
            System.out.println("Message from gRPC-Client:" + req.getName());
            System.out.println("Message Response:" + reply.getMessage());
        }
    }
}

主要是實現 sayHello() 方法,裡面對數據進行了簡單處理,入參為 “W orld”,返回的是 “Hello World”。

4. 啟動服務

先啟動 Server,返回如下:

再啟動 Client,返回如下:

同時 Server返回如下:

源碼地址:https://github.com/lml200701158/rpc-study

寫在最後

這篇文章其實是我去年寫的,這次是重新整理,文章詳細講解了 RPC 和 gRPC,以及 gRPC 的應用示例,非常全面,後面會再把 Thrift 整理出來。

這個 Demo 看起來很簡單,我 TM 居然搞了大半天,一開始是因為不知道需要執行 2 個不同的插件來生成 protobuf,以為只需要點擊 protobuf:compile 就可以,結果發現 protobuf:compile-javanano 也需要點一下。

如果覺得作者寫的好,有所收穫的話,點個關註,推薦一波,文章首發於公眾號!!!
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 坦克大戰【2】 筆記目錄:(https://www.cnblogs.com/wenjie2000/p/16378441.html) 線程-應用到坦克大戰 坦克大戰0.3版 陸游曾說:紙上得來總覺淺,絕知此事要躬行。前面我要已經瞭解java線程基本知識,現在我們來實際運用一下。 在坦克大戰游戲(0.2 ...
  • 相信很多剛開始使用pycharm不太熟練的小伙伴,每天一開機打開pycharm總是卡半天,不知道的還以為是電腦卡了或者啥問題的。 莫慌,其實並不是… 今天我們就來解決一下這個問題 大致總結了以下這幾種方法 1、exclude不必要文件 依次打開 file(文件) → project:administ ...
  • Django_響應對象 響應對象 響應對象有三種形式: HttpResponse() render() Redirect() (1) HttpResponse() django伺服器接收到客戶端發來的請求之後,會將提交上來的數據封裝成一個HttpResponse對象傳給視圖函數。視圖函數在處理完相關 ...
  • 前文再續,之前一篇我們已經配置好了資料庫以及模板引擎,現在可以在邏輯層編寫具體業務代碼了,博客平臺和大多數線上平臺一樣,都是基於用戶賬號體系來進行操作,所以我們需要針對用戶表完成用戶賬號的CURD操作。 用戶後臺模板 首先用戶操作邏輯主要在後臺展現,所以模板應該單獨生成admin文件夾,和前臺模板進 ...
  • 資料庫說明文檔,在我們開發項目時是非常必要的,有時項目交付時,客戶也是需要讓我們提供的,而如果人工編寫,比如耗時,通過screw組件來生成文檔,非常方便。 源代碼和使用:https://github.com/pig-mesh/screw 添加依賴 <dependency> <groupId>cn.s ...
  • 《Python灰帽子》PDF高清版免費下載地址(點擊即可) 內容簡介 · · · · · · 《Python灰帽子》是由知名安全機構Immunity Inc的資深黑帽Justin Seitz主筆撰寫的一本關於編程語言Python如何被廣泛應用於黑客與逆向工程領域的書籍。老牌黑客,同時也是Immuni ...
  • 前言 😋 大家早好、午好、晚好吖~ 開發環境: 解釋器版本: python 3.8 代碼編輯器: pycharm 2021.2 requests: pip install requests pandas: pip install pandas pyecharts: pip install pyec ...
  • 摘要:本文主要講解灰度線性變換,基礎性知識希望對您有所幫助。 本文分享自華為雲社區《[Python圖像處理] 十六.圖像的灰度非線性變換之對數變換、伽馬變換》,作者:eastmount 。 本篇文章主要講解非線性變換,使用自定義方法對圖像進行灰度化處理,包括對數變換和伽馬變換。 一.圖像灰度非線性變 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...