這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
RPC(Remote Procedure Call)—遠端程序呼叫,它是一種通過網路從遠端電腦程式上請求服務,而不需要瞭解底層網路技術的協議。RPC協議假定某些傳輸協議的存在,如TCP或UDP,為通訊程式之間攜帶資訊資料。在OSI網路通訊模型中,RPC跨越了傳輸層和應用程式層。RPC使得開發包括網路分布式多程式在內的應用程式更加容易。---百度百科
實際後台開發中,rpc是伺服器與伺服器互動的方式之一,隱藏底層網路實現,代碼程式化,開發效率高,BUG少。
通過一個簡單的demo來說明go官方rpc包的應用。
項目結構:
rpc ----Makefile ----src ----client ----main.go ----protocol ----type.go ----server ----main.go
rpc/Makefile
GOPATH := $(shell pwd)all: GOPATH=${GOPATH} go install client GOPATH=${GOPATH} go install server
rpc/protocol/type.go
package protocolconst( RPC_ADDITION = "Calculator.Addition" RPC_SUBTRACTION = "Calculator.Subtraction" RPC_MULTIPLICATION = "Calculator.Multiplication" RPC_DIVISION = "Calculator.Division")type Param struct { A int32 B int32}
RPC用戶端實現,包含同步(Call)和非同步(Go)調用方式,通常為了效率會使用非同步方式。
rpc/src/client/main.go
package mainimport "net/rpc"import ( . "protocol" "fmt" "time")var ( _CLIENT *rpc.Client _RPC_MSG chan *rpc.Call _CAN_CANCEL chan bool)func main() { DialRpcServer() //起個協程處理非同步rpc調用結果 go loop() //測試同步的方式調用rpc服務 param := Param{A:int32(10),B:int32(30)} reply := int32(0) SyncCallRpcFunc(RPC_ADDITION, ¶m, &reply) fmt.Printf("Sync Call Addition Result %d \n", reply) SyncCallRpcFunc(RPC_SUBTRACTION, ¶m, &reply) fmt.Printf("Sync Call Subtraction Result %d \n", reply) ////測試非同步方式調用rpc服務 ASyncCallRpcFunc(RPC_MULTIPLICATION, ¶m, &reply) ASyncCallRpcFunc(RPC_DIVISION, ¶m, &reply) //阻塞等待非同步呼叫完成 <- _CAN_CANCEL}func init(){ _RPC_MSG = make(chan *rpc.Call, 1024) _CAN_CANCEL = make(chan bool)}func DialRpcServer(){ c, e := rpc.DialHTTP("tcp", "127.0.0.1:2311") if e != nil { fmt.Errorf("Dial RPC Error %s", e.Error()) } _CLIENT = c}//重連RPC伺服器func ReDialRpcServer() bool{ c, e := rpc.DialHTTP("tcp", "127.0.0.1:2311") if e != nil { fmt.Printf("ReDial RPC Error %s \n", e.Error()) return false } _CLIENT = c fmt.Println("ReDial Rpc Server Succ") return true}//同步rpc調用func SyncCallRpcFunc(method string, args interface{}, reply interface{}){ if nil == _CLIENT{ for{//如果斷線就等到重連上為止 if ReDialRpcServer(){ break } time.Sleep(5000 * time.Millisecond) } } _CLIENT.Call(method, args, reply)}//非同步rpc調用func ASyncCallRpcFunc(method string, args interface{}, reply interface{}){ if nil == _CLIENT{ for{//如果斷線就等到重連上為止 if ReDialRpcServer(){ break } time.Sleep(5000 * time.Millisecond) } } // Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call)的done如果填nil會構建個新的channel用於接受結果 _CLIENT.Go(method, args, reply, _RPC_MSG)}//接收非同步呼叫的返回func loop(){ for{ select { case rpcMsg, ok := <- _RPC_MSG: if !ok{ fmt.Errorf("Rpc Call Error") } rpcMsgHandler(rpcMsg) } } _CAN_CANCEL <- true}// 處理非同步rpc的傳回值func rpcMsgHandler(msg * rpc.Call){ switch msg.ServiceMethod { case RPC_ADDITION: reply := msg.Reply.(*int32) fmt.Printf("Addtoion Result [%d] \n", *reply) case RPC_SUBTRACTION: reply := msg.Reply.(*int32) fmt.Printf("Subtraction Result [%d] \n", *reply) case RPC_MULTIPLICATION: reply := msg.Reply.(*int32) fmt.Printf("Multiplication Result [%d] \n", *reply) case RPC_DIVISION: reply := msg.Reply.(*int32) fmt.Printf("Division Result [%d] \n", *reply) default: fmt.Errorf("Can Not Handler Reply [%s] \n", msg.ServiceMethod) }}
RPC伺服器的實現。
rpc/src/server/main.go
package mainimport "net/rpc"import ( . "protocol" "errors" "net" "fmt" "net/http")type Calculator struct {}var ( _DATA *Calculator _CAN_CANCEL chan bool)func main() { runRpcServer()}func init(){ _DATA = new(Calculator) _CAN_CANCEL = make(chan bool)}func runRpcServer(){ //rpc包裡面定義了個DefaultServer,預設的Register和HandleHTTP均是對DefaultServer作的操作,如果想定製新的Server,就自己寫 rpc.Register(_DATA) rpc.HandleHTTP() l,e := net.Listen("tcp","127.0.0.1:2311") if e != nil{ fmt.Errorf("Create Listener Error %s", e.Error()) } go http.Serve(l, nil) //阻塞主進程,等待用戶端輸入 <-_CAN_CANCEL}//輸出方法的格式要求:func (t *T) MethodName(argType T1, replyType *T2) errorfunc (*Calculator) Addition(param *Param, reply *int32) error{ *reply = param.A + param.B return nil}func (*Calculator) Subtraction(param *Param, reply *int32) error{ *reply = param.A - param.B return nil}func (*Calculator) Multiplication(param *Param, reply *int32) error{ *reply = param.A * param.B return nil}func (*Calculator) Division(param *Param, reply *int32) error{ if 0 == param.B{ return errors.New("divide by zero") } *reply = param.A/param.B return nil}