網站首頁 編程語言 正文
gRPC
gRPC遠程過程調用框架是基于動作的模式,類似遠程調用微服務。這使得gRPC成為一種圍繞Protobufs構建的進程間通信(IPC)協議,用于處理客戶端和服務器之間的消息傳遞。gRPC非常適合密集而高效的通信,因為它支持客戶端和服務器流。
與REST對比
REST是一種基于資源的協議,客戶端根據請求體告訴服務器需要創建、讀取、更新或刪除哪些資源。gRPC還可以通過protobuf來定義接口,從而可以有更加嚴格的接口約束條件。gRPC為什么比REST更快?
gRPC利用HTTP/2協議,提供多種方式提供性能:
- 報頭壓縮和重用以減少消息大小
- 在單個TCP連接上同時發送多個請求和接收多個響應的多路復用
- 持久TCP連接用于單個TCP連接上的多個連續請求和響應
- 二進制格式支持,如協議緩沖區
需求說明
Go RPC服務器允許注冊任何Go類型及其方法,通過RPC協議公開這些方法,即可以從遠程客戶端按名稱進行調用。我們要實現的需求可以簡單描述為:
- 用戶可以添加書籍的信息
- 用戶添加書籍的閱讀進度
- 用戶查詢書籍的閱讀進度
當然我們還可以實現其他更復雜的功能,這里為了簡化,僅說明這幾個方法。下面首先定義Book類型:
// Book represents a book entry type Book struct { ISBN string Title, Author string Year, Pages int } // ReadingList keeps tracks of books and pages read type ReadingList struct { Books []Book Progress []int }
ReadingList 類內部包括兩個slice,模擬數據庫存儲書籍及對應進度。下面定義助手方法:
func (r *ReadingList) bookIndex(isbn string) int { for i := range r.Books { if isbn == r.Books[i].ISBN { return i } } return -1 }
上面通過ISBN查詢書籍對應索引號,現在繼續在ReadingList類型上定義幾個方法:
// AddBook checks if the book is not present and adds it func (r *ReadingList) AddBook(b Book) error { if b.ISBN == "" { return ErrISBN } if r.bookIndex(b.ISBN) != -1 { return ErrDuplicate } r.Books = append(r.Books, b) r.Progress = append(r.Progress, 0) return nil } // GetProgress returns the progress of a book func (r *ReadingList) GetProgress(isbn string) (int, error) { if isbn == "" { return -1, ErrISBN } i := r.bookIndex(isbn) if i == -1 { return -1, ErrMissing } return r.Progress[i], nil } // 設置進度 func (r *ReadingList) SetProgress(isbn string, pages int) error { if isbn == "" { return ErrISBN } i := r.bookIndex(isbn) if i == -1 { return ErrMissing } if p := r.Books[i].Pages; pages > p { pages = p } r.Progress[i] = pages return nil }
我們需求就是通過RPC協議公開這些方法,讓客戶端進行遠程調用。下面實現RPC服務器。
創建RPC服務器
上節已經準備好了公開的方法,創建RPC服務需要遵守一些規則:
- 方法的類型和方法自身必須是公開的(大寫開頭)
- 方法有兩個參數,類型也是公開的
- 第二個參數是指針類型
- 方法返回一個error類型
語法如下:
?func (t *T) Method(in T1, out *T2) error
知道了方法規范,下面對上節的方法進行包裝,首先我們定義幾個錯誤類型:
// List of errors var ( ErrISBN = fmt.Errorf("missing ISBN") ErrDuplicate = fmt.Errorf("duplicate book") ErrMissing = fmt.Errorf("missing book") )
再定義一個助手方法:
// sets the success pointer value from error func setSuccess(err error, b *bool) error { *b = err == nil return err }
首先實現Addbook和GetProgress包裝方法:
func (r *ReadingService) AddBook(b Book, success *bool) error { return setSuccess(r.ReadingList.AddBook(b), success) } func (r *ReadingService) GetProgress(isbn string, pages *int) (err error) { *pages, err = r.ReadingList.GetProgress(isbn) return err }
由于更新閱讀進度需要傳入兩個參數,但rpc方法不能傳入多個參數,因此我們定義類型進行參數封裝:
type Progress struct { ISBN string Pages int } func (r *ReadingService) SetProgress(p Progress, success *bool) error { return setSuccess(r.ReadingList.SetProgress(p.ISBN, p.Pages), success) }
準備了包裝方法,下面定義RPC服務就很簡單了:
func main() { if err := rpc.Register(&book.ReadingService{}); err != nil { log.Fatalln(err) } rpc.HandleHTTP() l, err := net.Listen("tcp", ":8900") if err != nil { log.Fatalln(err) } log.Println("Server Started") if err := http.Serve(l, nil); err != nil { log.Fatal(err) } }
服務端實現完畢,下面實現客戶端。基礎類型Book,Progress再客戶端也需要使用,因此可以獨立定義進行共享。
// Book represents a book entry type Book struct { ISBN string Title, Author string Year, Pages int } type Progress struct { ISBN string Pages int }
實現客戶端
首先定義測試數據:
const hp = "H.P. Lovecraft" var books = []Book{ {ISBN: "1540335534", Author: hp, Title: "The Call of Cthulhu", Pages: 36}, {ISBN: "1980722803", Author: hp, Title: "The Dunwich Horror ", Pages: 53}, {ISBN: "197620299X", Author: hp, Title: "The Shadow Over Innsmouth", Pages: 40}, {ISBN: "1540335536", Author: hp, Title: "The Case of Charles Dexter Ward", Pages: 176}, }
下面定義rpc客戶端:
client, err := rpc.DialHTTP("tcp", ":8900") if err != nil { log.Fatalln(err) } defer client.Close()
連接服務端后,可以調用服務端方法,下面定義統一方法進行調用:
func callClient(client *rpc.Client, method string, in, out interface{}) { var r interface{} if err := client.Call(method, in, out); err != nil { out = err } switch v := out.(type) { case error: r = v case *int: r = *v case *bool: r = *v } log.Printf("%s: [%+v] -> %+v", method, in, r) }
最后是循環測試數據,依此調用RPC方法:
for i, book := range books { callClient(client, "ReadingService.AddBook", book, new(bool)) callClient(client, "ReadingService.SetProgress", Progress{ ISBN: book.ISBN, Pages: 10 + i*i, }, new(bool)) callClient(client, "ReadingService.GetProgress", book.ISBN, new(int)) } }
完整代碼如下:
package main import ( log "log" "net/rpc" ) func main() { client, err := rpc.DialHTTP("tcp", ":8900") if err != nil { log.Fatalln(err) } defer client.Close() for i, book := range books { callClient(client, "ReadingService.AddBook", book, new(bool)) callClient(client, "ReadingService.SetProgress", Progress{ ISBN: book.ISBN, Pages: 10 + i*i, }, new(bool)) callClient(client, "ReadingService.GetProgress", book.ISBN, new(int)) } } // Book represents a book entry type Book struct { ISBN string Title, Author string Year, Pages int } type Progress struct { ISBN string Pages int } const hp = "H.P. Lovecraft" var books = []Book{ {ISBN: "1540335534", Author: hp, Title: "The Call of Cthulhu", Pages: 36}, {ISBN: "1980722803", Author: hp, Title: "The Dunwich Horror ", Pages: 53}, {ISBN: "197620299X", Author: hp, Title: "The Shadow Over Innsmouth", Pages: 40}, {ISBN: "1540335536", Author: hp, Title: "The Case of Charles Dexter Ward", Pages: 176}, } func callClient(client *rpc.Client, method string, in, out interface{}) { var r interface{} if err := client.Call(method, in, out); err != nil { out = err } switch v := out.(type) { case error: r = v case *int: r = *v case *bool: r = *v } log.Printf("%s: [%+v] -> %+v", method, in, r) }
原文鏈接:https://blog.csdn.net/neweastsun/article/details/128269912
相關推薦
- 2023-04-03 GoLang?BoltDB數據庫詳解_Golang
- 2023-02-26 詳解Golang時間處理的踩坑及解決_Golang
- 2023-05-29 Python中如何給字典設置默認值_python
- 2022-05-31 如何使用yolov5輸出檢測到的目標坐標信息_python
- 2022-07-22 如何快速刪除node_modules目錄方法詳解
- 2022-07-19 linux臨時修改網卡
- 2023-12-21 uniapp 清除文件緩存
- 2022-06-29 docker容器狀態轉換管理命令實例詳解_docker
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支