微服务02——RPC协议

目录​​​​​​​

什么是RPC   

RPC 使用的步骤

服务端:

客户端:

RPC 相关函数

编码实现

server.go

 client.go

RPC封装

目录结构

Client/main/design.go

client.go

Server/main/design.go

server.go

github


​​​​​​​

什么是RPC   

RPC(Remote Procedure Call Protocol),是远程过程调用的缩写,通俗的说就是调用远处的一个函数。

RPC:远程进通信 —— 应用层协议(http协议同层)。底层使用 TCP 实现。

IPC: 进程间通信 

像调用本地函数一样,去调用远程函数。

通过rpc协议,传递:函数名、函数参数。达到在本地,调用远端函数,得返回值到本地的目标。

为什么微服务使用 RPC:

1.  每个服务都被封装成 进程。彼此”独立“。

2.  进程和进程之间,可以使用不同的语言实现。

RPC 使用的步骤

服务端:

  1. 注册 rpc 服务对象。给对象绑定方法( 1. 定义类, 2. 绑定类方法 )

    rpc.RegisterName("服务名",回调对象)
    
  2. 创建监听器

    listener, err := net.Listen()
    
  3. 建立连接

    conn, err := listener.Accept()
    
  4. 将连接 绑定 rpc 服务。

    rpc.ServeConn(conn)

客户端:

  1. 用 rpc 连接服务器。

    conn, err := rpc.Dial()
    
  2. 调用远程函数。

    conn.Call("服务名.方法名", 传入参数, 传出参数

RPC 相关函数

  1. 注册 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))
  2. 绑定 rpc 服务

    func (server *Server) ServeConn(conn io.ReadWriteCloser)
        conn: 成功建立好连接的 socket —— conn
  3. 调用远程函数:

    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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值