RPC
RPC(Remote Procedure Call,远程过程调用 )是一种计算机通信协议,允许调用不同进程空间的程序。RPC 的客户端和服务器可以在一台机器上,也可以在不同的机器上。程序员使用时,就像调用本地程序一样,无需关注内部的实现细节。
为什么需要rpc
两台机器上,两个应用程序之间需要通信。
首先需要确定采用的传输协议是什么?
? 两个应用程序位于不同的机器,那么一般会选择 TCP 协议或者 HTTP 协议;那如果两个应用程序位于相同的机器,也可以选择 Unix Socket 协议。
报文的编码格式?
? 比如采用最常用的 JSON 或者 XML,那如果报文比较大,还可能会选择 protobuf 等其他的编码方式。 1 首先要明确一点:RPC可以用HTTP协议实现,并且用HTTP是建立在 TCP 之上最广泛使用的 RPC。 2、现在业界提倡“微服务“的概念,而服务之间通信目前有两种方式,RPC就是其中一种。RPC可以保证不同服务之间的互相调用。即使是跨语言跨平台也不是问题,让构建分布式系统更加容易。 3、RPC框架都会有服务降级、流量控制的功能,保证服务的高可用。
rpc 实现
go中 RPC 可以有多种实现方式:但其大体思路就是相同的:
1 在 Server 端注册一个服务 eg:rpc.Register(new(Arith))
2 服务端监听相应的端口 lis, err := net.Listen(“tcp”, “127.0.0.1:8095”)
3 客户端请求对应地址加端口 conn, err := rpc.DialHTTP(“tcp”, “127.0.0.1:8095”)
4 客户端调用在服务端注册服务的方法,并返回返回值。
// 调用注册服务的方法, 服务名.方法名, 请求参数,返回参数 err = conn.Call(“Arith.Multiply”, req, &res)
在gRPC里,客户端可以像调用本地方法一样直接调用其他机器上的服务端应用程序的方法,帮助你更容易创建分布式应用程序和服务。与许多RPC系统一样,gRPC是基于定义一个服务,指定一个可以远程调用的带有参数和返回类型的的方法。在服务端程序中实现这个接口并且运行gRPC服务处理客户端调用。在客户端,有一个stub提供和服务端相同的方法。
Go RPC可以利用tcp或http来传递数据,可以对要传递的数据使用多种类型的编解码方式。
golang官方的net/rpc 库实现RPC方法,使用http 作为RPC的载体,通过net/http 包监听客户端连接请求。
示例:
服务器端
package main
import (
"errors"
"fmt"
"log"
"net"
"net/http"
"net/rpc"
"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.HandleHTTP()
lis, err := net.Listen("tcp", "127.0.0.1:8095")
if err != nil {
log.Fatalln("fatal error: ", err)
}
fmt.Fprintf(os.Stdout, "%s", "start connection")
http.Serve(lis, nil)
}
客户端
package main
import (
"fmt"
"log"
"net/rpc"
)
type ArithRequest struct {
A int
B int
}
type ArithResponse struct {
Pro int
Quo int
Rem int
}
func main() {
conn, err := rpc.DialHTTP("tcp", "127.0.0.1:8095")
if err != nil {
log.Fatalln("dailing error: ", err)
}
req := ArithRequest{9, 2}
var res ArithResponse
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)
}
|