十年網(wǎng)站開發(fā)經(jīng)驗 + 多家企業(yè)客戶 + 靠譜的建站團隊
量身定制 + 運營維護+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
今天就跟大家聊聊有關(guān)Golang中怎么實現(xiàn)一個RPC功能,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

創(chuàng)新互聯(lián)建站來電聯(lián)系:18980820575,為您提供成都網(wǎng)站建設(shè)網(wǎng)頁設(shè)計及定制高端網(wǎng)站建設(shè)服務(wù),創(chuàng)新互聯(lián)建站網(wǎng)頁制作領(lǐng)域十余年,包括成都玻璃隔斷等多個行業(yè)擁有多年的網(wǎng)站設(shè)計經(jīng)驗,選擇創(chuàng)新互聯(lián)建站,為網(wǎng)站保駕護航。
客戶端:服務(wù)調(diào)用發(fā)起方,又稱之為服務(wù)消費者 服務(wù)器:遠端計算機上運行的程序,其中包含客戶端要調(diào)用和訪問的方法 客戶端存根:存放服務(wù)器端的地址,端口消息,將客戶端的請求參數(shù)打包成網(wǎng)絡(luò)消息,發(fā)送給服務(wù)器方。接收服務(wù)器方返回的數(shù)據(jù)包。該段程序運行在客戶端。 服務(wù)端存根:接收客戶端發(fā)送的數(shù)據(jù)包,解析數(shù)據(jù)包,調(diào)用數(shù)據(jù)包,調(diào)用具體的服務(wù)方法,將調(diào)用結(jié)果打包發(fā)送給客戶端一方。該段程序運行在服務(wù)端。
工作過程:
1、客戶端想要發(fā)起一個遠程過程調(diào)用,首先通過調(diào)用本地客戶端Stub程序的方式調(diào)用想要使用的功能方法名;
2、客戶端Stub程序接收到了客戶端的功能調(diào)用請求,將客戶端請求調(diào)用的方法名,攜帶的參數(shù)等信息做序列化操作,并打包成數(shù)據(jù)包。
3、客戶端Stub查找到遠程服務(wù)器程序的IP地址,調(diào)用Socket通信協(xié)議,通過網(wǎng)絡(luò)發(fā)送給服務(wù)端。
4、服務(wù)端Stub程序接收到客戶端發(fā)送的數(shù)據(jù)包信息,并通過約定好的協(xié)議將數(shù)據(jù)進行反序列化,得到請求的方法名和請求參數(shù)等信息。
5、服務(wù)端Stub程序準備相關(guān)數(shù)據(jù),調(diào)用本地Server對應的功能方法進行,并傳入相應的參數(shù),進行業(yè)務(wù)處理。
6、服務(wù)端程序根據(jù)已有業(yè)務(wù)邏輯執(zhí)行調(diào)用過程,待業(yè)務(wù)執(zhí)行結(jié)束,將執(zhí)行結(jié)果返回給服務(wù)端Stub程序。
7、服務(wù)端Stub程序?qū)⒊绦蛘{(diào)用結(jié)果按照約定的協(xié)議進行序列化,并通過網(wǎng)絡(luò)發(fā)送回客戶端Stub程序。
8、客戶端Stub程序接收到服務(wù)端Stub發(fā)送的返回數(shù)據(jù),對數(shù)據(jù)進行反序列化操作,并將調(diào)用返回的數(shù)據(jù)傳遞給客戶端請求發(fā)起者。
9、客戶端請求發(fā)起者得到調(diào)用結(jié)果,整個RPC調(diào)用過程結(jié)束。
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ā)實踐過程中,都是使用動態(tài)代理技術(shù)自動生成的一段程序。
2、序列化和反序列化: 在RPC調(diào)用的過程中,我們可以看到數(shù)據(jù)需要在一臺機器上傳輸?shù)搅硗庖慌_機器上。在互聯(lián)網(wǎng)上,所有的數(shù)據(jù)都是以字節(jié)的形式進行傳輸?shù)?。而我們在編程的過程中,往往都是使用數(shù)據(jù)對象,因此想要在網(wǎng)絡(luò)上將數(shù)據(jù)對象和相關(guān)變量進行傳輸,就需要對數(shù)據(jù)對象做序列化和反序列化的操作。
序列化:把對象轉(zhuǎn)換為字節(jié)序列的過程稱為對象的序列化,也就是編碼的過程。
反序列化:把字節(jié)序列恢復為對象的過程稱為對象的反序列化,也就是解碼的過程。
服務(wù)定義及暴漏:
func (t *T) MethodName(request T1,response *T2) error 上述代碼是go語言官方給出的對外暴露的服務(wù)方法的定義標準,其中包含了主要的幾條規(guī)則,分別是: 1、對外暴露的方法有且只能有兩個參數(shù),這個兩個參數(shù)只能是輸出類型或內(nèi)建類型,兩種類型中的一種。 2、方法的第二個參數(shù)必須是指針類型。 3、方法的返回類型為error。 4、方法的類型是可輸出的。 * 5、方法本身也是可輸出的。
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)用者。
客戶端連接服務(wù)端:
client, err := rpc.DialHTTP("tcp", "localhost:8081")
if err != nil {
panic(err.Error())
}遠端方法調(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)用的遠端服務(wù)的方法名,第二個參數(shù)是調(diào)用時要傳入的參數(shù),第三個參數(shù)是調(diào)用要接收的返回值。 上述的Call方法調(diào)用實現(xiàn)的方式是同步的調(diào)用,除此之外,還有一種異步的方式可以實現(xiàn)調(diào)用。異步調(diào)用代碼實現(xiàn)如下:
var respSync *float32
//異步的調(diào)用方式
syncCall := client.Go("MathUtil.CalculateCircleArea", req, &respSync, nil)
replayDone := <-syncCall.Done
fmt.Println(replayDone)
fmt.Println(*respSync)代碼演示:
Go HTTPRPC
//server
package main
import (
"errors"
"fmt"
"log"
"net/http"
"net/rpc"
)
//使用 rpc http實現(xiàn)簡單的 rpc操作
type Args struct {
A, B int
}
type Math int
func (m *Math) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
type Quotient struct {
Quo, Rem int
}
func (m *Math) Divide(args *Args, quo *Quotient) error {
if args.B == 0 {
return errors.New("divide by zero")
}
quo.Quo = args.A / args.B
quo.Rem = args.A % args.B
return nil
}
func main() {
math := new(Math)
rpc.Register(math) //注冊rpc
rpc.HandleHTTP() //使用http rpc
fmt.Println("rpc http server runing ....")
err := http.ListenAndServe(":1234", nil)
if err != nil {
log.Println(err.Error())
}
}
//client
package main
import (
"fmt"
"log"
"net/rpc"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
func main() {
// fmt.Println(os.Args)
// if len(os.Args) != 2 {
// fmt.Println("usage:", os.Args[0], "server")
// os.Exit(1)
// }
serverAddr := "127.0.0.1"
client, err := rpc.DialHTTP("tcp", serverAddr+":1234")
if err != nil {
log.Println("dial err is ", err)
}
var reply int
args := Args{1, 2}
err = client.Call("Math.Multiply", args, &reply) //server method方法名要與server一致,寫錯后,將會提示服務(wù)不存在
if err != nil {
log.Println("call err ", err)
}
fmt.Printf("Math Multiply: %d * %d =%d \n", args.A, args.B, reply)
var quo Quotient
err = client.Call("Math.Divide", args, &quo)
if err != nil {
log.Println("divide err ", err)
}
fmt.Printf("Math Divide: %d / %d =%d remainder %d \n", args.A, args.B, quo.Quo, quo.Rem)
}Go TcpRPC
//server
package main
import (
"errors"
"log"
"net"
"net/rpc"
)
type Math int
type Args struct {
A, B int
}
func (m *Math) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
type Quotient struct {
Quo, Rem int
}
func (m *Math) Divide(args *Args, quo *Quotient) error {
if args.B == 0 {
return errors.New("divide by zero")
}
quo.Quo = args.A / args.B
quo.Rem = args.A % args.B
return nil
}
func main() {
math := new(Math)
rpc.Register(math)
tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234")
if err != nil {
log.Println("Resolve Ip addr err ", err)
return
}
listen, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
log.Println("listen err is ", err)
return
}
for {
server, err := listen.Accept()
if err != nil {
log.Println("accept err is ", err)
continue
}
rpc.ServeConn(server)
}
}
//client
package main
import (
"fmt"
"log"
"net/rpc"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
func main() {
client, err := rpc.Dial("tcp", "127.0.0.1"+":1234")
if err != nil {
log.Println("rpc dial err :", err)
return
}
args := Args{1, 2}
var reply int
err = client.Call("Math.Multiply", args, &reply)
if err != nil {
log.Println("multiply err ", err)
return
}
fmt.Printf("multiply %d * %d = %d \n", args.A, args.B, reply)
var quo Quotient
err = client.Call("Math.Divide", args, &quo)
if err != nil {
log.Println("Divite err is ", err)
return
}
fmt.Printf("Divide %d / %d =%d ,rem %d", args.A, args.B, quo.Quo, quo.Rem)
}Go JSONRPC
//server
package main
import (
"net/rpc"
"net/rpc/jsonrpc"
"log"
"net"
"errors"
)
type Math int
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
func (m *Math)Multiply(args Args,reply *int) error{
*reply=args.A*args.B
return nil
}
func (m *Math)Divide(args Args,quo *Quotient) error{
if args.B==0{
return errors.New("divide by zreo")
}
quo.Quo=args.A/args.B
quo.Rem=args.A%args.B
return nil
}
func main(){
math:=new(Math)
rpc.Register(math)
listenAddr,err:=net.ResolveTCPAddr("tcp",":1234")
if err!=nil{
log.Println("resove ip addr err is ",err)
return
}
listen,err:=net.ListenTCP("tcp",listenAddr)
if err!=nil{
log.Println("listenTcp err ",err)
return
}
for{
conn,err:=listen.Accept()
if err!=nil{
log.Println("accept err is ",err)
return
}
jsonrpc.ServeConn(conn)
}
}
//client
package main
import (
"fmt"
"log"
"net/rpc/jsonrpc"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
func main() {
client, err := jsonrpc.Dial("tcp", "127.0.0.1"+":1234")
if err != nil {
log.Println("rpc dial err :", err)
return
}
args := Args{1, 2}
var reply int
err = client.Call("Math.Multiply", args, &reply)
if err != nil {
log.Println("multiply err ", err)
return
}
fmt.Printf("multiply %d * %d = %d \n", args.A, args.B, reply)
var quo Quotient
err = client.Call("Math.Divide", args, &quo)
if err != nil {
log.Println("Divite err is ", err)
return
}
fmt.Printf("Divide %d / %d =%d ,rem %d", args.A, args.B, quo.Quo, quo.Rem)
}看完上述內(nèi)容,你們對Golang中怎么實現(xiàn)一個RPC功能有進一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。