Golang遠(yuǎn)程調(diào)用框架RPC的具體使用
gRPC
gRPC遠(yuǎn)程過程調(diào)用框架是基于動(dòng)作的模式,類似遠(yuǎn)程調(diào)用微服務(wù)。這使得gRPC成為一種圍繞Protobufs構(gòu)建的進(jìn)程間通信(IPC)協(xié)議,用于處理客戶端和服務(wù)器之間的消息傳遞。gRPC非常適合密集而高效的通信,因?yàn)樗С挚蛻舳撕头?wù)器流。
與REST對(duì)比
REST是一種基于資源的協(xié)議,客戶端根據(jù)請(qǐng)求體告訴服務(wù)器需要?jiǎng)?chuàng)建、讀取、更新或刪除哪些資源。gRPC還可以通過protobuf來定義接口,從而可以有更加嚴(yán)格的接口約束條件。gRPC為什么比REST更快?
gRPC利用HTTP/2協(xié)議,提供多種方式提供性能:
- 報(bào)頭壓縮和重用以減少消息大小
- 在單個(gè)TCP連接上同時(shí)發(fā)送多個(gè)請(qǐng)求和接收多個(gè)響應(yīng)的多路復(fù)用
- 持久TCP連接用于單個(gè)TCP連接上的多個(gè)連續(xù)請(qǐng)求和響應(yīng)
- 二進(jìn)制格式支持,如協(xié)議緩沖區(qū)
需求說明
Go RPC服務(wù)器允許注冊(cè)任何Go類型及其方法,通過RPC協(xié)議公開這些方法,即可以從遠(yuǎn)程客戶端按名稱進(jìn)行調(diào)用。我們要實(shí)現(xiàn)的需求可以簡(jiǎn)單描述為:
- 用戶可以添加書籍的信息
- 用戶添加書籍的閱讀進(jìn)度
- 用戶查詢書籍的閱讀進(jìn)度
當(dāng)然我們還可以實(shí)現(xiàn)其他更復(fù)雜的功能,這里為了簡(jiǎn)化,僅說明這幾個(gè)方法。下面首先定義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 類內(nèi)部包括兩個(gè)slice,模擬數(shù)據(jù)庫存儲(chǔ)書籍及對(duì)應(yīng)進(jìn)度。下面定義助手方法:
func (r *ReadingList) bookIndex(isbn string) int { for i := range r.Books { if isbn == r.Books[i].ISBN { return i } } return -1 }
上面通過ISBN查詢書籍對(duì)應(yīng)索引號(hào),現(xiàn)在繼續(xù)在ReadingList類型上定義幾個(gè)方法:
// 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 } // 設(shè)置進(jìn)度 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協(xié)議公開這些方法,讓客戶端進(jìn)行遠(yuǎn)程調(diào)用。下面實(shí)現(xiàn)RPC服務(wù)器。
創(chuàng)建RPC服務(wù)器
上節(jié)已經(jīng)準(zhǔn)備好了公開的方法,創(chuàng)建RPC服務(wù)需要遵守一些規(guī)則:
- 方法的類型和方法自身必須是公開的(大寫開頭)
- 方法有兩個(gè)參數(shù),類型也是公開的
- 第二個(gè)參數(shù)是指針類型
- 方法返回一個(gè)error類型
語法如下:
func (t *T) Method(in T1, out *T2) error
知道了方法規(guī)范,下面對(duì)上節(jié)的方法進(jìn)行包裝,首先我們定義幾個(gè)錯(cuò)誤類型:
// List of errors var ( ErrISBN = fmt.Errorf("missing ISBN") ErrDuplicate = fmt.Errorf("duplicate book") ErrMissing = fmt.Errorf("missing book") )
再定義一個(gè)助手方法:
// sets the success pointer value from error func setSuccess(err error, b *bool) error { *b = err == nil return err }
首先實(shí)現(xiàn)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 }
由于更新閱讀進(jìn)度需要傳入兩個(gè)參數(shù),但rpc方法不能傳入多個(gè)參數(shù),因此我們定義類型進(jìn)行參數(shù)封裝:
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) }
準(zhǔn)備了包裝方法,下面定義RPC服務(wù)就很簡(jiǎn)單了:
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) } }
服務(wù)端實(shí)現(xiàn)完畢,下面實(shí)現(xiàn)客戶端?;A(chǔ)類型Book,Progress再客戶端也需要使用,因此可以獨(dú)立定義進(jìn)行共享。
// Book represents a book entry type Book struct { ISBN string Title, Author string Year, Pages int } type Progress struct { ISBN string Pages int }
實(shí)現(xiàn)客戶端
首先定義測(cè)試數(shù)據(jù):
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()
連接服務(wù)端后,可以調(diào)用服務(wù)端方法,下面定義統(tǒng)一方法進(jìn)行調(diào)用:
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) }
最后是循環(huán)測(cè)試數(shù)據(jù),依此調(diào)用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) }
到此這篇關(guān)于Golang遠(yuǎn)程調(diào)用框架RPC的具體使用的文章就介紹到這了,更多相關(guān)Go RPC內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang并發(fā)執(zhí)行的幾種方式小結(jié)
本文主要介紹了golang并發(fā)執(zhí)行的幾種方式小結(jié),主要包括了Channel,WaitGroup ,Context,使用這三種機(jī)制中的一種或者多種可以達(dá)到并發(fā)控制很好的效果,具有一定的參考價(jià)值,感興趣的可以了解一下2023-08-08golang如何使用gomobile進(jìn)行Android開發(fā)
golang可以開發(fā)android,使用golang開發(fā)android需要下載安裝gomobile,下面這篇文章主要給大家介紹了關(guān)于golang如何使用gomobile進(jìn)行Android開發(fā)的相關(guān)資料,需要的朋友可以參考下2023-01-01golang?pprof監(jiān)控memory?block?mutex使用指南
這篇文章主要為大家介紹了golang?pprof監(jiān)控memory?block?mutex使用指南,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04