title: 【轉載】gRPC 快速入門
date: 2021-08-09 16:36:33
comment: false
toc: true
category:
- Golang
- gRPC
tags: - 轉載
- Go
- gRPC
- 快速
- 入門
本文轉載自:gRPC 快速入門 | 李文周的博客
RPC 算是近些年比較火熱的概念了,隨著微服務架構的興起,RPC 的應用越來越廣泛。本文介紹了 RPC 和 gRPC 的相關概念,並且通過詳細的代碼示例介紹了 gRPC 的基本使用。
gRPC#
RPC 是什麼#
在分佈式計算,遠程過程調用(英語:Remote Procedure Call,縮寫為 RPC)是一個計算機通信協議。該協議允許運行於一台計算機的程序調用另一個地址空間(通常為一個開放網絡的一台計算機)的子程序,而程序員就像調用本地程序一樣,無需額外地為這個交互作用編程(無需關注細節)。RPC 是一種伺服器 - 客戶端(Client/Server)模式,經典實現是一個通過 發送請求-接受回應
進行信息交互的系統。
gRPC 是什麼#
gRPC
是一種現代化開源的高性能 RPC 框架,能夠運行於任意環境之中。最初由谷歌進行開發。它使用 HTTP/2 作為傳輸協議。
在 gRPC 裡,客戶端可以像調用本地方法一樣直接調用其他機器上的伺服端應用程序的方法,幫助你更容易創建分佈式應用程序和服務。與許多 RPC 系統一樣,gRPC 是基於定義一個服務,指定一個可以遠程調用的帶有參數和返回類型的方法。在伺服端程序中實現這個接口並且運行 gRPC 服務處理客戶端調用。在客戶端,有一個 stub 提供和伺服端相同的方法。
為什麼要用 gRPC#
使用 gRPC,我們可以一次性的在一個 .proto
文件中定義服務並使用任何支持它的語言去實現客戶端和伺服端,反過來,它們可以應用在各種場景中,從 Google 的伺服器到你自己的平板電腦 —— gRPC 幫你解決了不同語言及環境間通信的複雜性。使用 protocol buffers
還能獲得其他好處,包括高效的序列號,簡單的 IDL 以及容易進行接口更新。總之一句話,使用 gRPC 能讓我們更容易編寫跨語言的分佈式代碼。
安裝 gRPC#
安裝 gRPC#
go get -u google.golang.org/grpc
安裝 Protocol Buffers v3#
安裝用於生成 gRPC 服務代碼的協議編譯器,最簡單的方法是從下面的鏈接:https://github.com/google/protobuf/releases下載適合你平台的預編譯好的二進制文件(protoc-<version>-<platform>.zip
)。
下載完之後,執行下面的步驟:
- 解壓下載好的文件
- 把
protoc
二進制文件的路徑加到環境變量中
接下來執行下面的命令安裝 protoc 的 Go 插件:
go get -u github.com/golang/protobuf/protoc-gen-go
編譯插件 protoc-gen-go
將會安裝到 $GOBIN
,默認是 $GOPATH/bin
,它必須在你的 $PATH
中以便協議編譯器 protoc
能夠找到它。
安裝指定#
gRPC 開發分三步#
把大象放進冰箱分幾步?
- 把冰箱門打開。
- 把大象放進去。
- 把冰箱門帶上。
gRPC 開發同樣分三步:
- 編寫
.proto
文件,生成指定語言源代碼。 - 編寫伺服端代碼
- 編寫客戶端代碼
gRPC 入門示例#
編寫 proto 代碼#
gRPC 是基於 Protocol Buffers。
Protocol Buffers
是一種與語言無關,平台無關的可擴展機制,用於序列化結構化數據。使用 Protocol Buffers
可以一次定義結構化的數據,然後可以使用特殊生成的源代碼輕鬆地在各種數據流中使用各種語言編寫和讀取結構化數據。
關於 Protocol Buffers
的教程可以自行在網上搜索,本文默認讀者熟悉 Protocol Buffers
。
syntax = "proto3"; // 版本聲明,使用Protocol Buffers v3版本
package pb; // 包名
// 定義一個打招呼服務
service Greeter {
// SayHello 方法
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// 包含人名的一個請求消息
message HelloRequest {
string name = 1;
}
// 包含問候語的響應消息
message HelloReply {
string message = 1;
}
執行下面的命令,生成 go 語言源代碼:
protoc -I helloworld/ helloworld/pb/helloworld.proto --go_out=plugins=grpc:helloworld
在 gRPC_demo/helloworld/pb
目錄下會生成 helloworld.pb.go
文件。
編寫 Server 端 Go 代碼#
package main
import (
"fmt"
"net"
pb "gRPC_demo/helloworld/pb"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
type server struct{}
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
func main() {
// 監聽本地的8972端口
lis, err := net.Listen("tcp", ":8972")
if err != nil {
fmt.Printf("failed to listen: %v", err)
return
}
s := grpc.NewServer() // 創建gRPC伺服器
pb.RegisterGreeterServer(s, &server{}) // 在gRPC伺服端註冊服務
reflection.Register(s) //在給定的gRPC伺服器上註冊伺服器反射服務
// Serve方法在lis上接受傳入連接,為每個連接創建一個ServerTransport和server的goroutine。
// 該goroutine讀取gRPC請求,然後調用已註冊的處理程序來響應它們。
err = s.Serve(lis)
if err != nil {
fmt.Printf("failed to serve: %v", err)
return
}
}
將上面的代碼保存到 gRPC_demo/helloworld/server/server.go
文件中,編譯並執行:
cd helloworld/server
go build
./server
編寫 Client 端 Go 代碼#
package main
import (
"context"
"fmt"
pb "gRPC_demo/helloworld/pb"
"google.golang.org/grpc"
)
func main() {
// 連接伺服器
conn, err := grpc.Dial(":8972", grpc.WithInsecure())
if err != nil {
fmt.Printf("faild to connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// 調用伺服端的SayHello
r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: "q1mi"})
if err != nil {
fmt.Printf("could not greet: %v", err)
}
fmt.Printf("Greeting: %s !\n", r.Message)
}
將上面的代碼保存到 gRPC_demo/helloworld/client/client.go
文件中,編譯並執行:
cd helloworld/client/
go build
./client
得到輸出如下(注意要先啟動 server 端再啟動 client 端):
$ ./client
Greeting: Hello q1mi!
此時我們的目錄結構如下:
./gRPC_demo
├── go.mod
├── go.sum
└── helloworld
├── client
│ ├── client
│ └── client.go
│ ├── client.py
├── pb
│ ├── helloworld.pb.go
│ └── helloworld.proto
└── server
├── server
└── server.go
gRPC 跨語言調用#
接下來,我們演示一下如何使用 gRPC 實現跨語言的 RPC 調用。
我們使用 Python
語言編寫 Client
,然後向上面使用 go
語言編寫的 server
發送 RPC 請求。
生成 Python 代碼#
在 gRPC_demo
目錄執行下面的命令:
python -m grpc_tools.protoc -I helloworld/pb/ --python_out=helloworld/client/ --grpc_python_out=helloworld/client/ helloworld/pb/helloworld.proto
上面的命令會在 gRPC_demo/helloworld/client/
目錄生成如下兩個 python 文件:
helloworld_pb2.py
helloworld_pb2_grpc.py
編寫 Python 版 Client#
在 ``gRPC_demo/helloworld/client/目錄創建
client.py` 文件,其內容如下:
# coding=utf-8
import logging
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
def run():
# 注意(gRPC Python Team): .close()方法在channel上是可用的。
# 並且應該在with語句不符合代碼需求的情況下使用。
with grpc.insecure_channel('localhost:8972') as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel)
response = stub.SayHello(helloworld_pb2.HelloRequest(name='q1mi'))
print("Greeter client received: {}!".format(response.message))
if __name__ == '__main__':
logging.basicConfig()
run()
將上面的代碼保存執行,得到輸出結果如下:
gRPC_demo $ python helloworld/client/client.py
Greeter client received: Hello q1mi!
這裡我們就實現了,使用 python 代碼編寫的 client 去調用 Go 語言版本的 server 了。
點擊右邊的鏈接查看完整代碼:gRPC_demo 完整代碼
gRPC 還有更多進階用法,未完待續…