Go中RPC遠(yuǎn)程過程調(diào)用的實(shí)現(xiàn)
一、RPC簡介及原理介紹
1.1、背景
在前面的課程《Go語言微服務(wù)理論與實(shí)踐課程》課程中,我們已經(jīng)學(xué)習(xí)了微服務(wù)的理論知識,了解了微服務(wù)實(shí)踐中需要解決哪些問題。
從本篇技術(shù)文檔開始,我們進(jìn)入新的微服務(wù)內(nèi)容的學(xué)習(xí)。在本系列課程中,我們會著重講框架的內(nèi)容,主要包括兩個:gRPC框架、go-micro框架。
首先來學(xué)習(xí)gRPC框架相關(guān)的內(nèi)容。
1.2、本地過程調(diào)用
讓我們先來看看正常情況下程序的執(zhí)行和調(diào)用情況。例如有如下go語言代碼:
func main() { var a, b int a = 1 b = 2 c := Add(a, b) fmt.Println("計算結(jié)果:", c) } func Add(a int, b int) int { return a + b }
在上述的Go語言代碼中,我們定義了一個Add方法用于實(shí)現(xiàn)兩個數(shù)相加的功能,在main方法中通過調(diào)用Add方法實(shí)現(xiàn)了計算兩個變量之和的操作。整個過程涉及到變量值入棧,出棧,賦值等操作,最后將出棧的計算結(jié)果返回并賦值給c變量。
總結(jié)說來,本地程序調(diào)用的過程大致可以分為幾個步驟和階段:
- 開發(fā)者開發(fā)好的程序,并進(jìn)行編譯,編譯成機(jī)器認(rèn)可的可執(zhí)行文件。
- 運(yùn)行可執(zhí)行文件,調(diào)用對應(yīng)的功能方法,期間會讀取可執(zhí)行文件中國的機(jī)器指令,進(jìn)行入棧,出棧賦值等操作。此時,計算機(jī)由可執(zhí)行程序所在的進(jìn)程控制。
- 調(diào)用結(jié)束,所有的內(nèi)存數(shù)據(jù)出棧,程序執(zhí)行結(jié)束。計算機(jī)繼續(xù)由操作系統(tǒng)進(jìn)行控制。
1.3、問題及解決方法
上文我們已經(jīng)說過,遠(yuǎn)程過程調(diào)用是在兩臺或者多臺不同的物理機(jī)器上實(shí)現(xiàn)的調(diào)用,其間要跨越網(wǎng)絡(luò)進(jìn)行調(diào)用。因此,我們再想通過前文本地方法調(diào)用的形式完成功能調(diào)用,就無法實(shí)現(xiàn)了,因?yàn)榫幾g器無法通過編譯的可執(zhí)行文件來調(diào)用遠(yuǎn)程機(jī)器上的程序方法。因此需要采用RPC的方式來實(shí)現(xiàn)遠(yuǎn)端服務(wù)器上的程序方法的調(diào)用。
RPC技術(shù)內(nèi)部原理是通過兩種技術(shù)的組合來實(shí)現(xiàn)的:本地方法調(diào)用 和 網(wǎng)絡(luò)通信技術(shù)。
1.4、RPC簡介
在上述本地過程調(diào)用的例子中,我們是在一臺計算機(jī)上執(zhí)行了計算機(jī)上的程序,完成調(diào)用。隨著計算機(jī)技術(shù)的發(fā)展和需求場景的變化,有時就需要從一臺計算機(jī)上執(zhí)行另外一臺計算機(jī)上的程序的需求,因此后來又發(fā)展出來了RPC技術(shù)。特別是目前隨著互聯(lián)網(wǎng)技術(shù)的快速迭代和發(fā)展,用戶和需求幾乎都是以指數(shù)式的方式在高速增長,這個時候絕大多數(shù)情況下程序都是部署在多臺機(jī)器上,就需要在調(diào)用其他物理機(jī)器上的程序的情況。
RPC是Remote Procedure Call Protocol單詞首字母的縮寫,簡稱為:RPC,翻譯成中文叫遠(yuǎn)程過程調(diào)用協(xié)議。所謂遠(yuǎn)程過程調(diào)用,通俗的理解就是可以在本地程序中調(diào)用運(yùn)行在另外一臺服務(wù)器上的程序的功能方法。這種調(diào)用的過程跨越了物理服務(wù)器的限制,是在網(wǎng)絡(luò)中完成的,在調(diào)用遠(yuǎn)端服務(wù)器上程序的過程中,本地程序等待返回調(diào)用結(jié)果,直到遠(yuǎn)端程序執(zhí)行完畢,將結(jié)果進(jìn)行返回到本地,最終完成一次完整的調(diào)用。
需要強(qiáng)調(diào)的是:遠(yuǎn)程過程調(diào)用指的是調(diào)用遠(yuǎn)端服務(wù)器上的程序的方法整個過程。
1.5、RPC設(shè)計組成
RPC技術(shù)在架構(gòu)設(shè)計上有四部分組成,分別是:客戶端、客戶端存根、服務(wù)端、服務(wù)端存根。
這里提到了客戶端和服務(wù)端的概念,其屬于程序設(shè)計架構(gòu)的一種方式,在現(xiàn)代的計算機(jī)軟件程序架構(gòu)設(shè)計上,大方向上分為兩種方向,分別是:B/S架構(gòu)、C/S架構(gòu)。B/S架構(gòu)指的是瀏覽器到服務(wù)器交互的架構(gòu)方式,另外一種是在計算機(jī)上安裝一個單獨(dú)的應(yīng)用,稱之為客戶端,與服務(wù)器交互的模式。
由于在服務(wù)的調(diào)用過程中,有一方是發(fā)起調(diào)用方,另一方是提供服務(wù)方。因此,我們把服務(wù)發(fā)起方稱之為客戶端,把服務(wù)提供方稱之為服務(wù)端。以下是對RPC的四種角色的解釋和說明:
- **客戶端(Client):**服務(wù)調(diào)用發(fā)起方,也稱為服務(wù)消費(fèi)者。
- **客戶端存根(Client Stub):**該程序運(yùn)行在客戶端所在的計算機(jī)機(jī)器上,主要用來存儲要調(diào)用的服務(wù)器的地址,另外,該程序還負(fù)責(zé)將客戶端請求遠(yuǎn)端服務(wù)器程序的數(shù)據(jù)信息打包成數(shù)據(jù)包,通過網(wǎng)絡(luò)發(fā)送給服務(wù)端Stub程序;其次,還要接收服務(wù)端Stub程序發(fā)送的調(diào)用結(jié)果數(shù)據(jù)包,并解析返回給客戶端。
- **服務(wù)端(Server):**遠(yuǎn)端的計算機(jī)機(jī)器上運(yùn)行的程序,其中有客戶端要調(diào)用的方法。
- **服務(wù)端存根(Server Stub):**接收客戶Stub程序通過網(wǎng)絡(luò)發(fā)送的請求消息數(shù)據(jù)包,并調(diào)用服務(wù)端中真正的程序功能方法,完成功能調(diào)用;其次,將服務(wù)端執(zhí)行調(diào)用的結(jié)果進(jìn)行數(shù)據(jù)處理打包發(fā)送給客戶端Stub程序。
1.6、RPC原理及調(diào)用步驟
了解完了RPC技術(shù)的組成結(jié)構(gòu)我們來看一下具體是如何實(shí)現(xiàn)客戶端到服務(wù)端的調(diào)用的。實(shí)際上,如果我們想要在網(wǎng)絡(luò)中的任意兩臺計算機(jī)上實(shí)現(xiàn)遠(yuǎn)程調(diào)用過程,要解決很多問題,比如:
- 兩臺物理機(jī)器在網(wǎng)絡(luò)中要建立穩(wěn)定可靠的通信連接。
- 兩臺服務(wù)器的通信協(xié)議的定義問題,即兩臺服務(wù)器上的程序如何識別對方的請求和返回結(jié)果。也就是說兩臺計算機(jī)必須都能夠識別對方發(fā)來的信息,并且能夠識別出其中的請求含義和返回含義,然后才能進(jìn)行處理。這其實(shí)就是通信協(xié)議所要完成的工作。
讓我們來看看RPC具體是如何解決這些問題的,RPC每一步的調(diào)用過程。具體描述為:
1、客戶端想要發(fā)起一個遠(yuǎn)程過程調(diào)用,首先通過調(diào)用本地客戶端Stub程序的方式調(diào)用想要使用的功能方法名;
2、客戶端Stub程序接收到了客戶端的功能調(diào)用請求,將客戶端請求調(diào)用的方法名,攜帶的參數(shù)等信息做序列化操作,并打包成數(shù)據(jù)包。
3、客戶端Stub查找到遠(yuǎn)程服務(wù)器程序的IP地址,調(diào)用Socket通信協(xié)議,通過網(wǎng)絡(luò)發(fā)送給服務(wù)端。
4、服務(wù)端Stub程序接收到客戶端發(fā)送的數(shù)據(jù)包信息,并通過約定好的協(xié)議將數(shù)據(jù)進(jìn)行反序列化,得到請求的方法名和請求參數(shù)等信息。
5、服務(wù)端Stub程序準(zhǔn)備相關(guān)數(shù)據(jù),調(diào)用本地Server對應(yīng)的功能方法進(jìn)行,并傳入相應(yīng)的參數(shù),進(jìn)行業(yè)務(wù)處理。
6、服務(wù)端程序根據(jù)已有業(yè)務(wù)邏輯執(zhí)行調(diào)用過程,待業(yè)務(wù)執(zhí)行結(jié)束,將執(zhí)行結(jié)果返回給服務(wù)端Stub程序。
7、服務(wù)端Stub程序**將程序調(diào)用結(jié)果按照約定的協(xié)議進(jìn)行序列化,**并通過網(wǎng)絡(luò)發(fā)送回客戶端Stub程序。
8、客戶端Stub程序接收到服務(wù)端Stub發(fā)送的返回數(shù)據(jù),**對數(shù)據(jù)進(jìn)行反序列化操作,**并將調(diào)用返回的數(shù)據(jù)傳遞給客戶端請求發(fā)起者。
9、客戶端請求發(fā)起者得到調(diào)用結(jié)果,整個RPC調(diào)用過程結(jié)束。
1.7、RPC涉及到的相關(guān)技術(shù)
通過上文一系列的文字描述和講解,我們已經(jīng)了解了RPC的由來和RPC整個調(diào)用過程。我們可以看到RPC是一系列操作的集合,其中涉及到很多對數(shù)據(jù)的操作,以及網(wǎng)絡(luò)通信。因此,我們對RPC中涉及到的技術(shù)做一個總結(jié)和分析:
- 1、動態(tài)代理技術(shù): 上文中我們提到的Client Stub和Sever Stub程序,在具體的編碼和開發(fā)實(shí)踐過程中,都是使用動態(tài)代理技術(shù)自動生成的一段程序。
- 2、序列化和反序列化: 在RPC調(diào)用的過程中,我們可以看到數(shù)據(jù)需要在一臺機(jī)器上傳輸?shù)搅硗庖慌_機(jī)器上。在互聯(lián)網(wǎng)上,所有的數(shù)據(jù)都是以字節(jié)的形式進(jìn)行傳輸?shù)摹6覀冊诰幊痰倪^程中,往往都是使用數(shù)據(jù)對象,因此想要在網(wǎng)絡(luò)上將數(shù)據(jù)對象和相關(guān)變量進(jìn)行傳輸,就需要對數(shù)據(jù)對象做序列化和反序列化的操作。
**序列化:**把對象轉(zhuǎn)換為字節(jié)序列的過程稱為對象的序列化,也就是編碼的過程。
**反序列化:**把字節(jié)序列恢復(fù)為對象的過程稱為對象的反序列化,也就是解碼的過程。
我們常見的Json,XML等相關(guān)框架都可以對數(shù)據(jù)做序列化和反序列化編解碼操作。同時,在之前的《Go語言微服務(wù)理論與實(shí)踐》課程中,我們已經(jīng)學(xué)習(xí)過Protobuf協(xié)議,這也是一種數(shù)據(jù)編解碼的協(xié)議,在RPC框架中使用的更廣泛。
二、Go語言實(shí)現(xiàn)RPC編程
上節(jié)課我們對RPC知識做了介紹,講解了RPC的原理,通過圖示方式講解了RPC的內(nèi)部執(zhí)行過程。本節(jié)課,我們繼續(xù)來學(xué)習(xí)RPC相關(guān)的內(nèi)容。
2.1、RPC官方庫
在Go語言官方網(wǎng)站的pkg說明中,提供了官方支持的rpc包,具體鏈接如下:https://golang.org/pkg/net/rpc/。官方提供的rpc包完整的包名是:net/rpc。根據(jù)官方的解釋,rpc包主要是提供通過網(wǎng)絡(luò)訪問一個對象方法的功能。
本節(jié)課,我們就來學(xué)習(xí)如何使用go語言官方提供的RPC包實(shí)現(xiàn)RPC調(diào)用編碼。
2.2、net/rpc庫實(shí)現(xiàn)RPC調(diào)用編程
前文我們已經(jīng)講過rpc調(diào)用有兩個參與者,分別是:客戶端(client)和服務(wù)器(server)。
首先是提供方法暴露的一方–服務(wù)器。
2.2.1、服務(wù)定義及暴露
在編程實(shí)現(xiàn)過程中,服務(wù)器端需要注冊結(jié)構(gòu)體對象,然后通過對象所屬的方法暴露給調(diào)用者,從而提供服務(wù),該方法稱之為輸出方法,此輸出方法可以被遠(yuǎn)程調(diào)用。當(dāng)然,在定義輸出方法時,能夠被遠(yuǎn)程調(diào)用的方法需要遵循一定的規(guī)則。我們通過代碼進(jìn)行講解:
func (t *T) MethodName(request T1,response *T2) error
上述代碼是go語言官方給出的對外暴露的服務(wù)方法的定義標(biāo)準(zhǔn),其中包含了主要的幾條規(guī)則,分別是:
1、對外暴露的方法有且只能有兩個參數(shù),這個兩個參數(shù)只能是輸出類型或內(nèi)建類型,兩種類型中的一種。
2、方法的第二個參數(shù)必須是指針類型。
3、方法的返回類型為error。
4、方法的類型是可輸出的。
5、方法本身也是可輸出的。
我們舉例說明:假設(shè)目前我們有一個需求,給出一個float類型變量,作為圓形的半徑,要求通過RPC調(diào)用,返回對應(yīng)的圓形面積。具體的編程實(shí)現(xiàn)思路如下:
type MathUtil struct{ } //該方法向外暴露:提供計算圓形面積的服務(wù) func (mu *MathUtil) CalculateCircleArea(req float32, resp *float32) error { *resp = math.Pi * req * req //圓形的面積 s = π * r * r return nil //返回類型 }
在上述的案例中,我們可以看到:
1、Calculate方法是服務(wù)對象MathUtil向外提供的服務(wù)方法,該方法用于接收傳入的圓形半徑數(shù)據(jù),計算圓形面積并返回。
2、第一個參數(shù)req代表的是調(diào)用者(client)傳遞提供的參數(shù)。
3、第二個參數(shù)resp代表要返回給調(diào)用者的計算結(jié)果,必須是指針類型。
4、正常情況下,方法的返回值為是error,為nil。如果遇到異常或特殊情況,則error將作為一個字符串返回給調(diào)用者,此時,resp參數(shù)就不會再返回給調(diào)用者。
至此為止,已經(jīng)實(shí)現(xiàn)了服務(wù)端的功能定義,接下來就是讓服務(wù)代碼生效,需要將服務(wù)進(jìn)行注冊,并啟動請求處理。
2.2.2、注冊服務(wù)及監(jiān)聽請求
net/rpc包為我們提供了注冊服務(wù)和處理請求的一系列方法,結(jié)合本案例實(shí)現(xiàn)注冊及處理邏輯,如下所示:
//1、初始化指針數(shù)據(jù)類型 mathUtil := new(MathUtil) //初始化指針數(shù)據(jù)類型 //2、調(diào)用net/rpc包的功能將服務(wù)對象進(jìn)行注冊 err := rpc.Register(mathUtil) if err != nil { ?? ?panic(err.Error()) } //3、通過該函數(shù)把mathUtil中提供的服務(wù)注冊到HTTP協(xié)議上,方便調(diào)用者可以利用http的方式進(jìn)行數(shù)據(jù)傳遞 rpc.HandleHTTP() //4、在特定的端口進(jìn)行監(jiān)聽 listen, err := net.Listen("tcp", ":8081") if err != nil { ?? ?panic(err.Error()) } go http.Serve(listen, nil)
經(jīng)過服務(wù)注冊和監(jiān)聽處理,RPC調(diào)用過程中的服務(wù)端實(shí)現(xiàn)就已經(jīng)完成了。接下來需要實(shí)現(xiàn)的是客戶端請求代碼的實(shí)現(xiàn)。
2.2.3、客戶端調(diào)用
在服務(wù)端是通過Http的端口監(jiān)聽方式等待連接的,因此在客戶端就需要通過http連接,首先與服務(wù)端實(shí)現(xiàn)連接。
客戶端連接服務(wù)端
client, err := rpc.DialHTTP("tcp", "localhost:8081") if err != nil { panic(err.Error()) }
遠(yuǎn)端方法調(diào)用
客戶端成功連接服務(wù)端以后,就可以通過方法調(diào)用調(diào)用服務(wù)端的方法,具體調(diào)用方法如下:
var req float32 //請求值 req = 3 var resp *float32 //返回值 err = client.Call("MathUtil.CalculateCircleArea", req, &resp) if err != nil { ?? ?panic(err.Error()) } fmt.Println(*resp)
上述的調(diào)用方法核心在于client.Call方法的調(diào)用,該方法有三個參數(shù),第一個參數(shù)表示要調(diào)用的遠(yuǎn)端服務(wù)的方法名,第二個參數(shù)是調(diào)用時要傳入的參數(shù),第三個參數(shù)是調(diào)用要接收的返回值。
上述的Call方法調(diào)用實(shí)現(xiàn)的方式是同步的調(diào)用,除此之外,還有一種異步的方式可以實(shí)現(xiàn)調(diào)用。異步調(diào)用代碼實(shí)現(xiàn)如下:
var respSync *float32 //異步的調(diào)用方式 syncCall := client.Go("MathUtil.CalculateCircleArea", req, &respSync, nil) replayDone := <-syncCall.Done fmt.Println(replayDone) fmt.Println(*respSync)
2.2.4、多參數(shù)的請求調(diào)用參數(shù)傳遞
上述內(nèi)容演示了單個參數(shù)下的RPC調(diào)用,對于多參數(shù)下的請求該如何實(shí)現(xiàn)。我們通過另外一個案例進(jìn)行演示。
假設(shè)現(xiàn)在需要實(shí)現(xiàn)另外一個需求:通過RPC調(diào)用實(shí)現(xiàn)計算兩個數(shù)字相加功能并返回計算結(jié)果。此時,就需要傳遞兩個參數(shù),具體實(shí)現(xiàn)如下:
將參數(shù)定義在一個新的結(jié)構(gòu)體中,存放在param包中:
type AddParma struct { Args1 float32 //第一個參數(shù) Args2 float32 //第二個參數(shù) }
在server.go文件中,實(shí)現(xiàn)兩數(shù)相加的功能,并實(shí)現(xiàn)服務(wù)注冊的邏輯:
func (mu *MathUtil) Add(param param.AddParma, resp *float32) error { ?? ?*resp = param.Args1 + param.Args2 //實(shí)現(xiàn)兩數(shù)相加的功能 ?? ?return nil } mathUtil := new(MathUtil) ?? ?err := rpc.RegisterName("MathUtil", mathUtil) ?? ?if err != nil { ?? ??? ?panic(err.Error()) ?? ?} ?? ?rpc.HandleHTTP() ?? ?listen, err := net.Listen("tcp", ":8082") ?? ?http.Serve(listen, nil)
在本案例中,我們通過新的注冊方法rpc.RegisterName實(shí)現(xiàn)了服務(wù)的注冊和調(diào)用。
至此,我們已經(jīng)完成了net/rpc包的最基礎(chǔ)的使用。
三、RPC與Protobuf結(jié)合使用
上節(jié)課我們使用Golang提供的核心net/rpc庫實(shí)現(xiàn)了RPC調(diào)用編程。本節(jié)課繼續(xù)來看一下RPC和之前所學(xué)的Protobuf在編程中的結(jié)合實(shí)現(xiàn)。
需求:假設(shè)在一個系統(tǒng)中,有訂單模塊(Order),其他模塊想要實(shí)現(xiàn)RPC的遠(yuǎn)程工程調(diào)用,根據(jù)訂單ID和時間戳可以獲取訂單信息。如果獲取成功就返回相應(yīng)的訂單信息;如果查詢不到返回失敗信息?,F(xiàn)在,我們來進(jìn)行需求的編程實(shí)現(xiàn)。
3.1、傳輸數(shù)據(jù)格式定義
在《Go語言微服務(wù)理論實(shí)踐課程》中,學(xué)習(xí)過關(guān)于Protobuf的相關(guān)知識??梢岳肞rotobuf相關(guān)規(guī)則定義相應(yīng)的數(shù)據(jù)格式,文件擴(kuò)展名是.proto。
數(shù)據(jù)定義
根據(jù)需求,定義message.proto文件,詳細(xì)定義如下:
syntax = "proto3"; package message; //訂單請求參數(shù) message OrderRequest { ? ? string orderId = 1; ? ? int64 timeStamp = 2; } //訂單信息 message OrderInfo { ? ? string OrderId = 1; ? ? string OrderName = 2; ? ? string OrderStatus = 3; }
在上述文件中,定義了客戶端發(fā)起RPC調(diào)用時的請求數(shù)據(jù)結(jié)構(gòu)OrderRequest和服務(wù)端查詢后返回的數(shù)據(jù)結(jié)構(gòu)OrderInfo。數(shù)據(jù)定義采用proto3語法實(shí)現(xiàn),整個數(shù)據(jù)定義被定義在message包下。
編譯proto文件
通過proto編譯命令對.proto文件進(jìn)行編譯,自動生成對應(yīng)結(jié)構(gòu)體的Go語言文件。編譯命令如下:
protoc ./message.proto --go_out=./
執(zhí)行上述命令是在message包下。編譯命令結(jié)束后,會在message包下生成message.pb.go文件,其中自動生成了OrderRequest和OrderInfo在Go語言中結(jié)構(gòu)體的定義和相關(guān)的方法。
3.2、Protobuf格式數(shù)據(jù)與RPC結(jié)合
服務(wù)的定義
進(jìn)行RPC遠(yuǎn)程過程調(diào)用,實(shí)現(xiàn)調(diào)用遠(yuǎn)程服務(wù)器的方法,首先要有服務(wù)。在本案例中,定義提供訂單查詢功能的服務(wù),取名為OrderService,同時提供訂單信息查詢方法供遠(yuǎn)程調(diào)用。詳細(xì)的服務(wù)和方法定義如下:
//訂單服務(wù) type OrderService struct { } func (os *OrderService) GetOrderInfo(request message.OrderRequest, response *message.OrderInfo) error { ?? ?//201907310003 ?? ?orderMap := map[string]message.OrderInfo{ ?? ??? ?"201907300001": message.OrderInfo{OrderId: "201907300001", OrderName: "衣服", OrderStatus: "已付款"}, ?? ??? ?"201907310001": message.OrderInfo{OrderId: "201907310001", OrderName: "零食", OrderStatus: "已付款"}, ?? ??? ?"201907310002": message.OrderInfo{OrderId: "201907310002", OrderName: "食品", OrderStatus: "未付款"}, ?? ?} ? ?current := time.Now().Unix() ? ?if (request.TimeStamp > current) { ?? ? ?*response = message.OrderInfo{OrderId: "0", OrderName: "", OrderStatus: "訂單信息異常"} ? ?} else { ?? ? ?result := orderMap[request.OrderId]//201907310003 ?? ? ?if result.OrderId != "" { ?? ??? ? *response = orderMap[request.OrderId] ?? ? ?} else { ?? ??? ? return errors.New("server error") ?? ? ?} ? ?} ? ?return nil }
在服務(wù)的方法定義中,使用orderMap模擬初始訂單數(shù)據(jù)庫,方便案例查詢展示。GetOrderInfo方法有兩個參數(shù),第一個是message.OrderRequest,作為調(diào)用者傳遞的參數(shù),第二個是message.OrderInfo,作為調(diào)用返回的參數(shù),通過此處的兩個參數(shù),將上文通過.proto定義并自動生成的Go語言結(jié)構(gòu)體數(shù)據(jù)結(jié)合起來。
服務(wù)的注冊和處理
服務(wù)定義好以后,需要將服務(wù)注冊到RPC框架,并開啟http請求監(jiān)聽處理。這部分代碼與之前的RPC服務(wù)端實(shí)現(xiàn)邏輯一致,具體實(shí)現(xiàn)如下:
func main() { ?? ?orderService := new(OrderService) ?? ?rpc.Register(orderService) ?? ?rpc.HandleHTTP() ?? ?listen, err := net.Listen("tcp", ":8081") ?? ?if err != nil { ?? ??? ?panic(err.Error()) ?? ?} ?? ?http.Serve(listen, nil) }
RPC客戶端調(diào)用實(shí)現(xiàn)
在客戶端,除了客戶端正常訪問遠(yuǎn)程服務(wù)器的邏輯外,還需要準(zhǔn)備客戶端需要傳遞的請求數(shù)據(jù)message.OrderInfo。具體實(shí)現(xiàn)如下:
client, err := rpc.DialHTTP("tcp", "localhost:8081") ?? ?if err != nil { ?? ??? ?panic(err.Error()) ?? ?} ?? ?timeStamp := time.Now().Unix() ?? ?request := message.OrderRequest{OrderId: "201907310001", TimeStamp: timeStamp} ?? ?var response *message.OrderInfo ?? ?err = client.Call("OrderService.GetOrderInfo", request, &response) ?? ?if err != nil { ?? ??? ?panic(err.Error()) ?? ?} ?? ?fmt.Println(*response)
3.3、運(yùn)行結(jié)果
分別依次運(yùn)行server.go和client.go程序。
到此這篇關(guān)于Go中RPC遠(yuǎn)程過程調(diào)用的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Go中RPC遠(yuǎn)程調(diào)用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
GO語言字符串處理Strings包的函數(shù)使用示例講解
這篇文章主要為大家介紹了GO語言字符串處理Strings包的函數(shù)使用示例講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04golang通過node_exporter監(jiān)控GPU及cpu頻率、溫度的代碼
node_exporter這個開源組件是配合prometheus收集主機(jī)操作系統(tǒng)層的metrics的常用組件,但是官方?jīng)]有提供GPU卡的metrics的采集,今天通過本文給大家介紹golang通過node_exporter監(jiān)控GPU及cpu頻率、溫度的相關(guān)知識,感興趣的朋友一起看看吧2022-05-05詳解Golang中結(jié)構(gòu)體方法的高級應(yīng)用
本文旨在深度剖析Go中結(jié)構(gòu)體方法的高級應(yīng)用。我們不僅會回顧結(jié)構(gòu)體方法的基本概念和用法,還將探討如何通過高級技巧和最佳實(shí)踐,希望對大家有所幫助2024-01-01