一言不合就改用 gRPC?要我大前端怎麼配合啊!

nchuuu
nchuuu
Nov 8 · 7 min read
Photo by FuYong Hua on Unsplash

— grpc-web 串接步驟及開發經驗分享。

前言

前陣子因為外包團隊開發的 WebSocket 極度不穩定,主管決定在內部先嘗試將服務替換為 grpc 的解決方案。身為前端工程師的我,本來也很期待這次的技術嘗試,grpc 又是 Google 所主持的 Project,開發體驗及可靠性應該很高吧!然而實際串接後,我有了不一樣的想法…。我們還是先從技術的部分開始吧!

gRPC

gRPC is a modern open source high performance RPC framework that can run in any environment.

簡單來說 gRPC 是 Google 製作的 RPC 框架,與我們一般常串接的 REST API 有些差別。本篇將不細究其中差異,直接以前端的角度分享如何建立及串接 gRPC 服務。而我們建置的步驟主要依賴官方文件,以及針對 web 端開發的 grpc-web 套件。

首先 gRPC 有個最重要的定義檔 .proto,它的功能類似於我們在使用 npm 進行套件管理的 package.json,也就是告訴 npm 該去下載什麼版本的套件;而 .proto 則是定義了你的 gRPC 會有什麼樣的功能,並透過 protocol buffer compiler (protoc)進行編譯為程式語言。

message EchoRequest {
string message = 1;
}

message EchoResponse {
string message = 1;
}

service EchoService {
rpc Echo(EchoRequest) returns (EchoResponse);
}

上面的例子是一個呼叫訊息的 .proto ,這份檔案對前後端來說是共用的,後端要將 .proto 編譯為 Java, Golang, etc. 來提供服務;而前端當然是編譯成 Javascript ,再搭配 grpc-web 來串接囉。

不過定義檔多是靠後端提供,必竟他們才是服務的提供者,前端同學們只需要每天催他們,把 .proto 給我交出來啦!😆😆😆

grpc-web

有了上述的背景之後,我們就來看看文件的說明吧。首先第一步當然就是安裝 grpc-web 套件在你的專案之中。以下提供官方範例的 package.json

{
"name": "grpc-web-commonjs-example",
"dependencies": {
"google-protobuf": "^3.6.1",
"grpc-web": "^0.4.0"
},
"devDependencies": {
"browserify": "^16.2.2",
"webpack": "^4.16.5",
"webpack-cli": "^3.1.0"
}
}

再來 .proto 是需要編譯的,故我們需要在系統層安裝一些 plug-in 讓我們能透過 terminal 下指令進行編譯。這些套件建議按照 grpc-web 的 Readme 安裝。

(1) protoc
這是編譯 .proto 的核心工具,我們將 release 的檔案如 protoc-3.10.1-osx-x86_64.zip 下載後進行二進制安裝。不過若是 Mac 的用戶,則推薦使用 Homebrew 安裝會比較簡單。

brew install protobuf// check if installed & version
$ protoc --version

(2) protoc-gen-grpc-web
這個套件可以幫助 protoc 編譯出 javascript。這邊比較方便的方式是將專案 clone 下來透過編譯安裝。

git clone https://github.com/grpc/grpc-web.git$ cd grpc-web
$ sudo make install-plugin

準備好以上前作業後,我們就可以在專案底下執行 protoc 指令,將 .proto 編譯出來並引用進我們的程式碼中。以下以 echo.proto 為例:

$ protoc echo.proto \
--js_out=import_style=commonjs:$OUT_DIR \
--grpc-web_out=import_style=commonjs:$OUT_DIR

$OUT_DIR 是輸出檔案的位置,可以自定義。指令執行完成後我們會得到 echo_pb.js echo_grpc_web_pb.js 兩支程式。接下來只要在我們的 javascript 引入即可使用囉。

(3) 使用
有了編譯完成的 js 後,我們該如何使用呢?參照官方文件的範例,在需要串接 gRPC 的文件引入:

const {EchoRequest, EchoResponse} = require('./echo_pb.js');
const {EchoServiceClient} = require('./echo_grpc_web_pb.js');
var echoService = new EchoServiceClient('http://localhost:8080');var request = new EchoRequest();
request.setMessage('Hello World!');
echoService.echo(request, {}, function(err, response) {
// ...
});

呼叫連線的方法已經被包裝成 EchoServiceClient 這個 Class,給予連線的位址即可建立。而過去我們在 REST 架構下需要關注的 GET POST PUT DELETE 等 API 方法,在 gRPC 中也被包裝成 Fuction 來使用,而這些方法只要參照 .proto 檔就可以了解明確的定義。

總結

其實 gRPC 的應用通常是在處理微服務架構間的通訊,所以在後端的成熟度相對較高,以前端的角度來看會稍微有點門檻,因此下面會分享一些串接過程的負面感受 😅。

  1. 環境建置成本較高。有別於過去我們只需要掌握 npm 管理套件,或 webpack 的打包設定,根據以上的步驟,相信大家也發現需要一些 compiler 套件的支援,在環境及開發上又更為複雜。
  2. gRPC 的連線於 web 的實作其實是靠 http/2 的協議達成的,雖然許多瀏覽器很早就支援這個連線方法,但 debug 工具卻不夠成熟(如 chrome dev tool 還沒辦法詳細檢視連線串送的內容),甚至需要安裝第三方 proxy 軟體攔截連線內容才能詳查。
  3. 因為前端打出的是 http/2 的連線,必須多使用一層 Proxy 來將內容轉成後端程式看得懂的 gRPC 連線。目前官方推薦 Envoy 來做 Server side Proxy,但這可能導致服務架構更複雜。
    (可參考文章建置 Enovy Proxy:https://greddywork.gitlab.io/greddyblogs/2019/11/01/envoy/)
  4. .proto 的共用雖然很方便前後端同步連線方法,但若有一個改動,就必須重新編譯,在開發的流程上容易造成混亂。

基於以上的原因,我個人其實還不建議基於 web 的服務使用 gRPC 來作為前後端的溝通;僅管很新很潮,傳輸穩定體積小,但若為了追求穩定及又能保持一定的開發節奏,以目前不成熟的環境著實不適合導入。

當然問題是靠人解決的,若能將 .proto 的編譯導入 CI/CD ,從流程上下手,也許能夠更加優化 gRPC 的開發體驗。以後若有機會實作這一段,我會詳盡的記錄下來並分享給大家!

nchuuu

Written by

nchuuu

難為工程師

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade