Golang遠(yuǎn)程調(diào)用框架RPC的具體使用
gRPC
gRPC遠(yuǎn)程過程調(diào)用框架是基于動作的模式,類似遠(yuǎn)程調(diào)用微服務(wù)。這使得gRPC成為一種圍繞Protobufs構(gòu)建的進程間通信(IPC)協(xié)議,用于處理客戶端和服務(wù)器之間的消息傳遞。gRPC非常適合密集而高效的通信,因為它支持客戶端和服務(wù)器流。
與REST對比
REST是一種基于資源的協(xié)議,客戶端根據(jù)請求體告訴服務(wù)器需要創(chuàng)建、讀取、更新或刪除哪些資源。gRPC還可以通過protobuf來定義接口,從而可以有更加嚴(yán)格的接口約束條件。gRPC為什么比REST更快?
gRPC利用HTTP/2協(xié)議,提供多種方式提供性能:
- 報頭壓縮和重用以減少消息大小
- 在單個TCP連接上同時發(fā)送多個請求和接收多個響應(yīng)的多路復(fù)用
- 持久TCP連接用于單個TCP連接上的多個連續(xù)請求和響應(yīng)
- 二進制格式支持,如協(xié)議緩沖區(qū)
需求說明
Go RPC服務(wù)器允許注冊任何Go類型及其方法,通過RPC協(xié)議公開這些方法,即可以從遠(yuǎn)程客戶端按名稱進行調(diào)用。我們要實現(xiàn)的需求可以簡單描述為:
- 用戶可以添加書籍的信息
- 用戶添加書籍的閱讀進度
- 用戶查詢書籍的閱讀進度
當(dāng)然我們還可以實現(xiàn)其他更復(fù)雜的功能,這里為了簡化,僅說明這幾個方法。下面首先定義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)部包括兩個slice,模擬數(shù)據(jù)庫存儲書籍及對應(yīng)進度。下面定義助手方法:
func (r *ReadingList) bookIndex(isbn string) int {
for i := range r.Books {
if isbn == r.Books[i].ISBN {
return i
}
}
return -1
}
上面通過ISBN查詢書籍對應(yīng)索引號,現(xiàn)在繼續(xù)在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
}
// 設(shè)置進度
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é)議公開這些方法,讓客戶端進行遠(yuǎn)程調(diào)用。下面實現(xiàn)RPC服務(wù)器。
創(chuàng)建RPC服務(wù)器
上節(jié)已經(jīng)準(zhǔn)備好了公開的方法,創(chuàng)建RPC服務(wù)需要遵守一些規(guī)則:
- 方法的類型和方法自身必須是公開的(大寫開頭)
- 方法有兩個參數(shù),類型也是公開的
- 第二個參數(shù)是指針類型
- 方法返回一個error類型
語法如下:
func (t *T) Method(in T1, out *T2) error
知道了方法規(guī)范,下面對上節(jié)的方法進行包裝,首先我們定義幾個錯誤類型:
// 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
}
首先實現(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
}
由于更新閱讀進度需要傳入兩個參數(shù),但rpc方法不能傳入多個參數(shù),因此我們定義類型進行參數(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ù)就很簡單了:
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ù)端實現(xiàn)完畢,下面實現(xiàn)客戶端?;A(chǔ)類型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
}
實現(xiàn)客戶端
首先定義測試數(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)一方法進行調(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)測試數(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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解golang執(zhí)行Linux shell命令完整場景下的使用方法
本文主要介紹了golang執(zhí)行Linux shell命令完整場景下的使用方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
golang如何設(shè)置Header Content-type
這篇文章主要介紹了golang如何設(shè)置Header Content-type問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
Go語言函數(shù)的延遲調(diào)用(Deferred Code)詳解
本文將介紹Go語言函數(shù)和方法中的延遲調(diào)用,正如名稱一樣,這部分定義不會立即執(zhí)行,一般會在函數(shù)返回前再被調(diào)用,我們通過一些示例來了解一下延遲調(diào)用的使用場景2022-07-07
一文帶你玩轉(zhuǎn)Golang Prometheus Eexporter開發(fā)
本文分兩大塊,一是搞清楚prometheus四種類型的指標(biāo)Counter,Gauge,Histogram,Summary用golang語言如何構(gòu)造這4種類型對應(yīng)的指標(biāo),二是搞清楚修改指標(biāo)值的場景和方式,感興趣的可以了解一下2023-02-02
Golang使用pprof和trace進行診斷和修復(fù)性能問題
在?Go?中,開發(fā)人員可以使用強大的內(nèi)置工具來幫助診斷和修復(fù)性能問題,其中兩個工具是?pprof?和?trace?包,下面就跟隨小編一起來了解下如何使用pprof和trace進行診斷和修復(fù)性能問題吧2024-01-01

