gRPC雙向資料流的互動控制(go語言實現)| gRPC雙向資料流的互動控制系列(1)
gRPC簡介
gRPC (https://grpc.io) 是一個由Google開發的高效能、開源、跨多種程式語言和通用的遠端過程呼叫協議(RPC) 框架,用於客戶端和伺服器端之間的通訊,使用HTTP/2協議並將 ProtoBuf (https://developers.google.com/protocol-buffers)作為序列化工具。
gRPC模式
gRPC主要有4種請求/響應模式,分別是:
(1) 簡單模式(Simple RPC)
這種模式最為傳統,即客戶端發起一次請求,服務端響應一個資料,這和大家平時熟悉的RPC沒有什麼大的區別,所以不再詳細介紹。
(2) 服務端資料流模式(Server-side streaming RPC)
這種模式是客戶端發起一次請求,服務端返回一段連續的資料流。典型的例子是客戶端向服務端傳送一個股票程式碼,服務端就把該股票的實時資料來源源不斷的返回給客戶端。
(3) 客戶端資料流模式(Client-side streaming RPC)
與服務端資料流模式相反,這次是客戶端源源不斷的向服務端傳送資料流,而在傳送結束後,由服務端返回一個響應。典型的例子是物聯網終端向伺服器報送資料。
(4) 雙向資料流模式(Bidirectional streaming RPC)
顧名思義,這是客戶端和服務端都可以向對方傳送資料流,這個時候雙方的資料可以同時互相傳送,也就是可以實現實時互動。典型的例子是聊天機器人。
雙向資料流實戰
在gRPC中文文件(http://doc.oschina.net/grpc?t=60133)中有上述4種模式的例項,但是其中雙向資料流的例子過於簡單,沒有體現出雙向控制的特點,所以本文建立一個新的例子(用golang實現),用以展示gRPC雙向資料流的互動(關於proto如何定義、相關包如何安裝,在文件中都有介紹,所以本文略去此部分)。
1、proto定義
syntax = "proto3"; // 語法使用 protocol buffer proto3
// 包名: chat
package chat;
/*
服務名: Chat,
其中只有 名為“BidStream”的一個RPC服務,
輸入是 Request格式的資料流, 輸出是 Response 格式的資料流
*/
service Chat {
rpc BidStream(stream Request) returns (stream Response) {}
}
// 請求資料 Request格式定義
message Request {
string input = 1;
}
// 響應資料Response格式定義
message Response {
string output = 1;
}
服務端程式 server.go
package main
import (
"io"
"log"
"net"
"strconv"
"google.golang.org/grpc"
proto "chat" // 自動生成的 proto程式碼
)
// Streamer 服務端
type Streamer struct{}
// BidStream 實現了 ChatServer 介面中定義的 BidStream 方法
func (s *Streamer) BidStream(stream proto.Chat_BidStreamServer) error {
ctx := stream.Context()
for {
select {
case <-ctx.Done():
log.Println("收到客戶端通過context發出的終止訊號")
return ctx.Err()
default:
// 接收從客戶端發來的訊息
輸入, err := stream.Recv()
if err == io.EOF {
log.Println("客戶端傳送的資料流結束")
return nil
}
if err != nil {
log.Println("接收資料出錯:", err)
return err
}
// 如果接收正常,則根據接收到的 字串 執行相應的指令
switch 輸入.Input {
case "結束對話\n":
log.Println("收到'結束對話'指令")
if err := stream.Send(&proto.Response{Output: "收到結束指令"}); err != nil {
return err
}
// 收到結束指令時,通過 return nil 終止雙向資料流
return nil
case "返回資料流\n":
log.Println("收到'返回資料流'指令")
// 收到 收到'返回資料流'指令, 連續返回 10 條資料
for i := 0; i < 10; i++ {
if err := stream.Send(&proto.Response{Output: "資料流 #" + strconv.Itoa(i)}); err != nil {
return err
}
}
default:
// 預設情況下, 返回 '服務端返回: ' + 輸入資訊
log.Printf("[收到訊息]: %s", 輸入.Input)
if err := stream.Send(&proto.Response{Output: "服務端返回: " + 輸入.Input}); err != nil {
return err
}
}
}
}
}
func main() {
log.Println("啟動服務端...")
server := grpc.NewServer()
// 註冊 ChatServer
proto.RegisterChatServer(server, &Streamer{})
address, err := net.Listen("tcp", ":3000")
if err != nil {
panic(err)
}
if err := server.Serve(address); err != nil {
panic(err)
}
}
客戶端程式 client.go
package main
import (
"bufio"
"context"
"io"
"log"
"os"
"google.golang.org/grpc"
proto "chat" // 根據proto檔案自動生成的程式碼
)
func main() {
// 建立連線
conn, err := grpc.Dial("localhost:3000", grpc.WithInsecure())
if err != nil {
log.Printf("連線失敗: [%v]\n", err)
return
}
defer conn.Close()
// 宣告客戶端
client := proto.NewChatClient(conn)
// 宣告 context
ctx := context.Background()
// 建立雙向資料流
stream, err := client.BidStream(ctx)
if err != nil {
log.Printf("建立資料流失敗: [%v]\n", err)
}
// 啟動一個 goroutine 接收命令列輸入的指令
go func() {
log.Println("請輸入訊息...")
輸入 := bufio.NewReader(os.Stdin)
for {
// 獲取 命令列輸入的字串, 以回車 \n 作為結束標誌
命令列輸入的字串, _ := 輸入.ReadString('\n')
// 向服務端傳送 指令
if err := stream.Send(&proto.Request{Input: 命令列輸入的字串}); err != nil {
return
}
}
}()
for {
// 接收從 服務端返回的資料流
響應, err := stream.Recv()
if err == io.EOF {
log.Println("⚠️ 收到服務端的結束訊號")
break //如果收到結束訊號,則退出“接收迴圈”,結束客戶端程式
}
if err != nil {
// TODO: 處理接收錯誤
log.Println("接收資料出錯:", err)
}
// 沒有錯誤的情況下,列印來自服務端的訊息
log.Printf("[客戶端收到]: %s", 響應.Output)
}
}
執行效果
先啟動服務端程式 server.go 再啟動客戶端程式 client.go
輸入訊息,結果類似下圖:
總結
gRPC是個很強大的RPC框架,而且支援多語言程式設計,上面的服務端、客戶端程式我們完全可以用不同的語言實現,比如服務端用JAVA,客戶端用Python...
gRPC的四種互動模式也給我們提供了很大的發揮空間,最近Nginx宣佈支援gRPC,這可能也預示著某種趨勢...
gRPC雙向資料流的互動控制系列
(之二): 通過Websocket與gRPC互動
(之三): 通過Nginx實現gRPC服務的負載均衡
相關文章
- 通過Websocket與gRPC互動 | gRPC雙向資料流的互動控制系列(2)WebRPC
- 通過Nginx實現gRPC服務的負載均衡 | gRPC雙向資料流的互動控制系列(3)NginxRPC負載
- 透過Nginx實現gRPC服務的負載均衡 | gRPC雙向資料流的互動NginxRPC負載
- grpc雙向流RPC
- Vue 中雙向繫結 Vs 單向資料流Vue
- 【網路協議】TCP的互動資料流和成塊資料流協議TCP
- 手動實現vue元件間的雙向資料繫結Vue元件
- javascript實現資料的雙向繫結(手動繫結)JavaScript
- 手動簡單實現Vue雙向資料繫結Vue
- Silverlight與HTML雙向互動HTML
- 雙向資料繫結實現原理
- javascript實現雙向資料繫結JavaScript
- js 實現vue的雙向資料繫結JSVue
- vue資料雙向繫結的實現原理Vue
- 單向資料流
- JavaScript實現簡單的雙向資料繫結JavaScript
- 基於vue實現的雙向資料繫結Vue
- 資料結構(雙向連結串列的實現)資料結構
- iOS 自定義的卡片流互動控制元件iOS控制元件
- unison+inotify實現web資料雙向同步Web
- 通過原生js實現資料的雙向繫結JS
- 阿里雲資料中臺升級Quick Audience實現企業與消費者雙向互動阿里UI
- Go 語言實現 gRPC 的釋出訂閱模式,REST 介面和超時控制GoRPC模式REST
- C語言資料結構:雙向連結串列的增刪操作C語言資料結構
- JS雙向資料繫結JS
- 從單向到雙向資料繫結
- GoldenGate實現oracle和sqlserver雙向資料同步GoOracleSQLServer
- 在winform中如何實現雙向資料繫結?ORM
- 資料互動
- go語言gRPC系列(二) - 為gRPC新增證書GoRPC
- go語言實現TLS雙向認證的客戶端 程式碼例子GoTLS客戶端
- Go實現雙向連結串列Go
- 縱向控制的橫向滾動
- 前後端資料的互動--如何實現資料加密?--02後端加密
- 軟體是實現資料自動流動的核心
- 淺析vue的雙向資料繫結Vue
- vue中的雙向資料繫結原理Vue
- Redis資料庫4:Go與Redis的互動Redis資料庫Go