1. 程式人生 > 實用技巧 >go語言rpc學習

go語言rpc學習

rpc 就是 遠端過程呼叫 指的是呼叫遠端伺服器上的程式的方法整個過程。

rpc 理論

RPC技術在架構設計上有四部分組成,分別是:客戶端、客戶端存根、服務端、服務端存根。

客戶端:服務呼叫發起方 又叫做 服務消費者

伺服器: server上執行的可以被客戶端呼叫的方法

客戶端存根:存放server端的地址和埠訊息 。 將客戶端訊息打包轉換成網路訊息 傳送給服務端 。 接受服務端的資料包 該程式執行在客戶端。

服務端存根:存放client 網路資料 解析資料 呼叫本地對應的方法執行,將產生的結果回給客戶端 該程式執行在 服務端。

go語言自帶的rpc 框架的使用

1 服務定義及暴露

在程式設計實現過程中,伺服器端需要註冊結構體物件,然後通過物件所屬的方法暴露給呼叫者,從而提供服務,該方法稱之為輸出方法,此輸出方法可以被遠端呼叫。

當然,在定義輸出方法時,能夠被遠端呼叫的方法需要遵循一定的規則。我們通過程式碼進行講解:

func (t *T) MethodName(request T1,response *T2) error

上述程式碼是go語言官方給出的對外暴露的服務方法的定義標準,其中包含了主要的幾條規則,分別是:

  • 1、對外暴露的方法有且只能有兩個引數,這個兩個引數只能是輸出型別或內建型別,兩種型別中的一種。
  • 2、方法的第二個引數必須是指標型別。
  • 3、方法的返回型別為error。
  • 4、方法的型別是可輸出的。
  • 5、方法本身也是可輸出的。

我們舉例說明:假設目前我們有一個需求,給出一個float型別變數,作為圓形的半徑,要求通過RPC呼叫,返回對應的圓形面積。具體的程式設計實現思路如下:

type MathUtil struct{
}
//該方法向外暴露:提供計算圓形面積的服務
func (mu *MathUtil) CalculateCircleArea(req float32, resp *float32) error {
	*resp = math.Pi * req * req //圓形的面積 s = π * r * r
	return nil //返回型別
}

在上述的案例中,我們可以看到:

  • 1、Calculate方法是服務物件MathUtil向外提供的服務方法,該方法用於接收傳入的圓形半徑資料,計算圓形面積並返回。
  • 2、第一個引數req代表的是呼叫者(client)傳遞提供的引數。
  • 3、第二個引數resp代表要返回給呼叫者的計算結果,必須是指標型別。
  • 4、正常情況下,方法的返回值為是error,為nil。如果遇到異常或特殊情況,則error將作為一個字串返回給呼叫者,此時,resp引數就不會再返回給呼叫者。

至此為止,已經實現了服務端的功能定義,接下來就是讓服務程式碼生效,需要將服務進行註冊,並啟動請求處理。

2 註冊服務及監聽請求

net/rpc包為我們提供了註冊服務和處理請求的一系列方法,結合本案例實現註冊及處理邏輯,如下所示:

//1、初始化指標資料型別
mathUtil := new(MathUtil) //初始化指標資料型別

//2、呼叫net/rpc包的功能將服務物件進行註冊
err := rpc.Register(mathUtil)
if err != nil {
	panic(err.Error())
}

//3、通過該函式把mathUtil中提供的服務註冊到HTTP協議上,方便呼叫者可以利用http的方式進行資料傳遞
rpc.HandleHTTP()

//4、在特定的埠進行監聽
listen, err := net.Listen("tcp", ":8081")
if err != nil {
	panic(err.Error())
}
go http.Serve(listen, nil)

經過服務註冊和監聽處理,RPC呼叫過程中的服務端實現就已經完成了。接下來需要實現的是客戶端請求程式碼的實現。

3 客戶端呼叫

在服務端是通過Http的埠監聽方式等待連線的,因此在客戶端就需要通過http連線,首先與服務端實現連線。

  • 客戶端連線服務端

    client, err := rpc.DialHTTP("tcp", "localhost:8081")
    	if err != nil {
    		panic(err.Error())
    	}
  • 遠端方法呼叫 客戶端成功連線服務端以後,就可以通過方法呼叫呼叫服務端的方法,具體呼叫方法如下:

    var req float32 //請求值
    req = 3
    
    var resp *float32 //返回值
    err = client.Call("MathUtil.CalculateCircleArea", req, &resp)
    if err != nil {
    	panic(err.Error())
    }
    fmt.Println(*resp)

    上述的呼叫方法核心在於client.Call方法的呼叫,該方法有三個引數,

  • 第一個引數表示要呼叫的遠端服務的方法名,第二個引數是呼叫時要傳入的引數,第三個引數是呼叫要接收的返回值。 上述的Call方法呼叫實現的方式是同步的呼叫,除此之外,還有一種非同步的方式可以實現呼叫。非同步呼叫程式碼實現如下:

  • var respSync *float32
    //非同步的呼叫方式
    syncCall := client.Go("MathUtil.CalculateCircleArea", req, &respSync, nil)
    replayDone := <-syncCall.Done
    fmt.Println(replayDone)
    fmt.Println(*respSync)

4 多引數的請求呼叫引數傳遞

上述內容演示了單個引數下的RPC呼叫,對於多引數下的請求該如何實現。我們通過另外一個案例進行演示。

假設現在需要實現另外一個需求:通過RPC呼叫實現計算兩個數字相加功能並返回計算結果。此時,就需要傳遞兩個引數,具體實現如下:

將引數定義在一個新的結構體中,存放在param包中:

type AddParma struct {
	Args1 float32 //第一個引數
	Args2 float32 //第二個引數
}

在server.go檔案中,實現兩數相加的功能,並實現服務註冊的邏輯:

func (mu *MathUtil) Add(param param.AddParma, resp *float32) error {
	*resp = param.Args1 + param.Args2 //實現兩數相加的功能
	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實現了服務的註冊和呼叫。

至此,我們已經完成了net/rpc包的最基礎的使用。

https://github.com/rubyhan1314/Golang-100-Days/tree/master/Day80(RPC%E8%BF%9C%E7%A8%8B%E8%B0%83%E7%94%A8%E6%9C%BA%E5%88%B6)/code

上面的連線對應三個例子 程式碼對應三個 一個是單個 引數 一個是多個引數 一個就是prob 封裝的引數 環境安裝啃爹 prob go語言到環境到 protobuf構建有點麻煩