RPC
什么是RPC
- RPC的中文是“远程过程调用”,对应的英文全称是:Remote Procedure Call,可以简单理解为一个节点请求另一个节点提供的服务
- 理解“本地过程调用”可以更好的理解“远程过程调用”
- 知识点:RPC主要依赖于客户端与服务端建立socket链接;而HTTP REST实现通讯的代价比较高,这是RPC的一个优势体现。
- RPC详解看这里
为什么用RPC
就是因为无法在同一个进程内,或者无法在同一个服务器上通过本地调用的方式实现我们的需求。HTTP能满足需求但是不够高效,所以我们需要使用RPC。
RPC的优势
- RPC能够跨多种开发工具和平台
- RPC能够跨语言调用
- RPC能够提高系统的可扩展性,解耦,提高复用
- RPC相较于HTTP,传输效率更高,性能消耗更小,自带负载均衡策略,自动实现服务治理
RPC和HTTP对比
- RPC主要用于公司内部的服务调用,性能消耗低,传输效率高,服务治理方便。
- HTTP主要用于对外的异构环境,浏览器接口调用,APP接口调用,第三方接口调用等。
- RPC和HTTP的详细对别
RPC的使用边界
- 通过和HTTP的对比,我们倒推出RPC的边界:对外的异构环境,浏览器接口调用,APP接口调用,第三方接口调用。
- 上述这些都不适合RPC,不知道RPC不适合做什么,比知道RPC能做什么更重要。
RPC入门1:net/rpc
基本构成
- RPC的基本构成:服务端,客户端
- 服务端基本构成:结构体,请求结构体,响应结构体
- 客户端基本构成:请求结构体,响应结构体
代码示例
rpc_service.go
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服务 | |
rpc.Register(new(Arith)) | |
//采用http协议作为rpc载体 | |
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\n") | |
//常规启动http服务 | |
http.Serve(lis,nil) | |
} | |
复制代码 |
rpc_client.go
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("dialing error:",err) | |
} | |
req := ArithRequest{10,20} | |
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 = %d 余数是:%d",req.A,req.B,res.Quo,res.Rem) | |
} | |
复制代码 |
运行结果
- 先启动服务端,再启动客户端连接服务端
//服务端console | |
start connection | |
//客户端console | |
10 * 20 = 200 | |
10 / 20 = 0 余数是:10 | |
复制代码 |
RPC入门2:net/rpc/jsonrpc
实现跨语言调用
jsonrpc_server.go
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服务 | |
rpc.Register(new(Arith)) | |
//采用http协议作为rpc载体 | |
rpc.HandleHTTP() | |
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\n") | |
//接收客户端请求 并发处理 jsonrpc | |
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) | |
} | |
//常规启动http服务 | |
//http.Serve(lis,nil) | |
} | |
复制代码 |
jsonrpc_client.go
package main | |
import ( | |
"fmt" | |
"log" | |
"net/rpc/jsonrpc" | |
) | |
//算数运算请求结构体 | |
type ArithRequest struct { | |
A int | |
B int | |
} | |
//响应结构体 | |
type ArithResponse struct { | |
Pro int //乘 | |
Quo int //商 | |
Rem int //余数 | |
} | |
func main() { | |
// 只有这里不一样 | |
conn,err := jsonrpc.Dial("tcp","127.0.0.1:8096") | |
if err!=nil { | |
log.Fatalln("dialing 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 = %d 余数是:%d",req.A,req.B,res.Quo,res.Rem) | |
} | |
复制代码 |
运行结果
- 先启动服务端,再启动客户端连接服务端
//服务端console | |
start connection | |
//客户端console | |
9 * 2 = 18 | |
9 / 2 = 4 余数是:1 | |
//服务端console | |
new client in coming | |
复制代码 |
RPC入门3:go php跨语言调用
Go作为服务端,PHP作为客户端
jsonrpc_server.go:和入门2服务端的代码一样
jsonrpc_client.php
class JsonRPC | |
{ | |
private $conn; | |
function __construct($host, $port) | |
{ | |
$this->conn = fsockopen($host, $port, $errno, $errstr, 3); | |
if (!$this->conn) { | |
return false; | |
} | |
} | |
public function Call($method, $params) | |
{ | |
if (!$this->conn) { | |
return false; | |
} | |
$err = fwrite($this->conn, json_encode(array( | |
'method' => $method, | |
'params' => array($params), | |
'id' => 0, | |
)) . "\n"); | |
if ($err === false) { | |
return false; | |
} | |
stream_set_timeout($this->conn, 0, 3000); | |
$line = fgets($this->conn); | |
if ($line === false) { | |
return NULL; | |
} | |
return json_decode($line, true); | |
} | |
} | |
$client = new JsonRPC("127.0.0.1", 8096); | |
$args = array('A' => 9, 'B' => 2); | |
$r = $client->Call("Arith.Multiply", $args); | |
printf("%d * %d = %d\n", $args['A'], $args['B'], $r['result']['Pro']); | |
$r = $client->Call("Arith.Divide", array('A' => 9, 'B' => 2)); | |
printf("%d / %d, Quo is %d, Rem is %d\n", $args['A'], $args['B'], $r['result']['Quo'], $r['result']['Rem']); | |
复制代码 |
如何在本地启动PHP
运行结果
//本地启动PHP服务:http://127.0.0.1/jsonrpc_client.php,运行结果如下: | |
9 * 2 = 18 9 / 2, Quo is 4, Rem is 1 | |
复制代码 |
参考博客
- RPC相关博客,可以点击原文链接查看
- Mac本地启动PHP,可以点击原文链接查看
名词解释
- Thrift:是一种接口描述语言和二进制通讯协议,被当做RPC的框架来使用。