首页>国内 > 正文

一文搞懂RPC,So Easy!

2023-04-03 15:05:44来源:程序员升职加薪之旅

什么是RPC

RPC的中文是“远程过程调用”,对应的英文全称是:Remote Procedure Call,可以简单理解为一个节点请求另一个节点提供的服务。

请先自行思考一下什么是“本地过程调用”,可以更好的理解“远程过程调用”。

知识点:RPC主要依赖于客户端与服务端建立socket链接;而HTTP REST实现通讯的代价比较高,这是RPC的一个优势体现。(gRPC使用http2.0)


(资料图片)

为什么用RPC

就是因为无法在同一个进程内,或者无法在同一个服务器上通过本地调用的方式实现我们的需求。

HTTP能满足需求但是不够高效,所以我们需要使用RPC。

知乎大神的回答[1]

RPC的优势RPC能够跨多种开发工具和平台RPC能够跨语言调用RPC能够提高系统的可扩展性,解耦,提高复用RPC相较于HTTP1.1,传输效率更高,性能消耗更小,自带负载均衡策略,自动实现服务治理RPC和HTTP对比RPC主要用于公司内部的服务调用,性能消耗低,传输效率高,服务治理方便。HTTP主要用于对外的异构环境,浏览器接口调用,APP接口调用,第三方接口调用等。RPC和HTTP的详细对别[2]可以看这篇文章,不作为本篇的重点RPC的使用边界

通过和HTTP的对比,我们可以倒推出RPC的边界:对外的异构环境,浏览器接口调用,APP接口调用,第三方接口调用。

上述这些都不适合RPC,不知道RPC不适合做什么,比知道RPC能做什么更重要。

RPC入门1:net/rpc基本构成RPC的基本构成:服务端,客户端服务端基本构成:结构体,请求结构体,响应结构体客户端基本构成:请求结构体,响应结构体代码示例rpc_service.go

package mainimport ( "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 mainimport ( "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)}

运行结果先启动服务端,再启动客户端连接服务端

//服务端consolestart connection//客户端console10 * 20 = 20010 / 20 = 0 余数是:10

RPC入门2:net/rpc/jsonrpc实现跨语言调用jsonrpc_server.go

package mainimport ( "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 mainimport ( "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)}

运行结果先启动服务端,再启动客户端连接服务端

//服务端consolestart connection//客户端console9 * 2 = 189 / 2 = 4 余数是:1//服务端consolenew client in coming

RPC入门3:go php跨语言调用Go作为服务端,PHP作为客户端jsonrpc_server.go:和入门2服务端的代码一样

下面是PHP代码

jsonrpc_client.php

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[3]不作为本文重点,可以看这篇文章。

运行结果本地启动PHP服务:

​​http://127.0.0.1/jsonrpc_client.php​​

运行结果如下:

9 * 2 = 18 9 / 2, Quo is 4, Rem is 1

总结

一文入门RPC,就是如此丝滑,So Easy!

欢迎还在用单体架构,没有使用RPC的同学们操练起来,尤其是PHP的小伙伴们,卷起来吧。该学学Go语言啦~

相关资料

[1]知乎大神的回答:https://www.zhihu.com/question/25536695

[2]RPC和HTTP的详细对别:http://www.ccutu.com/244407.html

[3]如何在本地启动PHP:​​https://blog.csdn.net/resilient/article/details/80770531​​

本文转载自微信公众号「 程序员升级打怪之旅」,作者「王中阳Go」,可以通过以下二维码关注。

转载本文请联系「 程序员升级打怪之旅」公众号。

关键词:

相关新闻

Copyright 2015-2020   三好网  版权所有 联系邮箱:435 22 640@qq.com  备案号: 京ICP备2022022245号-21