RPC主要依賴于客戶端與服務(wù)端建立socket鏈接;而HTTP REST實現(xiàn)通訊的代價比較高,這是RPC的一個優(yōu)勢體現(xiàn)。
 什么是RPC
RPC的中文是“遠程過程調(diào)用”,對應(yīng)的英文全稱是:Remote Procedure Call,可以簡單理解為一個節(jié)點請求另一個節(jié)點提供的服務(wù)。
請先自行思考一下什么是“本地過程調(diào)用”,可以更好的理解“遠程過程調(diào)用”。
知識點:RPC主要依賴于客戶端與服務(wù)端建立socket鏈接;而HTTP REST實現(xiàn)通訊的代價比較高,這是RPC的一個優(yōu)勢體現(xiàn)。(gRPC使用http2.0)
為什么用RPC
就是因為無法在同一個進程內(nèi),或者無法在同一個服務(wù)器上通過本地調(diào)用的方式實現(xiàn)我們的需求。
HTTP能滿足需求但是不夠高效,所以我們需要使用RPC。
知乎大神的回答[1]
RPC的優(yōu)勢
- RPC能夠跨多種開發(fā)工具和平臺
 - RPC能夠跨語言調(diào)用
 - RPC能夠提高系統(tǒng)的可擴展性,解耦,提高復(fù)用
 - RPC相較于HTTP 1.1,傳輸效率更高,性能消耗更小,自帶負載均衡策略,自動實現(xiàn)服務(wù)治理
 
RPC和HTTP對比
- RPC主要用于公司內(nèi)部的服務(wù)調(diào)用,性能消耗低,傳輸效率高,服務(wù)治理方便。
 - HTTP主要用于對外的異構(gòu)環(huán)境,瀏覽器接口調(diào)用,APP接口調(diào)用,第三方接口調(diào)用等。
 - RPC和HTTP的詳細對別[2]可以看這篇文章,不作為本篇的重點
 
RPC的使用邊界
通過和HTTP的對比,我們可以倒推出RPC的邊界:對外的異構(gòu)環(huán)境,瀏覽器接口調(diào)用,APP接口調(diào)用,第三方接口調(diào)用。
上述這些都不適合RPC,不知道RPC不適合做什么,比知道RPC能做什么更重要。
RPC入門1:net/rpc
基本構(gòu)成
- RPC的基本構(gòu)成:服務(wù)端,客戶端
 - 服務(wù)端基本構(gòu)成:結(jié)構(gòu)體,請求結(jié)構(gòu)體,響應(yīng)結(jié)構(gòu)體
 - 客戶端基本構(gòu)成:請求結(jié)構(gòu)體,響應(yīng)結(jié)構(gòu)體
 
代碼示例
rpc_service.go
package main
import (
 "errors"
 "fmt"
 "log"
 "net"
 "net/http"
 "net/rpc"
 "os"
)
type Arith struct {
}
//請求結(jié)構(gòu)體
type ArithRequest struct {
 A int
 B int
}
//響應(yīng)結(jié)構(gòu)體
type ArithResponse struct {
 Pro int //乘積
 Quo int //商
 Rem int //余數(shù)
}
//乘積方法
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服務(wù)
 rpc.Register(new(Arith))
 //采用http協(xié)議作為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")
 //常規(guī)啟動http服務(wù)
 http.Serve(lis,nil)
}
rpc_client.go
package main
import (
 "fmt"
 "log"
 "net/rpc"
)
//算數(shù)運算請求結(jié)構(gòu)體
type ArithRequest struct {
 A int
 B int
}
//響應(yīng)結(jié)構(gòu)體
type ArithResponse struct {
 Pro int //乘
 Quo int //商
 Rem int //余數(shù)
}
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 余數(shù)是:%d",req.A,req.B,res.Quo,res.Rem)
}
運行結(jié)果
先啟動服務(wù)端,再啟動客戶端連接服務(wù)端
//服務(wù)端console
start connection
//客戶端console
10 * 20 = 200
10 / 20 = 0 余數(shù)是:10
RPC入門2:net/rpc/jsonrpc
實現(xiàn)跨語言調(diào)用
jsonrpc_server.go
package main
import (
 "errors"
 "fmt"
 "log"
 "net"
 "net/rpc"
 "net/rpc/jsonrpc"
 "os"
)
type Arith struct {
}
//請求結(jié)構(gòu)體
type ArithRequest struct {
 A int
 B int
}
//響應(yīng)結(jié)構(gòu)體
type ArithResponse struct {
 Pro int //乘積
 Quo int //商
 Rem int //余數(shù)
}
//乘積方法
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服務(wù)
 rpc.Register(new(Arith))
 //采用http協(xié)議作為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")
 //接收客戶端請求 并發(fā)處理 jsonrpc
 for {
  conn,err :=lis.Accept() //接收客戶端連接請求
  if err!=nil {
   continue
  }
  //并發(fā)處理客戶端請求
  go func(conn net.Conn) {
   fmt.Fprintf(os.Stdout,"%s","new client in coming\n")
   jsonrpc.ServeConn(conn)
  }(conn)
 }
 //常規(guī)啟動http服務(wù)
 //http.Serve(lis,nil)
}
jsonrpc_client.go
package main
import (
 "fmt"
 "log"
 "net/rpc/jsonrpc"
)
//算數(shù)運算請求結(jié)構(gòu)體
type ArithRequest struct {
 A int
 B int
}
//響應(yīng)結(jié)構(gòu)體
type ArithResponse struct {
 Pro int //乘
 Quo int //商
 Rem int //余數(shù)
}
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 余數(shù)是:%d",req.A,req.B,res.Quo,res.Rem)
}
運行結(jié)果
先啟動服務(wù)端,再啟動客戶端連接服務(wù)端
//服務(wù)端console
start connection
//客戶端console
9 * 2 = 18
9 / 2 = 4 余數(shù)是:1
//服務(wù)端console
new client in coming
RPC入門3:go php跨語言調(diào)用
Go作為服務(wù)端,PHP作為客戶端
jsonrpc_server.go:和入門2服務(wù)端的代碼一樣
下面是PHP代碼
jsonrpc_client.php
<?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[3] 不作為本文重點,可以看這篇文章。
運行結(jié)果
本地啟動PHP服務(wù):
??http://127.0.0.1/jsonrpc_client.php??
運行結(jié)果如下:
9 * 2 = 18 9 / 2, Quo is 4, Rem is 1
總結(jié)
一文入門RPC,就是如此絲滑,So Easy!
歡迎還在用單體架構(gòu),沒有使用RPC的同學(xué)們操練起來,尤其是PHP的小伙伴們,卷起來吧。該學(xué)學(xué)Go語言啦~
相關(guān)資料
[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??
本文轉(zhuǎn)載自微信公眾號「 程序員升級打怪之旅」,作者「王中陽Go」,可以通過以下二維碼關(guān)注。

轉(zhuǎn)載本文請聯(lián)系「 程序員升級打怪之旅」公眾號。