原理就是通过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是啥样的