gRPC的大訊息傳輸
概述
使用gRPC
的一個問題是,它的預設最大訊息大小預設被設定為4MB
,那麼當資料量太大時該怎麼辦?
options
可以通過在建立Server
的時候,配置相關的引數來擴大限制最小訊息大小的值。
s := grpc.NewServer(grpc.MaxRecvMsgSize(size), grpc.MaxSendMsgSize(size))
MaxRecvMsgSize
和MaxSendMsgSize
分別設定伺服器可以接收的最大訊息大小和可以傳送的最大訊息大小(以位元組為單位)。不設定的話預設都是4MB
。
雖然可以配置,但這種行為是一種滑坡謬誤,可能會導致不斷修改增加服務端客戶端最大訊息大小,而且每次請求不一定都需要全部的資料,會導致很多效能和資源上的浪費。
chunk
自然地將資料分成更小的塊並使用gRPC
流方法(stream
)對其進行流式傳輸是一個不錯的選擇。
首先一個proto
,是一個返回流式訊息型別的rpc service
。
syntax = "proto3";
package pb;
service Chunker {
rpc Chunker(Empty) returns (stream Chunk) {}
}
message Empty{}
message Chunk {
bytes chunk = 1;
}
實現server
的Chunker
邏輯。流式訊息的大小設定為64KB
。
const chunkSize = 64 * 1024
type chunkerSrv []byte
func (c chunkerSrv) Chunker(_ *pb.Empty, srv pb.Chunker_ChunkerServer) error {
chunk := &pb.Chunk{}
n := len(c)
for cur := 0; cur < n; cur += chunkSize {
if cur+chunkSize > n {
chunk.Chunk = c[cur:n]
} else {
chunk.Chunk = c[cur : cur+chunkSize]
}
if err := srv.Send(chunk); err != nil {
return err
}
}
return nil
}
然後把gRPC
服務端執行起來,使用隨機填充128M
的資料來方便測試。
func main() {
listen, err := net.Listen("tcp", ":8888")
if err != nil {
log.Fatal(err)
}
s := grpc.NewServer()
blob := make([]byte, 128*1024*1024) // 128M
rand.Read(blob)
pb.RegisterChunkerServer(s, chunkerSrv(blob))
log.Println("serving on localhost:8888")
log.Fatal(s.Serve(listen))
}
編寫個客戶端請求一下。
func main() {
conn, err := grpc.Dial("localhost:8888", grpc.WithInsecure())
if err != nil {
log.Fatal(err)
}
client := pb.NewChunkerClient(conn)
stream, err := client.Chunker(context.Background(), &pb.Empty{})
if err != nil {
log.Fatal(err)
}
var blob []byte
for {
c, err := stream.Recv()
if err != nil {
if err == io.EOF {
log.Printf("Transfer of %d bytes successful", len(blob))
// Transfer of 134217728 bytes successful
return
}
log.Fatal(err)
}
blob = append(blob, c.Chunk...)
}
使用分塊傳輸編碼,資料分解成一系列資料塊,並以一個或多個塊傳送,這樣客戶端自己再拼接成完整的資料,無論多少資料都可以不用修改配置。
range
在資料量大的情況,不是每次都需要請求全量的資料。基於之上可以借鑑http
的range
協議來分片的取獲取資源。同樣的在Chunker
的proto
基礎上修改,在請求的時候能傳入零個(代表全部獲取)或多個Range
來分片獲取資源。
syntax = "proto3";
package pb;
service RangeChunker {
rpc Range(Res) returns (stream Chunk) {}
}
message Res {
repeated Range r = 1;
}
message Range {
int32 start = 1;
int32 stop = 2;
}
message Chunk {
bytes chunk = 1;
}
服務端的實現主要是Range
的解析,這裡實現和http
的range
類似,使用0-99
代表前100位元組而不是0-100
,並簡化了很多,比如只保留了stop
設定-1
時代表最後一個位元組,其他的負數操作都沒有實現。需要的話可以自行修改rangeLimit
。
const chunkSize = 64 * 1024
type chunkerSrv []byte
func (c chunkerSrv) Range(r *pb.Res, srv pb.RangeChunker_RangeServer) error {
chunk := &pb.Chunk{}
ranges := c.parseRanges(r)
for _, rr := range ranges {
start, stop := rr[0], rr[1]
for cur := start; cur < stop; cur += chunkSize {
if cur+chunkSize > stop {
chunk.Chunk = c[cur:stop]
} else {
chunk.Chunk = c[cur : cur+chunkSize]
}
if err := srv.Send(chunk); err != nil {
return err
}
}
}
return nil
}
func (c chunkerSrv) parseRanges(r *pb.Res) [][2]int {
n := len(c)
ranges := [][2]int{}
rs := r.GetR()
if len(rs) == 0 {
return [][2]int{[2]int{0, n}}
}
for _, rr := range rs {
start, stop := rangeLimit(rr, n)
if start == -1 {
return nil
}
ranges = append(ranges, [2]int{start, stop})
}
return ranges
}
func rangeLimit(r *pb.Range, llen int) (int, int) {
start, stop := int(r.Start), int(r.Stop)+1
if stop > llen || stop == 0 {
stop = llen
}
if start < 0 || stop < 0 || start >= stop {
return -1, -1
}
return start, stop
}
客戶端請求也很簡單。
stream, err := client.Range(context.Background(), &pb.Res{
R: []*pb.Range{
{0, 99},
{100, 199},
{200, -1},
},
})
這樣我們就可以只請求資源的某個部分,基於此之上還可以並行請求,斷點續傳等。
原文連結:https://blog.keyboardman.me/2018/08/06/large-messages-with-grpc/
相關文章
- 訊息佇列之如何保證訊息的可靠傳輸佇列
- 探索AJAX中的訊息傳輸模式(一)模式
- 訊息佇列面試解析 - 傳輸協議佇列面試協議
- vue---元件間傳遞訊息(父子傳遞訊息,兄弟傳遞訊息)Vue元件
- 如何保證訊息佇列的可靠性傳輸?佇列
- SpringBoot 整合 RabbitMQ 實現訊息可靠傳輸Spring BootMQ
- Protobuf自動反射訊息型別的網路傳輸方案反射型別
- php ActiveMQ的傳送訊息,與處理訊息PHPMQ
- Android訊息傳遞之元件間傳遞訊息Android元件
- 訊息中介軟體—RocketMQ訊息傳送MQ
- Android訊息傳遞之Handler訊息機制Android
- RocketMQ中Producer訊息的傳送MQ
- 傳送不同型別的訊息型別
- 大檔案如何傳輸,大檔案的傳輸方式有哪些?
- Flutter中訊息傳遞Flutter
- 【RocketMQ】MQ訊息傳送MQ
- RocketMQ(八):訊息傳送MQ
- iOS 傳送位置訊息iOS
- Chrome Extension 訊息傳遞Chrome
- 鴻蒙傳送訊息通知鴻蒙
- MQ系列11:如何保證訊息可靠性傳輸(除夕奉上)MQ
- Kafka、RabbitMQ、RocketMQ訊息中介軟體的對比 —— 訊息傳送效能KafkaMQ
- 用程式碼理解 ObjC 中的傳送訊息和訊息轉發OBJ
- 用程式碼理解ObjC中的傳送訊息和訊息轉發OBJ
- 強大的音訊傳輸和錄製工具:Sound Siphon for Mac音訊Mac
- 傳送kafka訊息的shell指令碼Kafka指令碼
- MQTT QoS 設計:車聯網平臺訊息傳輸質量保障MQQT
- flutter 訊息傳遞機制Flutter
- RocketMQ(九):訊息傳送(續)MQ
- TNW-傳送模板訊息TNW
- 6-RocketMQ傳送訊息MQ
- Handler訊息傳遞機制
- Apache Kafka訊息傳遞策略ApacheKafka
- 如何快速傳輸大檔案:4 種大檔案傳輸有效的方法
- 用最少的程式碼模擬gRPC四種訊息交換模式RPC模式
- 鐳速傳輸:如何快速傳輸大檔案?
- 基於TCP長連線實現的帶QOS的訊息傳輸服務KTMTTCP
- 處理鍵盤輸入訊息(轉)