原理就是通过TCP等协议进行远程方法调用(Remote Procedure Call Protocol)
RPC有一个好处就是可以跨语言调用
java写的client/server也可以调用go中编写并暴露在网络中的RPC服务
不过下面的例子使用的协议是go专有的,如果想实现跨语言调用,需要统一协议(比如JSON RPC
下面是简单的代码实现
~ps:莫名想起了发布订阅模式~
Server端
// 原生实现的rpc Server
package main
import (
"fmt"
"net"
"net/rpc"
)
// 定义一个远程调用的方法
type Hello struct {
Name string
}
// 方法必须有两个参数,第二个需要是指针类型(毕竟需要操作修改)
// req 和res 不能和是Chanel或者func类型
func (Hello) SayHello(req string, res *string) error {
fmt.Println("接收到数据:", req)
*res = "好的好的,我是服务端,给你数据~"
return nil
}
func main() {
//注册一个rpc服务
err1 := rpc.RegisterName("hello", Hello{Name: "hello方法"})
if err1 != nil {
fmt.Println("hello注册失败")
return
}
Listrner, errNet := net.Listen("tcp", "localhost:8080")
if errNet != nil {
fmt.Println(errNet)
return
}
//应用推出的时候关闭监听端口
defer Listrner.Close()
fmt.Println("开始建立链接:localhost:8080")
for {
//建立一次链接
conn, errCon := Listrner.Accept()
if errCon != nil {
fmt.Println(errCon)
return
}
//绑定服务
rpc.ServeConn(conn)
}
}
Client端
//原生实现的rpc Client
package main
import (
"fmt"
"net/rpc"
)
func main() {
//通rpc连接服务器
conn, err1 := rpc.Dial("tcp", "localhost:8080")
if err1 != nil {
fmt.Println(err1)
return
}
//关闭时退出连接
defer func() {
if err := recover(); err != nil {
fmt.Println("出错咯")
}
conn.Close()
}()
//调用远程函数
var reply string
//调用订阅的结构体里的方法((
err2 := conn.Call("hello.SayHello", "我是客户端,我来找你要数据了", &reply)
if err2 != nil {
fmt.Println(err2)
return
}
//获取微服务返回的数据
fmt.Println(reply)
}
上面演示了如何使用RPC进行远程调用方法并获取方法的操作结果
RPC方法中的
req
与res
参数是类型可以是string struct
等类型,但不能是func
或者channel
还有complex
类型
演示使用struct类型作为函数参数
服务端方法
....
type Goods struct {
}
type AddGoodsReq struct {
Id int
Title string
Price float32
Content string
}
type AddGoodsRes struct {
Success bool
Message string
}
func (Goods) Add(req AddGoodsReq, res *AddGoodsRes) error {
//执行
fmt.Println(req)
*res = AddGoodsRes{
Success: true,
Message: "增加数据成功",
}
return nil
}
....
客户端
....
//客户端也需要提前声明请求体和返回提的类型
....
//调用远程函数
var reply AddGoodsRes
//调用订阅的结构体里的方法((
req := AddGoodsReq{
Id: 1,
Title: "test",
Price: 3.14,
Content: "this is a test DEMO for RPC",
}
err2 := conn.Call("goods.Add", req, &reply)
....
PS: 如果想使用JSON RPC ,只需要进行以下更改:
client端:
//建立基于JSON RPC编解码的RPC服务
// conn, err1 := rpc.Dial("tcp", "localhost:8081")
//使用了 jsonrpc.Dial 来建立 JSON-RPC 的连接,无需手动配置codec
conn, err1 := jsonrpc.Dial("tcp", "localhost:8081")
server端:
// rpc.ServeConn(conn)
//创建JSONRPC(过时写法)
// rpc.ServeCodec(jsonrpc.NewServerCodec(conn))
//jsonrpc里直接有ServeConn的实现
jsonrpc.ServeConn(conn)
据说php程序员最方便的转型是Go,我好奇Go是啥样的