編程學(xué)習(xí)網(wǎng) > 編程語言 > go > RPC系列之基本概念及go語言使用實現(xiàn)
2020
04-17

RPC系列之基本概念及go語言使用實現(xiàn)


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í)資料

Python編程學(xué)習(xí)

查 看2022高級編程視頻教程免費(fèi)獲取