RPC
RPC(Remote Procedure Call)远程过程调用的缩写,通俗的说就是调用远处的一个函数,是分布式系统中不同节点间流行的通信方式。
创建rpc微服务
package main
import (
"fmt"
"net"
"net/rpc"
)
// 定义一个远程调用的方法
type Hello struct{}
// 1、方法只能有两个可序列化的的参数,第二个参数是指针类型
// req 表示获取客户端传过来的数据
// res 表示给客户端返回类型(指针类型)
// 2、方法要返回一个error类型,同时必须是公开的方法
// 3、参数类型不能是channel、func类型
func (hl Hello) SayHello(req string, res *string) error {
fmt.Println(req)
*res = "你好" + req
return nil
}
func main() {
// 1、注册RPC服务
err1 := rpc.RegisterName("hello", new(Hello))
if err1 != nil {
fmt.Println(err1)
}
//2、监听端口
listener, err2 := net.Listen("tcp", "127.0.0.1:8080")
if err2 != nil {
fmt.Println(err2)
}
// 3、应用退出的时候关闭监听端口
defer listener.Close()
for {
fmt.Println("开始建立连接")
// 4、建立连接
conn, err3 := listener.Accept()
if err3 != nil {
fmt.Println(err3)
}
// 5、绑定服务
rpc.ServeConn(conn)
}
}
package main
import (
"fmt"
"net/rpc"
)
func main() {
// 1、通过 rpc.Dial 和 rpc为服务端建立连接
conn, err1 := rpc.Dial("tcp", "127.0.0.1:8080")
if err1 != nil {
fmt.Println(err1)
}
// 2、客户端退出的时候关闭连接
defer conn.Close()
// 3、调用远程函数
var reply string //接受返回值
/*
Call的三个参数
1、第一个参数 hello.SayHello hello表示服务器 SayHello方法名称
2、第二个参数 给服务器的req 传递数据
3、第三个参数 需要传入地址 获取微服务端返回的数据
*/
err2 := conn.Call("hello.SayHello", "我是客户端", &reply)
if err2 != nil {
fmt.Println(err2)
}
// 4、获取微服务返回的数据
fmt.Println(reply)
}
创建goodsRPC微服务
package main
import (
"fmt"
"net"
"net/rpc"
)
// 1、创建远程调用的函数,函数一般是放在结构体里面
type Goods struct{}
// AddGoods参数对营的结构团体
type AddGoodsReq struct {
Id int
Title string
Price float32
Content string
}
type AddGoodsRes struct {
Success bool
Message string
}
type GetGoodsReq struct {
Id int
}
type GetGoodsRes struct {
Id int
Title string
Price float32
Content string
}
func (g Goods) AddGoods(req AddGoodsReq, res *AddGoodsRes) error {
// 1、执行增加
fmt.Println(req)
// 2、返回增加结果
*res = AddGoodsRes{
Success: true,
Message: "增加数据成功",
}
return nil
}
func (g Goods) GetGoods(req GetGoodsReq, res *GetGoodsRes) error {
// 1、执行增加
fmt.Println(req)
// 2、返回增加结果
*res = GetGoodsRes{
Id: 12,
Title: "服务器获取的数据",
Price: 24.5,
Content: "我是服务器获取的内容",
}
return nil
}
func main() {
// 1、注册RPC服务
err1 := rpc.RegisterName("goods", new(Goods))
if err1 != nil {
fmt.Println(err1)
}
// 2、监听端口
listener, err2 := net.Listen("tcp", "127.0.0.1:8081")
if err2 != nil {
fmt.Println(err2)
}
//3、关闭监听
defer listener.Close()
for {
//4、监听客户端链接
fmt.Println("准备建立连接...")
conn, err3 := listener.Accept()
if err3 != nil {
fmt.Println(err3)
}
rpc.ServeConn(conn)
}
}
package main
import (
"fmt"
"net/rpc"
)
type AddGoodsReq struct {
Id int
Title string
Price float32
Content string
}
type AddGoodsRes struct {
Success bool
Message string
}
type GetGoodsReq struct {
Id int
}
type GetGoodsRes struct {
Id int
Title string
Price float32
Content string
}
func main() {
// 1、通过 rpc.Dial 和 rpc为服务端建立连接
conn, err1 := rpc.Dial("tcp", "127.0.0.1:8081")
if err1 != nil {
fmt.Println(err1)
}
// 2、客户端退出的时候关闭连接
defer conn.Close()
// 3、调用远程函数
var reply AddGoodsRes //接受返回值
err2 := conn.Call("goods.AddGoods", AddGoodsReq{
Id: 10,
Title: "商品标题",
Price: 23.5,
Content: "商品详情",
}, &reply)
if err2 != nil {
fmt.Println(err2)
}
// 4、获取微服务返回的数据
fmt.Printf("%#v\n", reply)
var goodsData GetGoodsRes
err3 := conn.Call("goods.GetGoods", GetGoodsReq{
Id: 12,
}, &goodsData)
if err3 != nil {
fmt.Println(err2)
}
fmt.Printf("%#v\n", reply)
}
net/rpc/jsonrpc库以及RPC跨语言
标准库的RPC默认采用Go语言特有的gob编码,没法实现语言调用,golang官方还提供了net/rpc/jsonrpc库实现RPC方法,JSON RPC采用JSON进行数据编解码,因而支持跨语言调用,但目前jsonrpc库是基于tcp协议实现的,暂时不支持使用http进行数据传输。
package main
import (
"fmt"
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
func main() {
// 1、通过 rpc.Dial 和 rpc为服务端建立连接
conn, err1 := net.Dial("tcp", "127.0.0.1:8080")
if err1 != nil {
fmt.Println(err1)
}
// 2、客户端退出的时候关闭连接
defer conn.Close()
// 建立基于json编码的rpc服务
client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))
// 3、调用远程函数
var reply string //接受返回值
/*
Call的三个参数
1、第一个参数 hello.SayHello hello表示服务器 SayHello方法名称
2、第二个参数 给服务器的req 传递数据
3、第三个参数 需要传入地址 获取微服务端返回的数据
*/
err2 := client.Call("hello.SayHello", "我是客户端", &reply)
if err2 != nil {
fmt.Println(err2)
}
// 4、获取微服务返回的数据
fmt.Println(reply)
}
package main
import (
"fmt"
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
// 定义一个远程调用的方法
type Hello struct{}
// 1、方法只能有两个可序列化的的参数,第二个参数是指针类型
// req 表示获取客户端传过来的数据
// res 表示给客户端返回类型(指针类型)
// 2、方法要返回一个error类型,同时必须是公开的方法
// 3、参数类型不能是channel、func类型
func (hl Hello) SayHello(req string, res *string) error {
fmt.Println(req)
*res = "你好" + req
return nil
}
func main() {
// 1、注册RPC服务
err1 := rpc.RegisterName("hello", new(Hello))
if err1 != nil {
fmt.Println(err1)
}
//2、监听端口
listener, err2 := net.Listen("tcp", "127.0.0.1:8080")
if err2 != nil {
fmt.Println(err2)
}
// 3、应用退出的时候关闭监听端口
defer listener.Close()
for {
fmt.Println("开始建立连接")
// 4、建立连接
conn, err3 := listener.Accept()
if err3 != nil {
fmt.Println(err3)
}
// 5、绑定服务
rpc.ServeCodec(jsonrpc.NewServerCodec(conn))
}
}
// jsonrpc 和默认rpc之间的区别:
// jsonrpc中通过
ProtoBuf
Protobuf是Protocol Buffers的简称,是Google公司开发的一种数据描述语言,是一种轻便高效的结构数据存储格式,可以用于结构化数据,或者序列化,适合做数据存储或PRC数据交换。可用于通讯数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式,它是一种灵活,高校,地动画的机制,用于序列化结构化数据,对比于XML和JSON,她更小,更快,更简单。
优势:
- 序列化后的体积相比于json和xml很小,适合网络传输
- 支持跨平台多语言
- 消息格式省级和兼容性很好
- 序列化反序列化速度很快,快于json的处理速度
劣势
- 应用不够广(相比xml和json)
- 二进制格式导致可读性差
- 缺乏自描述