go RPC 开发

1 RPC

RPC 叫做远程过程调用,有的时候也叫做远程方法或者远程函数调用

这两个进程进行交换数据,那他就必须建立网络进行交换,交换数据的话无非就是有一端是服务器端,有一端是客户端。然后客户端要给服务器端发送一个请求,服务器端就需要给客户端一个响应。

请求和响应的话无非就是请求的数据,数据的话主要有编码和解码,当服务器只提供一个功能的时候,那我们只要把一个功能的数据交给他即可。

但是如何服务器提供了多个功能的时候,比如 ls、get、put、delete 这些功能的时候,那我需要在传递的数据中还要包含我们的操作。而我们在调用这些功能的时候其实就类似于远程的方法调用,因为这个本身就是在调用服务器端的方法和函数上。

RPC 即远程过程调用(Remote Procedure Call),用于构建计算机之间的通信协议,该协议允许运行于一台计算机的程序调用另一台计算机上的程序,开发人员无需对交互过程进行编程

  • rpc 和 rpc/jsonrpc 包提供了对 RPC 的支持。

    • rpc 构建于 TCP 或 HTTP 协议之上,底层数据编码使用 gob,因 gob 编码为 golang 自定义,所以无法支持跨语言调用;

    • rpc/jsonrpc 构建于 TCP 协议之上,底层数据编码使用 json,可支持跨语言调用

2 RPC 包

在 go 中提供了 RPC 包。

  • 服务器端提供的功能

    • 编解码

    • 监听服务/接受请求

    • 函数调用

    • 响应

  • 客户端提供的功能

    • 连接服务器

    • 发送请求

    • 处理结果

rpc 使用过程中,就像调用本地方法一样去调用远程方法。

rpc 没有限制底层协议,所以我们可以通过 udp/tcp 或者 http 协议来做。因为 http 也是基于在 tcp 之上的

go 里面的 rpc 其实提供了两种:

  1. rpc :协议可以使用 tcp/http,编码实现的是 gob 编码

  2. jsonrpc:协议建立在 tcp ,编码实现的是 json 编码

net/rpc 包

  • 常用函数

    • Register:注册 RPC 服务

    • RegisterName:注册 RPC 服务,并设置服务名称

    • HandleHTTP: 使用 HTTP 构建的 RPC 服务

    • ServeConn:处理客户端连接

    • Accept:监听客户端连接并调用 ServeConn 进行处理

常用结构体

  • Server:RPC 服务端

    • 常用函数

      ➢ NewServer:创建 RPC 服务端

    • 常用方法

      ➢ Register:注册 RPC 服务

      ➢ RegisterName:注册 RPC 服务,并设置服务名称

      ➢ HandleHTTP:使用 HTTP 构建的 RPC 服务

      ➢ ServeConn:处理客户端连接

      ➢ Accept:监听客户端连接并调用 ServeConn 进行处理

  • Client:RPC 客户端

    • 常用函数

      ➢ Dial:连接使用 TCP 构建的 RPC 服务

      ➢ DialHTTP:连接使用 HTTP 构建的 RPC 服务

      ➢ NewClient:创建 RPC 客户端

    • 常用方法

      ➢ Call:同步调用 RPC 服务

      ➢ Go:异步调用 RPC 服务

      ➢ Close:关闭客户端连接

3 TCP RPC 范例

在 rpc 中由于传递的是数据,所以他有一个编解码的过程,也就是我们需要提供一个编码和解码在 go 里面的对象。

编码和解码是将一个东西转换为另一个东西,肯定是把我协议请求中的数据,和 go 程序中的对象进行转换了

所以我们就要定义这个对象,这个对象分为两种,也就是当我们请求的时候请求的对象,响应的时候响应的对象。

也就是说我们自己定义 RPC 的话就需要自己定义请求对象和响应对象,还有他的多个操作函数,因为这种函数也就是对应的各种远程调用操作。那么我就可以明白一点,至少请求就是方法或者说函数的输入,响应呢就是方法或者函数的返回值或者说参数

3.1 rpc 服务端开发

server 编解码需要使用到 响应对象 和 请求对象,一般我们会把编解码的过程放到公共的包中,因为客户端和服务器端都需要使用

package main

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

// 远程服务 Add(1,2) int 最后返回 1 + 2 的和
// 1.定义 AddRequest 请求结构体,并且属性首字母一定要大写,因为要提供给 rpc 包进行访问,设置为公开
type AddRequest struct {
    Left  int
    Right int
}

// 2.定义 AddResponse 响应信息结构体,并且属性首字母一定要大写,因为要提供给 rpc 包进行访问,设置为公开
type AddResponse struct {
    Result int
}

// 3.定义 Calc 计算器结构体
type Calc struct{}

// 4.定义 Add 方法,
// 参数 1 req:请求对象(可以是指针/值)
// 参数 2 resq:响应对象(是指针)
// 在 rpc 方法中 返回值 必须是 error
func (c *Calc) Add(req AddRequest, resq *AddResponse) error {
    fmt.Println("calc.Add 启动")
    // 将 AddRequest 结构体两个属性相加,赋值给 AddResponse 的 resq.result 属性
    resq.Result = req.Left + req.Right
    return nil
}

type AAA struct{}

func (a *AAA) Add(req AddRequest, resq *AddResponse) error {
    fmt.Println("AAA.a 方法")
    return nil
}

func main() {
    // 注册,也就是告诉 rpc 我需要传递那个函数给客户端
    // 这里传递 &Calc{} 结构体
    rpc.Register(&Calc{})

    // 这里传递 &AAA{} 结构体
    rpc.Register(&AAA{})

    // 开启 rpc 之前需要先启动监听
    addr := ":8888"
    network := "tcp"
    listens, _ := net.Listen(network, addr)

    // 启动 rpc
    // rpc.Accept() 能接收来自 net.Listener 的链接,能够处理数据,并且自动实现了循环结束
    rpc.Accept(listens)

    listens.Close()
}

启动服务端

[08:01:28 root@go tcprpc]#go run server.go 

3.2 rpc 客户端开发

client 编解码需要使用到 响应对象 和 请求对,一般我们会把编解码的过程放到公共的包中,因为客户端和服务器端都需要使用。

func rpc.Dial(network string, address string) (*rpc.Client, error)

func (*rpc.Client).Call(serviceMethod string, args interface{}, reply interface{}) error

// serviceMethod:指定调用服务器端方法名称
// args:请求信息
// reply:响应信息

package main

import (
    "fmt"
    "net/rpc"
)

// 1.定义 AddRequest 请求结构体
type AddRequest struct {
    Left  int
    Right int
}

// 2.定义 AddResponse 响应信息结构体
type AddResponse struct {
    Result int
}

func main() {
    addr := "10.0.0.10:8888"
    network := "tcp"

    // 由于是 rpc 链接所以使用 rpc.Dial
    // 创建客户端链接
    client, err := rpc.Dial(network, addr)
    if err != nil {
        fmt.Println(err)
        return
    }

    // 调用编解码过程
    req := AddRequest{2, 3} // 传递 2,3 等会交给服务器端的 Calc{} 结构体处理
    resq := AddResponse{}

    // 调用服务器端方法
    // 在调用的时候是 结构体名.方法如:Calc.Add 接调用服务器端的 Calc.Add 方法
    // 在 Call 调用的时候肯定会修改 resq 的属性所以这里传递指针
    err = client.Call("Calc.Add", req, &resq)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(resq)

    client.Close()
}

启动客户端,客户端输出 2+3 = 5 ,服务器端输出 calc.Add 方法启动

这就是远程的方法调用

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇