1:RPC是什么

1:RPC是啥?

远程过程调用(Remote Procedure Call,缩写为 RPC)。它可以让你如调用本地函数一样,去调用处在远处另一台计算机上面的函数。

有关RPC的想法至少可以追溯到1976年以“信使报”(Courier)的名义使用。RPC首次在UNIX平台上普及的执行工具程序是SUN公司的RPC(现在叫ONC RPC)

RPC 的消息传输可以通过 TCP、UDP 或者 HTTP等,所以有时候我们称之为 RPC over TCP、 RPC over HTTP。

RPC 通过 HTTP 传输消息的时候和 REST ful的架构是类似的,但是也有不同。

2:RESTful和RPC over TCP 区别

我们再来比较一下 RPC over TCP 和 RESTful。 如果我们直接使用socket实现 RPC,可以获得性能上的优势

RPC over TCP可以通过长连接减少连接的建立所产生的花费,在调用次数非常巨大的时候(这是目前互联网公司经常遇到的情况,大并发的情况下),

这个花费影响是非常巨大的。 当然 RESTful 也可以通过 keep-alive 实现长连接,但是它最大的一个问题是它的request-response模型是阻塞的 (http1.0和 http1.1, http 2.0没这个问题), 发送一个请求后只有等到response返回才能发送第二个请求 (有些http server实现了pipeling的功能,但不是标配), RPC的实现没有这个限制。

在当今用户和资源都是大数据大并发的趋势下,一个大规模的公司不可能使用一个单体程序提供所有的功能,微服务的架构模式越来越多的被应用到产品的设计和开发中, 服务和服务之间的通讯也越发的重要, 所以 RPC 不失是一个解决服务之间通讯的好办法,

3:RPC实现

在前面我们理解socket链接方式,rpc也是s/c架构。它其实也socket的时候方式差不多,就是多了一个注册服务的概念。

server实现

package main

import (
	"fmt"
	"net"
	"net/rpc"
)

//结构体必须实现Say格式的方法。两个参数 一个传入 一个指针传出而且返回值必须是error类型
type HelloWorld struct {
}

func (this *HelloWorld) Say(req string, rep *string) error {
	*rep = req + "hello"
	return nil
}

func main() {
	err := rpc.RegisterName("hello", new(HelloWorld))
	if err != nil {
		fmt.Println("RegisterName Error")
	}
	listener, err := net.Listen("tcp", "127.0.0.1:9988")
	if err != nil {
		fmt.Println("listener Error")
	}
	defer func() {
		_ = listener.Close()
	}()
	for {
		conn, _ := listener.Accept()
		defer func() {
			_ = conn.Close()
		}()
		go rpc.ServeConn(conn)
	}
}

client实现

package main

import (
	"fmt"
	"net/rpc"
)

func main() {
	client, err := rpc.Dial("tcp", "127.0.0.1:9988")
	if err != nil {
		fmt.Println("Dial error")
	}
	var rep string
        //这里的hello.Say 表示调用远端的hello服务的Say方法。这种你不能写错,而且两个参数中第二个必须是指针地址
	err = client.Call("hello.Say", "libai", &rep)
	if err != nil {
		fmt.Println("Call error")
	}
	fmt.Println("接受来着客户端的消息", rep)

}

我们完成了上面的小案例!但是你有没有发现:

server端你必须要理解如何注册服务的格式(Say方法的格式)要不然肯定是失败的。

client端你必须要写对你的服务名和要调用的方法!而且第二个参数也是必须要指针的!

那么小白来了,我怎么提供给他们用!写接口!接口去约束方法格式。

并且上面的server和client如果我们写错了,他的错误是发生在运行时期的!我们说报错要宜早不宜迟,写了接口让我们在编译期就可以发现错误了。

4:接口封装rpc案例

server端

design.go

package main

import "net/rpc"

//----------server端--------
type RpcInterFace interface {
	Say(request string, response *string) error
}

//注册服务
func RegisterServer(name string, face RpcInterFace) error {
	return rpc.RegisterName(name, face)
}

//----------client端--------

server.go

package main

import (
	"fmt"
	"net"
	"net/rpc"
)

type HelloWorld struct {
}

func (this *HelloWorld) Say(req string, rep *string) error {
	fmt.Println("接收来自客户端发来的消息", req)
	*rep = req + "hello"
	return nil
}

func main() {
	//使用注册方法
	err := RegisterServer("hello", &HelloWorld{})
	if err != nil {
		fmt.Println("RegisterServer error")
	}
	listener, err := net.Listen("tcp", "127.0.0.1:9988")
	if err != nil {
		fmt.Println("listener Error")
	}
	defer listener.Close()
	for {
		conn, _ := listener.Accept()
		defer conn.Close()
		go rpc.ServeConn(conn)
	}
}

client端  

design.go

package main

import (
	"fmt"
	"net/rpc"
)

//----------client端--------
type Client struct {
	c *rpc.Client
}


func InitClient(addr string) *Client {
	conn,err:=rpc.Dial("tcp",addr)
	if err!= nil{
		fmt.Println("error")
	}
	return &Client{conn}
}


func (this *Client)Say(request string,response *string) error {
	return this.c.Call("hello.Say",request,response)
}

client.go

package main

import "fmt"

func main() {
	myclient := InitClient("127.0.0.1:9988")
	var rep string
	_ = myclient.Say("礼拜", &rep)
	fmt.Println("接受来自服务端的消息", rep)
}

5:RPC使用什么序列化数据

要知道rpc我们一直使用的是go内置的rpc模块,他是用gob进行序列化数据的,这种gob使用在go语言之间使用。如果

想要支持跨语言,让别人也可以调用我们的服务,我们就需要用一种通用的序列化格式--JSON!

rpc怎么使用json呢?很简单!只要改一行

client:
    conn, err := jsonrpc.Dial("tcp", addr)
server:
    go jsonrpc.ServeConn(conn)

 

 

 

 

 

posted @ 2022-01-26 00:16  你是我的神奇  阅读(226)  评论(0编辑  收藏  举报