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)

浙公网安备 33010602011771号