Skip to content

快速入门使用

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

3. 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 

4. 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}