RPC 編程 -二-: 快速入門使用

  1. 介紹

Go語言標準包 (net/rpc) 已經提供了對RPC的支持,而且支持三個級別的 RPC:TCP、HTTP和JSONRPC。但Go語言的RPC包是獨一無二的RPC,它和傳統的 RPC 系統不同,它只支持 Go 語言開發的服務器與客戶端之間的交互,因爲在內部,它們採用了Gob來編碼。

廢話不多說, 下面會分別實現: TCP版、HTTP版、JSONRPC版的 RPC。開始擼代碼~~

2.TCP 版

Go 語言的 RPC 規則:方法只能有兩個可序列化的參數,其中第二個參數是指針類型,並且返回一個 error 類型,同時必須是公開的方法。

2.1 服務端實現

package main

import (
 "log"
 "net"
 "net/rpc"
 "time"
)

type HelloService struct{}

func (h *HelloService) Say(request string, response *string) error {
 format := time.Now().Format("2006-01-02 15:04:05")
 *response = request + " -- " + format
 return nil
}

func main() {
 // 註冊服務名稱
 _ = rpc.RegisterName("HelloService", new(HelloService))
 // 監聽端口
 listen, err := net.Listen("tcp"":1234")
 if err != nil {
  return
 }
 for {
  // 監聽請求
  accept, err := listen.Accept()
  if err != nil {
   log.Fatalf("Accept Error: %s", err)
  }
  go rpc.ServeConn(accept)
 }
}

rpc.RegisterName()函數調用會將對象類型中所有滿足 RPC 規則的對象方法註冊爲 RPC 函數,所有註冊的方法會放在HelloService服務的空間之下。然後建立一個唯一的 TCP 鏈接,並且通過rpc.ServeConn()函數在該 TCP 鏈接上爲對方提供 RPC 服務。

2.2 客戶端實現

package main

import (
 "fmt"
 "log"
 "net/rpc"
 "time"
)

func main() {
 // 建立鏈接
 dial, err := rpc.Dial("tcp""127.0.0.1:1234")
 if err != nil {
  log.Fatal("Dial error ", err)
 }
 var result string
 for i := 0; i < 5; i++ {
  // 發起請求
  _ = dial.Call("HelloService.Say""go"&result)
  fmt.Println("result:", result)
  time.Sleep(time.Second * 2)
 }
}

首先是通過rpc.Dial撥號RPC服務,然後通過client.Call()調用具體的RPC方法。在調用client.Call()時,第一個參數是用點號鏈接的RPC服務名字和方法名字,第二個和第三個參數分別是定義 RPC 方法的兩個參數。

2.3 啓動 & 請求

# 啓動服務
➜ go run  rpc/server/main.go
# 啓動客戶端
➜ go run rpc/client/main.go 
result: go -- 2022-01-08 21:42:18
result: go -- 2022-01-08 21:42:21
result: go -- 2022-01-08 21:42:23
result: go -- 2022-01-08 21:42:25
result: go -- 2022-01-08 21:42:27
  1. HTTP 版

3.1 服務端實現

package main

import (
 "fmt"
 "net/http"
 "net/rpc"
)

type MathService struct {
}

// 相乘方法
func (u *MathService) Multi(a int, sum *int) error {
 *sum = a * a
 return nil
}

func main() {
 userService := new(MathService)
 // 註冊服務
 err := rpc.Register(userService)
 if err != nil {
  return
 }
 rpc.HandleHTTP()
 err = http.ListenAndServe(":8080", nil)
 if err != nil {
  fmt.Println(err)
 }
}

3.2 客戶端實現

package main

import (
 "fmt"
 "net/rpc"
)

func main() {
 // 建立鏈接
 client, err := rpc.DialHTTP("tcp"":8080")
 if err != nil {
  fmt.Println("err ", err)
  return
 }
 // 返回
 var result int
 //  請求方法
 for i := 1; i < 10; i++ {
  err = client.Call("MathService.Multi", i, &result)
  fmt.Printf("i:%v result:%v \n", i, result)
 }
}

3.3 啓動 & 請求

# 啓動服務端
➜ go run rpc/http/server.go

 # 啓動客戶端
➜ go run rpc/http/client.go
i:1 result:1 
i:2 result:4 
i:3 result:9 
i:4 result:16 
i:5 result:25 
i:6 result:36 
i:7 result:49 
i:8 result:64 
i:9 result:81
  1. JSON RPC

上面TCP版HTTP版, 數據編碼採用的都是默認的gob編碼,而gob編碼是Go特有的編碼和解碼的專用序列化方法,這也就意味着Gob無法跨語言使用。而JSON RPC則可以跨語言使用。

4.1 服務端實現

package main

import (
 "52lu/go-study-example/rpc/jsonrpc/dto"
 "fmt"
 "net"
 "net/rpc"
 "net/rpc/jsonrpc"
 "time"
)

type MathService struct {
}

// 求和
func (m MathService) Sum(arg *dto.SumParam, res *dto.SumRes) error {
 fmt.Printf("arg: %#v\n", arg)
 *res = dto.SumRes{
  Sum:  arg.A + arg.B,
  Time: time.Now(),
 }
 return nil
}

func main() {
 // 註冊服務
 err := rpc.RegisterName("MathService", new(MathService))
 if err != nil {
  fmt.Println("rpc RegisterName err ", err)
  return
 }
 // 監聽端口
 listen, err := net.Listen("tcp"":9090")
 if err != nil {
  fmt.Println("Listen err ", err)
  return
 }
 // 監聽
 for {
  conn, err := listen.Accept()
  if err != nil {
   fmt.Println("conn err ", err)
   return
  }
  // 使用json編碼
  go rpc.ServeCodec(jsonrpc.NewServerCodec(conn))
 }
}

4.2 客戶端實現

package main

import (
 "52lu/go-study-example/rpc/jsonrpc/dto"
 "fmt"
 "net"
 "net/rpc"
 "net/rpc/jsonrpc"
)

func main() {
 // 建立鏈接
 conn, err := net.Dial("tcp"":9090")
 if err != nil {
  fmt.Println(" rpc.Dial err ", err)
  return
 }
 // 使用json編碼
 client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))
 // 參數
 arg := dto.SumParam{A: 20, B: 80}
 // 結果
 var sum dto.SumRes
 err = client.Call("MathService.Sum"&arg, &sum)
 if err != nil {
  fmt.Println("client.Call err ", err)
  return
 }
 fmt.Printf("res:%+v \n", sum)
}

4.3 啓動 & 請求

# 啓動服務端
➜ go run rpc/jsonrpc/server.go
arg: &dto.SumParam{A:20, B:80} # 請求時打印

# 啓動客戶端
➜ go run rpc/jsonrpc/client.go
res:{Sum:100 Time:2022-01-10 23:12:48.880364 +0800 CST}
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/5RSkC4mM0q2ulP5VzOplgw