初涉RPC協(xié)議

RPC(Remote Procedure Call Protocol)——是一種遠(yuǎn)程調(diào)用協(xié)議,它采用客戶機(jī)/服務(wù)器模式。請(qǐng)求程序就是一個(gè)客戶機(jī),而服務(wù)提供程序就是一個(gè)服務(wù)器。首先,客戶機(jī)調(diào)用進(jìn)程發(fā)送一個(gè)有進(jìn)程參數(shù)的調(diào)用信息到服務(wù)進(jìn)程,然后等待應(yīng)答信息。在服務(wù)器端,進(jìn)程保持睡眠狀態(tài)直到調(diào)用信息到達(dá)為止。當(dāng)一個(gè)調(diào)用信息到達(dá),服務(wù)器獲得進(jìn)程參數(shù),計(jì)算結(jié)果,發(fā)送答復(fù)信息,然后等待下一個(gè)調(diào)用信息,最后,客戶端調(diào)用進(jìn)程接收答復(fù)信息,獲得進(jìn)程結(jié)果,然后調(diào)用執(zhí)行繼續(xù)進(jìn)行。

RPC的調(diào)用過程


運(yùn)行時(shí),一次客戶機(jī)對(duì)服務(wù)器的RPC調(diào)用,其內(nèi)部操作大致有如下十步:

(1)調(diào)用客戶端句柄;執(zhí)行傳送參數(shù)

(2)調(diào)用本地系統(tǒng)內(nèi)核發(fā)送網(wǎng)絡(luò)消息

(3)消息通過網(wǎng)絡(luò)到遠(yuǎn)程主機(jī)

(4)服務(wù)器句柄得到消息并取得參數(shù)

(5)執(zhí)行遠(yuǎn)程過程

(6)執(zhí)行的過程將結(jié)果返回服務(wù)器句柄

(7)服務(wù)器句柄返回結(jié)果,調(diào)用遠(yuǎn)程系統(tǒng)內(nèi)核

(8)消息通過網(wǎng)絡(luò)傳回本地主機(jī)

(9)客戶句柄由內(nèi)核接收消息

(10)客戶接收句柄返回的數(shù)據(jù)

RPC的目標(biāo)就是要2~8這些步驟都封裝起來,讓用戶對(duì)這些細(xì)節(jié)透明。

在go中,一個(gè)對(duì)象中只有滿足如下這些條件的方法,才能被 RPC 服務(wù)端設(shè)置為可供遠(yuǎn)程訪問:

1) 必須是在對(duì)象外部可公開調(diào)用的方法(首字母大寫);

2) 必須有兩個(gè)參數(shù),且參數(shù)的類型都必須是包外部可以訪問的類型或者是Go內(nèi)建支持的類

型;

3) 第二個(gè)參數(shù)必須是一個(gè)指針;

4) 方法必須返回一個(gè)error類型的值。

總結(jié)為:func (t *T) MethodName(argType T1, replyType *T2) error

在上面這行代碼中,類型T、T1 和 T2 默認(rèn)會(huì)使用 Go 內(nèi)置的 encoding/gob 包進(jìn)行編碼解碼。

關(guān)于encoding/gob 包的內(nèi)容,稍后我們將會(huì)對(duì)其進(jìn)行介紹。

該方法(MethodName)的第一個(gè)參數(shù)表示由 RPC 客戶端傳入的參數(shù),第二個(gè)參數(shù)表示要返

回給RPC客戶端的結(jié)果,該方法最后返回一個(gè) error 類型的值。

如果沒有明確指定 RPC 傳輸過程中使用何種編碼解碼器,默認(rèn)將使用 Go 標(biāo)準(zhǔn)庫提供的

encoding/gob 包進(jìn)行數(shù)據(jù)傳輸。net/rpc包下面還有json實(shí)現(xiàn)的rpc傳輸格式。

接下來貼一段go實(shí)現(xiàn)rpc的代碼:

server端:

package main

import (

? ? "errors"

? ? "fmt"

? ? "net"

? ? "net/http"

? ? "net/rpc"

)

type Person struct {

? ? Name string

? ? Sex? string

? ? Age? int

}

func (p *Person) Hello(args Person, reply *Person) error {

? ? reply.Name = "hello-" + args.Name

? ? reply.Sex = "hello-" + args.Sex

? ? reply.Age = args.Age * 2

? ? return nil

}

func main() {

? ? person := new(Person)

? ? //注冊(cè)服務(wù)對(duì)象并開啟該RPC服務(wù)

? ? rpc.Register(person)

? ? rpc.HandleHTTP() //rpc.HandleHTTP函數(shù)把該服務(wù)注冊(cè)到了HTTP協(xié)議上

? ? //然后我們就可以利用http的方式來傳遞數(shù)據(jù)了(基于HTTP協(xié)議的RPC)

? ? //如果我們采用基于TCP協(xié)議的RPC,則不用該函數(shù)

? ? tcpAddr, err := net.ResolveTCPAddr("tcp", ":6060")

? ? checkError(err)

? ? listener, err := net.ListenTCP("tcp", tcpAddr)

? ? checkError(err)

? ? http.Serve(listener, nil)//http處理監(jiān)聽器

}

func checkError(err error) {

? ? if err != nil {

? ? ? ? fmt.Println("Fatal error", err.Error())

? ? }

}

client端:

package main

import (

? ? "fmt"

? ? "log"

? ? "net/rpc"

)

type Person struct {

? ? Name string

? ? Sex? string

? ? Age? int

}

func main() {

? ? //與RPC服務(wù)端建立連接

? ? client, err := rpc.DialHTTP("tcp", ":6060")

? ? if err != nil {

? ? ? ? log.Fatal("dialing:", err)

? ? }

? ? //連接成功,RPC客戶端調(diào)用服務(wù)端提供的方法

? ? //同步調(diào)用程序順序執(zhí)行的方式

? ? person1 := Person{

? ? ? ? Name: "初級(jí)賽亞人",

? ? ? ? Sex:? "男",

? ? ? ? Age:? 117}

? ? person2 := new(Person)

? ? //同步調(diào)用rpc

? ? client.Call("Person.Hello", person1, &person2)

? ? fmt.Println("person2=", person2)

? ? //異步rpc方式進(jìn)行調(diào)用

? ? person3 := new(Person)

? ? divCall1 := client.Go("Person.Hello", person1, &person3, nil)

? ? <-divCall1.Done? //表示異步調(diào)用完成

? ? fmt.Println("person3=", person3)

}

打印結(jié)果:

person2= &{hello-初級(jí)賽亞人 hello-男 234}

person3= &{hello-初級(jí)賽亞人 hello-男 234}

.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容