1、基本使用
1.1、概念梳理
TPC/IP協(xié)議是傳輸層協(xié)議,主要解決數(shù)據(jù)如何在網(wǎng)絡(luò)中傳輸,而HTTP是應(yīng)用層協(xié)議,主要解決如何包裝數(shù)據(jù)。
什么是RPC?
遠(yuǎn)程過程調(diào)用(Remote Procedure Call,縮寫為 RPC)是一個計算機(jī)通信協(xié)議。該協(xié)議允許運(yùn)行于一臺計算機(jī)的程序調(diào)用另一臺計算機(jī)的子程序,而程序員無需額外地為這個交互作用編程。如果涉及的軟件采用面向?qū)ο缶幊蹋敲催h(yuǎn)程過程調(diào)用亦可稱作遠(yuǎn)程調(diào)用或遠(yuǎn)程方法調(diào)用。
web3 和 http , RPC 之間的關(guān)系。
HttpProvider should be used to send rpc calls over http
web3有兩種Provider, 一種是HttpProvider, 一種是IpcProvider
web3是通過http, ws, 發(fā)送Rpc調(diào)用,然后把Rpc調(diào)用的結(jié)果通過http,或者WS返回回來。
RPC主要是基于TCP/IP協(xié)議,而http服務(wù)則是基于HTTP協(xié)議
RPC(即Remote Procedure Call,遠(yuǎn)程過程調(diào)用)和HTTP(HyperText Transfer Protocol,超文本傳輸協(xié)議)他們最本質(zhì)的區(qū)別,就是RPC主要工作在TCP協(xié)議之上,而HTTP服務(wù)主要是工作在HTTP協(xié)議之上,我們都知道HTTP協(xié)議是在傳輸層協(xié)議TCP之上的,所以效率來看的話,RPC當(dāng)然是要更勝一籌。
HTTP與RPC存在重大不同的是:請求是使用具有標(biāo)準(zhǔn)語義的通用的接口定向到資源的,這些語義能夠被中間組件和提供服務(wù)的來源機(jī)器進(jìn)行解釋。結(jié)果是使得一個應(yīng)用支持分層的轉(zhuǎn)換(layers of transformation)和間接層(indirection),并且獨(dú)立于消息的來源,這對于一個Internet規(guī)模、多個組織、無法控制的可伸縮性的信息系統(tǒng)來說,是非常有用的。與之相比較,RPC的機(jī)制是根據(jù)語言的API(language API)來定義的,而不是根據(jù)基于網(wǎng)絡(luò)的應(yīng)用來定義的。
調(diào)用遠(yuǎn)程機(jī)器上的一個過程(procedure)的觀念,是RPC與其他形式的基于網(wǎng)絡(luò)的應(yīng)用通信的區(qū)別所在。
RPC(Remote Procedure Call,遠(yuǎn)程過程調(diào)用)是建立在Socket之上的,出于一種類比的愿望,在一臺機(jī)器上運(yùn)行的主程序,可以調(diào)用另一臺機(jī)器上準(zhǔn)備好的子程序,就像 LPC(本地過程調(diào)用).RPC帶來了開發(fā)C/S程序的簡單可靠的手段,它通過一種叫XDR的數(shù)據(jù)表達(dá)方法描述數(shù)據(jù),程序員書寫偽代碼,然后由 rpcgen程序翻譯為真正的可編譯的C語言源代碼,再編譯成真正的Client端和Server端程序。
RPC是在Socket的基礎(chǔ)上實現(xiàn)的,它比socket需要更多的網(wǎng)絡(luò)和系統(tǒng)資源.另外,在對程序優(yōu)化時,程序員雖然可以直接修改由rpcgen產(chǎn)生的令人費(fèi)解的源程序,但對于追求程序設(shè)計高效率的RPC而言,獲得的簡單性則被大大削弱.
1.2、go 語言 RPC實現(xiàn)方式
1. net/rpc庫
rpc.server
package main import ( "net/rpc" "net" ) type HelloService struct { } func (this *HelloService) SayHello(req string, reply *string) error { *reply = "hello:" + req return nil } func main() { rpc.RegisterName("HelloService", new(HelloService)) listerner, err := net.Listen("tcp", ":9900") if err != nil { panic(err) } for { conn, err := listerner.Accept() if err != nil { panic(err) } rpc.ServeConn(conn) } }
rpc_client.go
package main import "net/rpc" func main() { client, err := rpc.Dial("tcp", "localhost:9900") if err != nil { panic(err) } reply := "" err = client.Call("HelloService.SayHello", "alice", &reply) if err != nil { panic(err) } println(reply) }
2.net/rpc/jsonrpc庫
rpc_server.go
package main import ( "net" "net/rpc" "net/rpc/jsonrpc" ) //注意字段必須是導(dǎo)出 type Params struct { Width, Height int } type Rect struct{} func (r *Rect) Area(p Params, ret *int) error { *ret = p.Width * p.Height return nil } func main() { rect := new(Rect) //注冊rpc服務(wù) rpc.Register(rect) //監(jiān)聽端口 tcplisten, _ := net.Listen("tcp", ":8080") for { conn, err3 := tcplisten.Accept() if err3 != nil { continue } //這里使用jsonrpc進(jìn)行處理 go rpc.ServeCodec(jsonrpc.NewServerCodec(conn)) //go jsonrpc.ServeConn(conn) } }
rpc_client.go
package main import ( "fmt" "log" "net/rpc/jsonrpc" ) //注意字段必須是導(dǎo)出 type Params1 struct { Width, Height int } func main() { //連接遠(yuǎn)程rpc服務(wù) //這里使用jsonrpc.Dial rpc, err := jsonrpc.Dial("tcp", "127.0.0.1:8080") if err != nil { log.Fatal(err) } ret := 0 //調(diào)用遠(yuǎn)程方法 //注意第三個參數(shù)是指針類型 err2 := rpc.Call("Rect.Area", Params1{50, 100}, &ret) if err2 != nil { log.Fatal(err2) } fmt.Println(ret) }
3.http上的RPC
rpc_server.go
package main import ( "net/rpc" "net" "log" "net/http" ) //自己的數(shù)據(jù)類 type MyMath struct{ } //加法--只能兩個參數(shù)--方法名第一個字母必須大寫 func (mm *MyMath) Add(num map[string]int,reply *int) error { *reply = num["num1"] + num["num2"] return nil } func main() { //注冊MyMath類,以代客戶端調(diào)用 rpc.Register(new(MyMath)) rpc.HandleHTTP() l, e := net.Listen("tcp", ":1215") if e != nil { log.Fatal("listen error:", e) } http.Serve(l, nil) }
rpc_client.go
package main import ( "net/rpc" "fmt" "log" ) func main() { //連接服務(wù) client, err := rpc.DialHTTP("tcp", "127.0.0.1:1215") if err != nil { log.Fatal("dialing:", err) } var reply int var num = make(map[string]int) num["num1"] = 3 num["num2"] = 2 //調(diào)用遠(yuǎn)程MyMath的Add方法,也只能是三個參數(shù) err = client.Call("MyMath.Add",num,&reply) if err != nil { log.Fatal("arith error:", err) } //輸出結(jié)果 fmt.Println(reply) client.Close() }
4.protobuf版rpc
proto文件
syntax = "proto3"; package pb; // 算術(shù)運(yùn)算請求結(jié)構(gòu) message ArithRequest { int32 a = 1; int32 b = 2; } // 算術(shù)運(yùn)算響應(yīng)結(jié)構(gòu) message ArithResponse { int32 pro = 1; // 乘積 int32 quo = 2; // 商 int32 rem = 3; // 余數(shù) } // rpc方法 service ArithService { rpc multiply (ArithRequest) returns (ArithResponse); // 乘法運(yùn)算方法 rpc divide (ArithRequest) returns (ArithResponse); // 除法運(yùn)算方法 }
rpc_server.go
package main import ( "test/rpc/pb" ) // 算術(shù)運(yùn)算結(jié)構(gòu)體 type Arith struct { } // 乘法運(yùn)算方法 func (this *Arith) Multiply(req *pb.ArithRequest, res *pb.ArithResponse) error { res.Pro = req.GetA() * req.GetB() return nil } func main() { pb.ListenAndServeArithService("tcp", "127.0.0.1:8097", new(Arith)) }
rpc_client.go
package main import ( "fmt" "log" "test/rpc/pb" ) func main() { conn, err := pb.DialArithService("tcp", "127.0.0.1:8097") if err != nil { log.Fatalln("dailing error: ", err) } defer conn.Close() req := &pb.ArithRequest{9, 2} res, err := conn.Multiply(req) if err != nil { log.Fatalln("arith error: ", err) } fmt.Printf("%d * %d = %d\n", req.GetA(), req.GetB(), res.GetPro()) }
掃碼二維碼 獲取免費(fèi)視頻學(xué)習(xí)資料
- 本文固定鏈接: http://www.wangchenghua.com/post/7265/
- 轉(zhuǎn)載請注明:轉(zhuǎn)載必須在正文中標(biāo)注并保留原文鏈接
- 掃碼: 掃上方二維碼獲取免費(fèi)視頻資料