RPC简介
Remote Procedure Call Protocol —— 远程过程调用协议
RPC(Remote Procedure Call Protocol),是远程过程调用的缩写,通俗的说就是调用远处的一个函数。
理解RPC:
- ==像调用本地函数一样,去调用远程函数。==
- 通过rpc协议,传递:函数名、函数参数。达到在本地,调用远端函数,得返回值到本地的目标。
- ==像调用本地函数一样,去调用远程函数。==
为什么微服务使用 RPC:
- 每个服务都被封装成 进程。彼此”独立“。
- 进程和进程之间,可以使用不同的语言实现。
我们使用微服务化的一个好处就是,不限定服务的提供方使用什么技术选型,能够实现公司跨团队的技术解耦
如果没有统一的服务框架,RPC框架,各个团队的服务提供方就需要各自实现一套序列化、反序列化、网络框架、连接池、收发线程、超时处理、状态机等“业务之外”的重复技术劳动,造成整体的低效。所以,统一RPC框架把上述“业务之外”的技术劳动统一处理,是服务化首要解决的问题。
RPC 使用的步骤
—- 服务端:
注册 rpc 服务对象。给对象绑定方法( 1. 定义类, 2. 绑定类方法 )
rpc.RegisterName("服务名",回调对象)
创建监听器
listener, err := net.Listen()
建立连接
conn, err := listener.Accept()
将连接 绑定 rpc 服务。
rpc.ServeConn(conn)
// 定义类对象
type World struct {
}
//定义类方法
func (w *World) Hello(name string,resp *string)error {
*resp=name+" hello!"
return nil
}
func (w *World) Bye(name string, resp *string) error {
*resp=name+" Bye!"
return nil
}
func main(){
//1. 注册RPC服务, 绑定对象方法
err := rpc.RegisterName("Hello", new(World))
//
if err != nil {
fmt.Println("rpc.RegisterName err:",err)
return
}
//2.设置监听
listener, err := net.Listen("tcp", "127.0.0.1:8848")
if err != nil {
fmt.Println("net.Listen err:",err)
return
}
defer listener.Close()
fmt.Println("开始监听")
//3./ Accept 建立连接
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener.Accept err:",err)
return
}
defer conn.Close()
fmt.Println("连接建立成功")
//绑定服务
rpc.ServeConn(conn)
}
—- 客户端:
用 rpc 连接服务器。
conn, err := rpc.Dial()
调用远程函数。
conn.Call("服务名.方法名", 传入参数, 传出参数)
package main
import (
"fmt"
"net/rpc"
)
func main() {
// 1. 用 rpc 链接服务器 --Dial() 注意要使用rpc下的Dial
conn, err := rpc.Dial("tcp", "127.0.0.1:8848")
if err != nil {
fmt.Println("rpc.Dial err:",err)
return
}
defer conn.Close()
// 2. 调用远程函数
var resp string
//err = conn.Call("Hello.Hello", "zhu", &resp)
err = conn.Call("Hello.Bye", "zhu", &resp)
if err!=nil {
fmt.Println("conn.Call err:",err)
return
}
fmt.Println(resp)
}
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 变量,&变量名 完成传参。
json 版 rpc
修改客户端
修改客户端,使用jsonrpc:
conn, err := jsonrpc.Dial("tcp", "127.0.0.1:8800")
package main
import (
"errors"
"fmt"
"log"
"net"
"net/rpc"
"net/rpc/jsonrpc"
"os"
)
// 算数运算结构体
type Arith struct {
}
// 算数运算请求结构体
type ArithRequest struct {
A int
B int
}
// 算数运算响应结构体
type ArithResponse struct {
Pro int // 乘积
Quo int // 商
Rem int // 余数
}
// 乘法运算方法
func (this *Arith) Multiply(req ArithRequest, res *ArithResponse) error {
res.Pro = req.A * req.B
return nil
}
// 除法运算方法
func (this *Arith) Divide(req ArithRequest, res *ArithResponse) error {
if req.B == 0 {
return errors.New("divide by zero")
}
res.Quo = req.A / req.B
res.Rem = req.A % req.B
return nil
}
func main() {
rpc.Register(new(Arith)) // 注册rpc服务
lis, err := net.Listen("tcp", "127.0.0.1:8096")
if err != nil {
log.Fatalln("fatal error: ", err)
}
fmt.Fprintf(os.Stdout, "%s", "start connection")
for {
conn, err := lis.Accept() // 接收客户端连接请求
if err != nil {
continue
}
go func(conn net.Conn) { // 并发处理客户端请求
fmt.Fprintf(os.Stdout, "%s", "new client in coming\n")
jsonrpc.ServeConn(conn)
}(conn)
}
}
修改服务器端
修改服务器端,使用 jsonrpc:
jsonrpc.ServeConn(conn)
package main
import (
"fmt"
"log"
"net/rpc/jsonrpc"
)
// 算数运算请求结构体
type ArithRequest1 struct {
A int
B int
}
// 算数运算响应结构体
type ArithResponse1 struct {
Pro int // 乘积
Quo int // 商
Rem int // 余数
}
func main() {
conn, err := jsonrpc.Dial("tcp", "127.0.0.1:8096")
if err != nil {
log.Fatalln("dailing error: ", err)
}
req := ArithRequest1{9, 2}
var res ArithResponse1
err = conn.Call("Arith.Multiply", req, &res) // 乘法运算
if err != nil {
log.Fatalln("arith error: ", err)
}
fmt.Printf("%d * %d = %d\n", req.A, req.B, res.Pro)
err = conn.Call("Arith.Divide", req, &res)
if err != nil {
log.Fatalln("arith error: ", err)
}
fmt.Printf("%d / %d, quo is %d, rem is %d\n", req.A, req.B, res.Quo, res.Rem)
}
rpc 封装
服务端封装
// 定义接口 type xxx interface { 方法名(传入参数,传出参数) error } 例: type MyInterface interface { HelloWorld(string, *string) error }
// 封装注册服务方法 func RegisterService (i MyInterface) { rpc.RegisterName("hello", i) }
type MicroService interface {
Hello(string,*string) error
Bye(string,*string)error
}
type HelloService struct {
}
func (h *HelloService) Hello(name string, resp *string) error {
*resp=name+" hello!"
return nil}
func (h *HelloService) Bye(name string, resp *string) error {
*resp=name+" bye!"
return nil}
func RegisterService(name string,service MicroService){
rpc.RegisterName(name,service)
}
客户端封装
// 定义类 type MyClient struct { c *rpc.Client }
// 绑定类方法 func (this *MyClient)HelloWorld(a string, b *string) error { return this.c.Call("hello.HelloWorld", a, b) }
// 初始客户端 func InitClient(addr string) error { conn, _ := jsonrpc.Dial("tcp", adddr) return MyClient{c:conn} }
type MyClient struct {
c *rpc.Client
}
func (client *MyClient) Hello(a string,b *string)error {
return client.c.Call("Hello.Hello", a, b)
}
// 初始客户端
func InitClient(addr string) (MyClient,error) {
conn, err := rpc.Dial("tcp", addr)
return MyClient{c:conn},err
}
func main() {
client, err := InitClient("127.0.0.1:8848")
if err!=nil {
fmt.Println(err)
return
}
var resp string
//不能定义var resp *string,再传入resp,因为resp为指针类型,初始值为nil,所以不能找到这个指针
err = client.Hello("zhu", &resp)
if err!=nil {
fmt.Println("client.Hello err:",err)
}
fmt.Println(resp)
}