基于RPC协议实现微服务
- 基于rpc实现跨语言调用,不限定服务提供方使用的语言
- 在微服务架构中,每个微服务都被封装成了进程,相互独立
- 在这里提供了客户端和服务端演示调用示例
1 )服务端
- 新建 server/msg 目录,后执行 $
go mod init msg
- 编辑 server/msg/main.go 文件
package main // net 模块 gin框架内置 import ( "fmt" "net" "net/rpc" ) // 定义一个可供远程调用的struct type Msg struct {} // 为Msg这个struct添加被调用函数 GetReplay func (this Msg) GetReplay(req string, res *string) error { fmt.Println(req) // 服务端输出日志:请求参数 *res = "这是服务端返回的信息:已收到客户端的请求参数,参数为:" + req // 这是返回信息 return nil } func main() { // 1. 注册RPC服务 err1 := rpc.RegisterName("msg", new(Msg)) // 错误处理 if err1 != nil { fmt.Println(err1) } // 2、监听端口 listener, err2 := net.Listen("tcp", "127.0.0.1:3000") if err2 != nil { fmt.Println(err2) } // 3、应用退出的时候关闭监听端口 defer listener.Close() for { // 4、建立链接 conn, err3 := listener.Accept() // 错误处理 if err3 != nil { fmt.Println(err3) } fmt.Print("客户端参数为: "); // 5. 绑定服务 rpc.ServeConn(conn) } }
- net包是一个 go 内置的一个模块
- 准备调用函数
Msg.GetReplay
- 此方法只能有两个可序列化的参数,注意第二个参数是指针类型
req
表示获取客户端传过来的数据res
表示给客户端返回数据 (指针类型)
- 方法要返回一个 error 类型,同时必须是公开的方法
- req 和 res的类型不能是 channel(通道)、func(函数)
- 因为它们均不能进行序列化
- 此方法只能有两个可序列化的参数,注意第二个参数是指针类型
- 整个流程如下:
- 1 )通过
rpc.RegisterName
注册 rpc 服务,注意这里的 name 后期被客户端调用 - 2 )通过
net.Listen
监听rpc服务,得到listener
对象 - 3 )通过
defer listener.Close()
应用退出时关闭监听端口 - 4 )基于无限循环做响应处理,这里有两个环节
- 4.1 )通过
listener.Accept()
接收连接信息,得到conn
连接对象 - 4.2 )通过
rpc.ServeConn(conn)
绑定服务
- 4.1 )通过
- 1 )通过
3 )客户端
-
新建 client 目录,后执行 $
go mod init client
-
这里在 client 项目目录中,新建一个 main.go 文件
-
编辑 client/main.go 文件
package main import ( "fmt" "net/rpc" ) func main() { // 1. 用 rpc.Dial和rpc微服务端建立连接 conn, err1 := rpc.Dial("tcp", "127.0.0.1:3000") // 错误处理 if err1 != nil { fmt.Println(err1) } // 2、当客户端退出的时候关闭连接 defer conn.Close() // 3. 调用远程函数 var reply string err2 := conn.Call("msg.GetReplay", "滴滴滴", &reply) // 错误处理 if err2 != nil { fmt.Println(err2) } // 4、获取微服务返回的数据 fmt.Println(reply) }
-
这里是客户端,需要经过以下几个步骤:
-
1 )要连接的话,首先要建立连接
rpc.Dial
得到conn
连接对象 -
2 )添加客户端退出时的关闭连接处理
defer conn.Close()
-
3 )基于
conn.Call
调用远程函数- 第一个参数,
服务名.远程方法
的字符串形式 - 第二个参数,给服务端的参数
- 第三个参数,服务端返回的数据地址
- 第一个参数,
3 )部署服务
- 先启动服务端,再启动客户端
- 当服务端启动的瞬间
- 服务端接收到客户端的参数打印到服务端
- 客户端接收到服务端的返回信息
注意
- 在Golang中实现RPC非常简单,有封装好的官方库和一些第三方库提供支持
- Go RPC 可以利用tcp或http来传递数据,可以对要传递的数据使用多种类型的编解码方式
- Golang 官方的 net/rpc 库使用
encoding/gob
进行编解码,支持tcp或http数据传输方式 - 由于其他语言不支持 gob 编解码方式,所以使用 net/rpc 库实现的 RPC 方法没办法进行跨语言调用
- 另外,Golang官方还提供了 net/rpc/jsonrpc 库实现RPC方法,JSON RPC采用JSON进行数据编解码
- 因而支持跨语言调用,但目前的 jsonrpc 库是基于tcp协议实现的,暂时不支持使用http进行数据传输
- 除了Golang官方提供的rpc库,还有许多第三方库为在 Golang 中实现RPC提供支持
- 大部分第三方 RPC 库的实现都是使用 protobuf 进行数据编解码
- 根据 protobuf 声明文件自动生成 RPC 方法定义与服务注册代码
- 在 Golang 中可以很方便的进行 RPC 服务调用