[從RPC到Go-Micro 壹]Go語言實現RPC程式設計
根據上篇內容,瞭解到RPC的原理及呼叫過程,本篇則使用Go語言的標準庫中的RPC庫實現簡單的RPC小Demo
標準庫中的RPC庫
包名: net/rpc
使用RPC庫實現簡單的計算功能
首先是提供方法暴露的一方–伺服器。
一、服務定義及暴露
在程式設計實現過程中,伺服器端需要註冊結構體物件,然後通過物件所屬的方法暴露給呼叫者,從而提供服務,該方法稱之為輸出方法,此輸出方法可以被遠端呼叫。當然,在定義輸出方法時,能夠被遠端呼叫的方法需要遵循一定的規則。我們通過程式碼進行講解:
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引數就不會再返回給呼叫者。
至此為止,已經實現了服務端的功能定義,接下來就是讓服務程式碼生效,需要將服務進行註冊,並啟動請求處理。
二、註冊服務及監聽請求
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", ":9000")
if err != nil {
panic(err.Error())
}
go http.Serve(listen, nil)
經過服務註冊和監聽處理,RPC呼叫過程中的服務端實現就已經完成了。接下來需要實現的是客戶端請求程式碼的實現。
三、客戶端呼叫
在服務端是通過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)
多引數的請求呼叫引數傳遞
上述內容演示了單個引數下的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
}
// 1. 初始化指標資料型別
mathUtil := new(MathUtil)
// 2. 呼叫net/rpc包的功能將服務物件進行註冊
err := rpc.RegisterName("MathUtil", mathUtil)
if err != nil {
panic(err.Error())
}
// 3. 通過該函式把mathUtil中提供的服務註冊到HTTP協議上,方便呼叫者通過http的方式呼叫
rpc.HandleHTTP()
// 4. 在特定的埠上進行監聽
listen, err := net.Listen("tcp", ":9000")
http.Serve(listen, nil)
在本案例中,我們通過新的註冊方法rpc.RegisterName實現了服務的註冊和呼叫。
完整程式碼如下
server.go
package main
import (
"fmt"
"net"
"net/http"
"net/rpc"
)
type AddParam struct {
Args1 float32
Args2 float32
}
// 數學計算
type MathUtil struct {
}
// 向外暴露計算的方法
func (mu *MathUtil) Add(req AddParam, resp *float32) error {
*resp = req.Args1 + req.Args2
fmt.Println(*resp)
return nil
}
// main方法
func main() {
// 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", ":9000")
if err != nil {
panic(err.Error())
}
_ = http.Serve(listen, nil)
}
client.go
package main
import (
"fmt"
"net/rpc"
)
type AddParam2 struct {
Args1 float32
Args2 float32
}
func main() {
// 撥號
client, err := rpc.DialHTTP("tcp", "localhost:9000")
if err != nil {
panic(err.Error())
}
// 請求值
var req = AddParam2{1, 2}
//var resp *float32
同步呼叫的方式
//err = client.Call("MathUtil.CalculateCircleArea", req, &resp)
//if err != nil {
// panic(err)
//}
//fmt.Println(*resp)
var respSync *float32
//非同步的呼叫方式
syncCall := client.Go("MathUtil.Add", req, &respSync, nil)
replayDone := <-syncCall.Done // 堵塞,直到成功
fmt.Println(replayDone)
fmt.Println(*respSync)
}
參考連結
相關文章
- go語言實現自己的RPC:go rpc codecGoRPC
- Go語言實現RPCGoRPC
- 從零開始實現簡單 RPC 框架 1:RPC 框架的結構和設計RPC框架
- Go 實現簡易 RPC 框架GoRPC框架
- Go實戰 22 | 網路程式設計:通過 RPC 實現跨平臺服務Go程式設計RPC
- Go RpcGoRPC
- go-micro開發RPC服務的方法及其執行原理GoRPC
- 7天用Go動手寫/從零實現RPC框架GeeRPCGoRPC框架
- RPC 框架設計RPC框架
- 從零實現一個RPC框架系列文章(二):11個類實現簡單RPCRPC框架
- 如何從0到1設計一個類Dubbo的RPC框架RPC框架
- GO語言泛型程式設計實踐Go泛型程式設計
- 幾行程式碼實現RPC框架行程RPC框架
- Thrift RPC 系列教程(1)——Thrift語言RPC
- Go 語言使用.NET 包實現 Socket 網路程式設計Go程式設計
- go語言遊戲服務端開發(四)——RPC機制Go遊戲服務端RPC
- go微服務框架go-micro深度學習(四) rpc方法呼叫過程詳解Go微服務框架深度學習RPC
- RPC呼叫介面設計RPC
- Go語言實現設計模式之命令模式Go設計模式
- 快收藏!最全GO語言實現設計模式Go設計模式
- Go 語言程式設計規範Go程式設計
- Go語言併發程式設計Go程式設計
- 從零開始實現一個RPC框架(四)RPC框架
- 從零開始實現一個RPC框架(零)RPC框架
- 從零開始實現一個RPC框架(二)RPC框架
- 從零開始實現一個RPC框架(五)RPC框架
- 從零開始實現一個RPC框架(一)RPC框架
- 從零開始實現一個RPC框架(三)RPC框架
- 簡述RPC原理實現RPC
- go rpc 原始碼分析GoRPC原始碼
- RPC基本原理以及如何用Netty來實現RPCRPCNetty
- Go語言程式設計快速入門Go程式設計
- 從問題到程式 程式設計與C語言引論pdf程式設計C語言
- Go語言實現GoF設計模式:介面卡模式Go設計模式
- 從零開始實現一個IDL+RPC框架RPC框架
- 從零開始實現一個分散式RPC框架分散式RPC框架
- 從函數語言程式設計到Ramda函式庫(一)函數程式設計函式
- DDD的函數語言程式設計實現函數程式設計