<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    golang實現RPC的幾種方式

    共 7379字,需瀏覽 15分鐘

     ·

    2022-01-14 15:27

    什么是RPC

    遠程過程調用(Remote Procedure Call,縮寫為 RPC)是一個計算機通信協議。該協議允許運行于一臺計算機的程序調用另一臺計算機的子程序,而程序員無需額外地為這個交互作用編程。如果涉及的軟件采用面向對象編程,那么遠程過程調用亦可稱作遠程調用或遠程方法調用。

    用通俗易懂的語言描述就是:RPC允許跨機器、跨語言調用計算機程序方法。打個比方,我用go語言寫了個獲取用戶信息的方法getUserInfo,并把go程序部署在阿里云服務器上面,現在我有一個部署在騰訊云上面的php項目,需要調用golang的getUserInfo方法獲取用戶信息,php跨機器調用go方法的過程就是RPC調用。

    golang中如何實現RPC

    在golang中實現RPC非常簡單,有封裝好的官方庫和一些第三方庫提供支持。Go RPC可以利用tcp或http來傳遞數據,可以對要傳遞的數據使用多種類型的編解碼方式。golang官方的net/rpc庫使用encoding/gob進行編解碼,支持tcphttp數據傳輸方式,由于其他語言不支持gob編解碼方式,所以使用net/rpc庫實現的RPC方法沒辦法進行跨語言調用。

    golang官方還提供了net/rpc/jsonrpc庫實現RPC方法,JSON RPC采用JSON進行數據編解碼,因而支持跨語言調用。但目前的jsonrpc庫是基于tcp協議實現的,暫時不支持使用http進行數據傳輸。

    除了golang官方提供的rpc庫,還有許多第三方庫為在golang中實現RPC提供支持,大部分第三方rpc庫的實現都是使用protobuf進行數據編解碼,根據protobuf聲明文件自動生成rpc方法定義與服務注冊代碼,在golang中可以很方便的進行rpc服務調用。

    net/rpc庫

    下面的例子演示一下如何使用golang官方的net/rpc庫實現RPC方法,使用http作為RPC的載體,通過net/http包監(jiān)聽客戶端連接請求。

    $GOPATH/src/test/rpc/rpc_server.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.Register(new(Arith))?//?注冊rpc服務
    ????rpc.HandleHTTP()?????????//?采用http協議作為rpc載體

    ????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)
    }

    上述服務端程序運行后,將會監(jiān)聽本地的8095端口,我們可以實現一個客戶端程序,連接服務端并實現RPC方法調用。

    $GOPATH/src/test/rpc/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("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)
    }


    net/rpc/jsonrpc庫

    上面的例子我們演示了使用net/rpc實現RPC的過程,但是沒辦法在其他語言中調用上面例子實現的RPC方法。所以接下來的例子我們演示一下使用net/rpc/jsonrpc庫實現RPC方法,此方式實現的RPC方法支持跨語言調用。

    $GOPATH/src/test/rpc/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.Register(new(Arith))?//?注冊rpc服務

    ????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")

    ????for?{
    ????????conn,?err?:=?lis.Accept()?//?接收客戶端連接請求
    ????????if?err?!=?nil?{
    ????????????continue
    ????????}

    ????????go?func(conn?net.Conn)?{?//?并發(fā)處理客戶端請求
    ????????????fmt.Fprintf(os.Stdout,?"%s",?"new?client?in?coming\n")
    ????????????jsonrpc.ServeConn(conn)
    ????????}(conn)
    ????}
    }

    上述服務端程序啟動后,將會監(jiān)聽本地的8096端口,并處理客戶端的tcp連接請求。我們可以用golang實現一個客戶端程序連接上述服務端并進行RPC調用。

    $GOPATH/src/test/rpc/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("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)
    }


    protorpc庫

    為了實現跨語言調用,在golang中實現RPC方法的時候我們應該選擇一種跨語言的數據編解碼方式,比如JSON,上述的jsonrpc可以滿足此要求,但是也存在一些缺點,比如不支持http傳輸,數據編解碼性能不高等。于是呢,一些第三方rpc庫都選擇采用protobuf進行數據編解碼,并提供一些服務注冊代碼自動生成功能。下面的例子我們使用protobuf來定義RPC方法及其請求響應參數,并使用第三方的protorpc庫來生成RPC服務注冊代碼。

    首先,需要安裝protobufprotoc可執(zhí)行命令,可以參考此篇文章:protobuf快速上手指南

    然后,我們編寫一個proto文件,定義要實現的RPC方法及其相關參數。

    $GOPATH/src/test/rpc/pb/arith.proto

    syntax?=?"proto3";package?pb;??//?算術運算請求結構
    message?ArithRequest?{????int32?a?=?1;????int32?b?=?2;}//?算術運算響應結構

    message?ArithResponse?{????
    ????int32?pro?=?1;??//?乘積
    ????int32?quo?=?2;??//?商
    ????int32?rem?=?3;??//?余數
    }//?rpc方法

    service?ArithService?{????
    rpc?multiply?(ArithRequest)?returns?(ArithResponse);??//?乘法運算方法
    rpc?divide?(ArithRequest)?returns?(ArithResponse);???//?除法運算方法
    }

    接下來我們需要根據上述定義的arith.proto文件生成RPC服務代碼。要先安裝protorpc庫:go get github.com/chai2010/protorpc?然后使用protoc工具生成代碼:protoc --go_out=plugin=protorpc=. arith.proto?執(zhí)行protoc命令后,在與arith.proto文件同級的目錄下生成了一個arith.pb.go文件,里面包含了RPC方法定義和服務注冊的代碼。

    基于生成的arith.pb.go代碼我們來實現一個rpc服務端

    $GOPATH/src/test/rpc/protorpc_server.go

    package?main

    import?(
    ????"errors"
    ????"test/rpc/pb"
    )

    //?算術運算結構體
    type?Arith?struct?{
    }

    //?乘法運算方法
    func?(this?*Arith)?Multiply(req?*pb.ArithRequest,?res?*pb.ArithResponse)?error?{
    ????res.Pro?=?req.GetA()?*?req.GetB()
    ????return?nil
    }

    //?除法運算方法
    func?(this?*Arith)?Divide(req?*pb.ArithRequest,?res?*pb.ArithResponse)?error?{
    ????if?req.GetB()?==?0?{
    ????????return?errors.New("divide?by?zero")
    ????}
    ????res.Quo?=?req.GetA()?/?req.GetB()
    ????res.Rem?=?req.GetA()?%?req.GetB()
    ????return?nil
    }

    func?main()?{
    ????pb.ListenAndServeArithService("tcp",?"127.0.0.1:8097",?new(Arith))
    }

    運行上述程序,將會監(jiān)聽本地的8097端口并接收客戶端的tcp連接。

    基于ariti.pb.go再來實現一個客戶端程序。

    $GOPATH/src/test/protorpc_client.go

    package?main

    import?(
    ????"fmt"
    ????"log"
    ????"test/rpc/pb"
    )

    func?main()?{
    ????conn,?err?:=?pb.DialArithService("tcp",?"127.0.0.1:8097")
    ????if?err?!=?nil?{
    ????????log.Fatalln("dailing?error:?",?err)
    ????}
    ????defer?conn.Close()

    ????req?:=?&pb.ArithRequest{9,?2}

    ????res,?err?:=?conn.Multiply(req)
    ????if?err?!=?nil?{
    ????????log.Fatalln("arith?error:?",?err)
    ????}
    ????fmt.Printf("%d?*?%d?=?%d\n",?req.GetA(),?req.GetB(),?res.GetPro())

    ????res,?err?=?conn.Divide(req)
    ????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)
    }


    如何跨語言調用golang的RPC方法

    上面的三個例子,我們分別使用net/rpcnet/rpc/jsonrpc、protorpc實現了golang中的RPC服務端,并給出了對應的golang客戶端RPC調用示例,因為JSON和protobuf是支持多語言的,所以使用jsonrpcprotorpc實現的RPC方法我們是可以在其他語言中進行調用的。下面給出一個php客戶端程序,通過socket連接調用jsonrpc實現的服務端RPC方法。

    $PHPROOT/jsonrpc.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']);


    其他RPC庫

    除了上面提到的三種在golang實現RPC的方式外,還有一些其他的rpc庫提供了類似的功能,比較出名的有google開源的grpc,但是grpc的初次安裝比較麻煩,這里就不做進一步介紹了,有興趣的可以自己了解。

    瀏覽 51
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報
    評論
    圖片
    表情
    推薦
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    欧美成人精品导航 | 成人特级毛片全部免费播放 | 爱搞搞就爱搞搞 | 哪个网站可以看AV | 亚洲日韩第一页 |