目录
什么是RPC
RPC(Remote Procedure Call Protocol),是远程过程调用的缩写,通俗的说就是调用远处的一个函数。
RPC:远程进通信 —— 应用层协议(http协议同层)。底层使用 TCP 实现。
IPC: 进程间通信
像调用本地函数一样,去调用远程函数。
通过rpc协议,传递:函数名、函数参数。达到在本地,调用远端函数,得返回值到本地的目标。
为什么微服务使用 RPC:
1. 每个服务都被封装成 进程。彼此”独立“。
2. 进程和进程之间,可以使用不同的语言实现。
RPC 使用的步骤
服务端:
-
注册 rpc 服务对象。给对象绑定方法( 1. 定义类, 2. 绑定类方法 )
rpc.RegisterName("服务名",回调对象)
-
创建监听器
listener, err := net.Listen()
-
建立连接
conn, err := listener.Accept()
-
将连接 绑定 rpc 服务。
rpc.ServeConn(conn)
客户端:
-
用 rpc 连接服务器。
conn, err := rpc.Dial()
-
调用远程函数。
conn.Call("服务名.方法名", 传入参数, 传出参数
RPC 相关函数
-
注册 rpc 服务
func (server *Server) RegisterName(name string, rcvr interface{}) error 参1:服务名。字符串类型。 参2:对应 rpc 对象。 该对象绑定方法要满足如下条件: 1)方法必须是导出的 —— 包外可见。 首字母大写。 2)方法必须有两个参数, 都是导出类型、內建类型。 3)方法的第二个参数必须是 “指针” (传出参数) 4)方法只有一个 error 接口类型的 返回值。 举例: type World stuct { } func (this *World) HelloWorld (name string, resp *string) error { } rpc.RegisterName("服务名", new(World))
-
绑定 rpc 服务
func (server *Server) ServeConn(conn io.ReadWriteCloser) conn: 成功建立好连接的 socket —— conn
-
调用远程函数:
func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error serviceMethod: “服务名.方法名” args:传入参数。 方法需要的数据。 reply:传出参数。定义 var 变量,&变量名 完成传参。
编码实现
server.go
package main
import (
"net/rpc"
"fmt"
"net"
)
// 定义类对象
type World struct {
}
// 绑定类方法
func (this *World) HelloWorld (name string, resp *string) error {
*resp = name + " 你好!"
return nil
}
func main() {
// 1. 注册RPC服务, 绑定对象方法
err := rpc.RegisterName("hello", new(World))
if err != nil {
fmt.Println("注册 rpc 服务失败!", err)
return
}
// 2. 设置监听
listener, err := net.Listen("tcp", "127.0.0.1:8800")
if err != nil {
fmt.Println("net.Listen err:", err)
return
}
defer listener.Close()
fmt.Println("开始监听 ...")
// 3. 建立连接
conn, err := listener.Accept()
if err != nil {
fmt.Println("Accept() err:", err)
return
}
defer conn.Close()
fmt.Println("链接成功...")
// 4. 绑定服务
rpc.ServeConn(conn)
}
client.go
package main
import (
"net/rpc"
"fmt"
)
func main() {
// 1. 用 rpc 链接服务器 --Dial()
// conn, err := rpc.Dial("tcp", "127.0.0.1:8800")
conn, err := jsonrpc.Dial("tcp", "127.0.0.1:8800")
if err != nil {
fmt.Println("Dial err:", err)
return
}
defer conn.Close()
// 2. 调用远程函数
var reply string // 接受返回值 --- 传出参数
err = conn.Call("hello.HelloWorld", "张三", &reply)
if err != nil {
fmt.Println("Call:", err)
return
}
fmt.Println(reply)
}
RPC封装
目录结构
--RPC
------Client
----------main
--------------client.go
--------------design.go
------Server
----------main
--------------design.go
--------------server.go
Client/main/design.go
package main
import (
"net/rpc"
"net/rpc/jsonrpc"
)
// 客户端的封装
// 像调用本地函数一样,调用远程函数
// 定义一个类
type Myclient struct {
c *rpc.Client
}
// 由于使用了c去调用Call ,因此需要初始化c
func InitClient(addr string) Myclient {
conn, _ := jsonrpc.Dial("tcp", addr)
return Myclient{c: conn}
}
// 实现函数, 原型参照Interface来实现
func (this *Myclient) HelloWorld(a string, b *string) error {
// 参数1 参照Interface RegisterName 而来
// a 传入参数 b 传出参数
return this.c.Call("hello.HelloWorld", a, b)
}
client.go
package main
import "fmt"
// 结合 design.go 去测试
func main(){
myClient := InitClient("127.0.0.1:8800")
var resp string
err := myClient.HelloWorld("李四", &resp)
if err != nil{
fmt.Println("HelloWorld err:", err)
return
}
fmt.Println(resp, err)
}
Server/main/design.go
package main
import "net/rpc"
// 要求 服务端在注册RPC对象时,能让编译器检测出 注册对象是否合法
// 避免运行时,发现注册对象不合法
// 创建接口,在接口中定义方法原型
type MyInterface interface {
HelloWorld(string, *string) error // 限定参数是两个
}
// 封装一个函数实现
// 调用该方法时需要给i传参, 参数应该是实现了HelloWorld方法的类对象!
func RegisterService(i MyInterface) {
rpc.RegisterName("hello", i)
}
server.go
package main
import (
"fmt"
"net"
"net/rpc/jsonrpc"
)
// 定义类对象
type World struct {
}
// 绑定类方法
func (this *World) HelloWorld(name string, resp *string) error {
*resp = name + " 你好!"
return nil // 返回值不等于nil出错,等于nil不出错
// return errors.New("未知错误!") // 假如有错误,存在返回的值也不会显示
}
func main() {
// 封装后的注册
RegisterService(new(World))
// 2.设置监听
listener, err := net.Listen("tcp", "127.0.0.1:8800")
if err != nil {
fmt.Println("new.Listen err", err)
return
}
defer listener.Close()
fmt.Println("开始监听 ...")
// 3.建立连接
conn, err := listener.Accept()
if err != nil {
fmt.Println("Accept() err:", err)
return
}
defer conn.Close()
fmt.Println("连接建立成功 ...")
// 4.绑定服务
// rpc.ServeConn(conn)
jsonrpc.ServeConn(conn)
}
github
https://github.com/YTIANYE/PractiseGo/tree/master/Microservices/RPC