扫码订阅《 Go语言微服务框架学习实践》或入驻星球,即可阅读文章!

GOLANG ROADMAP

阅读模式

  • 沉浸
  • 自动
  • 日常
首页
Go友会
  • 城市
  • 校园
Go学院
  • Go小课
  • Go小考
  • Go实战
  • 精品课
Go求职
  • 求职辅导🔥
  • Offer收割社群
  • 企业题库
  • 面试宝典
Go宝典
  • 在线宝典
  • B站精选
  • 推荐图书
  • 每日博文
Go仓库
实验区
  • Go周边
  • Go下载
  • Go月刊
消息
更多
  • 用户中心

    • 我的信息
    • 推广返利
  • 玩转星球

    • 星球介绍
    • 角色体系
    • 星主权益
  • 支持与服务

    • 联系星主
    • 成长记录
    • 常见问题
    • 吐槽专区
  • 合作交流

    • 渠道合作
    • 课程入驻
    • 友情链接
author-avatar

GOLANG ROADMAP


首页
Go友会
  • 城市
  • 校园
Go学院
  • Go小课
  • Go小考
  • Go实战
  • 精品课
Go求职
  • 求职辅导🔥
  • Offer收割社群
  • 企业题库
  • 面试宝典
Go宝典
  • 在线宝典
  • B站精选
  • 推荐图书
  • 每日博文
Go仓库
实验区
  • Go周边
  • Go下载
  • Go月刊
消息
更多
  • 用户中心

    • 我的信息
    • 推广返利
  • 玩转星球

    • 星球介绍
    • 角色体系
    • 星主权益
  • 支持与服务

    • 联系星主
    • 成长记录
    • 常见问题
    • 吐槽专区
  • 合作交流

    • 渠道合作
    • 课程入驻
    • 友情链接
  • 《Go语言微服务框架学习实践》

    • 课程介绍
  • RPC远程调用机制

    • 第1节:RPC简介及原理介绍
    • 第2节:Go语言实现RPC编程
    • 第3节:RPC与Protobuf结合使用
  • gRPC微服务框架

  • go-micro微服务框架

扫码订阅《 Go语言微服务框架学习实践》或入驻星球,即可阅读文章!

第2节:Go语言实现RPC编程


GOLANG ROADMAP

上节课我们对RPC知识做了介绍,讲解了RPC的原理,通过图示方式讲解了RPC的内部执行过程。本节课,我们继续来学习RPC相关的内容。

# RPC官方库

在Go语言官方网站的pkg说明中,提供了官方支持的rpc包,具体链接如下:https://golang.org/pkg/net/rpc/。官方提供的rpc包完整的包名是:net/rpc。根据官方的解释,rpc包主要是提供通过网络访问一个对象方法的功能。

本节课,我们就来学习如何使用go语言官方提供的RPC包实现RPC调用编码。

# net/rpc库实现RPC调用编程

前文我们已经讲过rpc调用有两个参与者,分别是:客户端(client)和服务器(server)。

首先是提供方法暴露的一方–服务器。

# 一、服务定义及暴露

在编程实现过程中,服务器端需要注册结构体对象,然后通过对象所属的方法暴露给调用者,从而提供服务,该方法称之为输出方法,此输出方法可以被远程调用。当然,在定义输出方法时,能够被远程调用的方法需要遵循一定的规则。我们通过代码进行讲解:

func (t *T) MethodName(request T1,response *T2) error
1

上述代码是go语言官方给出的对外暴露的服务方法的定义标准,其中包含了主要的几条规则,分别是:

  • 1、对外暴露的方法有且只能有两个参数,这个两个参数只能是输出类型或内建类型,两种类型中的一种。
  • 2、方法的第二个参数必须是指针类型。
  • 3、方法的返回类型为error。
  • 4、方法的类型是可输出的。
  • 5、方法本身也是可输出的。

我们举例说明:假设目前我们有一个需求,给出一个float类型变量,作为圆形的半径,要求通过RPC调用,返回对应的圆形面积。具体的编程实现思路如下:

type MathUtil struct{
}
//该方法向外暴露:提供计算圆形面积的服务
func (mu *MathUtil) CalculateCircleArea(req float32, resp *float32) error {
    *resp = math.Pi * req * req //圆形的面积 s = π * r * r
    return nil //返回类型
}
1
2
3
4
5
6
7

在上述的案例中,我们可以看到:

  • 1、Calculate方法是服务对象MathUtil向外提供的服务方法,该方法用于接收传入的圆形半径数据,计算圆形面积并返回。
  • 2、第一个参数req代表的是调用者(client)传递提供的参数。
  • 3、第二个参数resp代表要返回给调用者的计算结果,必须是指针类型。
  • 4、正常情况下,方法的返回值为是error,为nil。如果遇到异常或特殊情况,则error将作为一个字符串返回给调用者,此时,resp参数就不会再返回给调用者。

至此为止,已经实现了服务端的功能定义,接下来就是让服务代码生效,需要将服务进行注册,并启动请求处理。

# 二、注册服务及监听请求

net/rpc包为我们提供了注册服务和处理请求的一系列方法,结合本案例实现注册及处理逻辑,如下所示:

//1、初始化指针数据类型
mathUtil := new(MathUtil) //初始化指针数据类型

//2、调用net/rpc包的功能将服务对象进行注册
err := rpc.Register(mathUtil)
if err != nil {
    panic(err.Error())
}

//3、通过该函数把mathUtil中提供的服务注册到HTTP协议上,方便调用者可以利用http的方式进行数据传递
rpc.HandleHTTP()

//4、在特定的端口进行监听
listen, err := net.Listen("tcp", ":8081")
if err != nil {
    panic(err.Error())
}
go http.Serve(listen, nil)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

经过服务注册和监听处理,RPC调用过程中的服务端实现就已经完成了。接下来需要实现的是客户端请求代码的实现。

# 三、客户端调用

在服务端是通过Http的端口监听方式等待连接的,因此在客户端就需要通过http连接,首先与服务端实现连接。

  • 客户端连接服务端

    client, err := rpc.DialHTTP("tcp", "localhost:8081")
        if err != nil {
            panic(err.Error())
        }
    
    1
    2
    3
    4
  • 远端方法调用 客户端成功连接服务端以后,就可以通过方法调用调用服务端的方法,具体调用方法如下:

    var req float32 //请求值
    req = 3
    
    var resp *float32 //返回值
    err = client.Call("MathUtil.CalculateCircleArea", req, &resp)
    if err != nil {
    panic(err.Error())
    }
    fmt.Println(*resp)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    上述的调用方法核心在于client.Call方法的调用,该方法有三个参数,第一个参数表示要调用的远端服务的方法名,第二个参数是调用时要传入的参数,第三个参数是调用要接收的返回值。 上述的Call方法调用实现的方式是同步的调用,除此之外,还有一种异步的方式可以实现调用。异步调用代码实现如下:

    var respSync *float32
    //异步的调用方式
    syncCall := client.Go("MathUtil.CalculateCircleArea", req, &respSync, nil)
    replayDone := <-syncCall.Done
    fmt.Println(replayDone)
    fmt.Println(*respSync)
    
    1
    2
    3
    4
    5
    6

# 多参数的请求调用参数传递

上述内容演示了单个参数下的RPC调用,对于多参数下的请求该如何实现。我们通过另外一个案例进行演示。

假设现在需要实现另外一个需求:通过RPC调用实现计算两个数字相加功能并返回计算结果。此时,就需要传递两个参数,具体实现如下:

将参数定义在一个新的结构体中,存放在param包中:

type AddParma struct {
    Args1 float32 //第一个参数
    Args2 float32 //第二个参数
}
1
2
3
4

在server.go文件中,实现两数相加的功能,并实现服务注册的逻辑:

func (mu *MathUtil) Add(param param.AddParma, resp *float32) error {
    *resp = param.Args1 + param.Args2 //实现两数相加的功能
    return nil
}
mathUtil := new(MathUtil)

    err := rpc.RegisterName("MathUtil", mathUtil)
    if err != nil {
        panic(err.Error())
    }

    rpc.HandleHTTP()

    listen, err := net.Listen("tcp", ":8082")
    http.Serve(listen, nil)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

在本案例中,我们通过新的注册方法rpc.RegisterName实现了服务的注册和调用。

至此,我们已经完成了net/rpc包的最基础的使用。

  • RPC官方库
  • net/rpc库实现RPC调用编程
  • 一、服务定义及暴露
  • 二、注册服务及监听请求
  • 三、客户端调用
  • 多参数的请求调用参数传递