用Go語言解釋什麼是RPC
簡介
遠端過程呼叫(英語:Remote Procedure Call,縮寫為 RPC)是一個計算機通訊協議。該協議允許運行於一臺計算機的程式呼叫另一臺計算機的子程式,而程式設計師無需額外地為這個互動作用程式設計。
Go語言使用RPC
標準庫的net/rpc
包是怎麼描述的:
package rpc
import "net/rpc"
rpc
包提供了通過網路或其他I/O連線對一個物件的匯出方法的訪問。服務端註冊一個物件,使它作為一個服務被暴露,服務的名字是該物件的型別名。註冊之後,物件的匯出方法就可以被遠端訪問。服務端可以註冊多個不同型別的物件(服務),但註冊具有相同型別的多個物件是錯誤的。
只有滿足如下標準的方法才能用於遠端訪問,其餘方法會被忽略:
- 方法是匯出的
- 方法有兩個引數,都是匯出型別或內建型別
- 方法的第二個引數是指標
- 方法只有一個error介面型別的返回值
事實上,方法必須看起來像這樣:
func (t *T) MethodName(argType T1, replyType *T2) error
其中T、T1和T2都能被encoding/gob
包序列化。這些限制即使使用不同的編解碼器也適用。(未來,對定製的編解碼器可能會使用較寬鬆一點的限制)
方法的第一個引數代表呼叫者提供的引數;第二個引數代表返回給呼叫者的引數。方法的返回值,如果非nil,將被作為字串回傳,在客戶端看來就和errors.New建立的一樣。如果返回了錯誤,回覆的引數將不會被髮送給客戶端。
服務端可能會單個連線上呼叫ServeConn
管理請求。更典型地,它會建立一個網路監聽器然後呼叫Accept
;或者,對於HTTP監聽器,呼叫HandleHTTP
和http.Serve
。
想要使用服務的客戶端會建立一個連線,然後用該連線呼叫NewClient
。
更方便的函式Dial(DialHTTP)
會在一個原始的連線(或HTTP連線)上依次執行這兩個步驟。
生成的Client型別值有兩個方法,Call和Go,它們的引數為要呼叫的服務和方法、一個包含引數的指標、一個用於接收接個的指標。
Call方法會等待遠端呼叫完成,而Go方法非同步的傳送呼叫請求並使用返回的Call結構體型別的Done通道欄位傳遞完成訊號。
除非設定了顯式的編解碼器,本包預設使用encoding/gob
然後標準庫也給出了一個例子:
data.go
package server
import (
"errors"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
type Arith int
func (t *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
func (t *Arith) 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
}
分析:這是一個公共包,服務端和客戶端都有用到裡面的資料
client.go
package main
import (
"fmt"
"log"
"net/rpc"
"learn-rpc/data"
)
func main() {
client, err := rpc.DialHTTP("tcp", "127.0.0.1:1234")
if err != nil {
log.Fatal("dialing:", err)
}
// Synchronous call
args := &data.Args{9, 9}
var reply int
err = client.Call("Arith.Multiply", args, &reply)
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)
}
分析:客戶端程式,先獲得一個rpc.Client
的結構,然後呼叫Call
方法
server.go
package main
import (
"log"
"net"
"net/http"
"net/rpc"
"learn-rpc/data"
)
func main() {
arith := new(data.Arith)
rpc.Register(arith)
rpc.HandleHTTP()
l, e := net.Listen("tcp", ":1234")
if e != nil {
log.Fatal("listen error:", e)
}
http.Serve(l, nil)
}
分析:先呼叫Listen
方法獲得Listen
結構,接著呼叫將該結構傳給Serve
方法即可開始監聽
注意:在執行程式時,要注意包路徑的問題,否則會編譯錯誤
先執行服務端,再執行客戶端