Golang之rpc框架rpcx

前言

遠程過程調用(Remote Procedure Call,縮寫爲 RPC)是一個計算機通訊協議。經過該協議程序員能夠實現像調取本地函數同樣,調取遠程服務的函數。這裏介紹一個高效的rpc庫(rpcx)。php

rpcx 是一個分佈式的Go語言的 RPC 框架,支持Zookepper、etcd、consul多種服務發現方式,多種服務路由方式, 是目前性能最好的 RPC 框架之一。html

官方資料https://books.studygolang.com...java

項目https://github.com/smallnest/...node

功能

  • 支持原始Go功能。無需定義原型文件。
  • 可插拔 能夠擴展功能,例如服務發現,跟蹤。
  • 支持TCP,HTTP,QUICKCP
  • 支持多種編解碼器,例如JSON,Protobuf,MessagePack和原始字節。
  • 服務發現。支持peer2peer,已配置的peer,zookeeperetcdconsulmDNS
  • 容錯:故障轉移,故障轉移,故障轉移。
  • 負載平衡:支持隨機,輪循,一致哈希,加權,網絡質量和地理位置。
  • 支持壓縮。
  • 支持傳遞元數據。
  • 支持受權。
  • 支持心跳和單向請求。
  • 其餘功能:指標,日誌,超時,別名,斷路器。
  • 支持雙向通信。
  • 支持經過HTTP訪問,所以您能夠使用任何編程語言編寫客戶端。
  • 支持API網關。
  • 支持備份請求,分叉和廣播。

rpcx使用二進制協議且與平臺無關,這意味着您能夠使用其餘語言(例如Java,Python,nodejs)開發服務,還能夠使用其餘編程語言來調用Go中開發的服務。python

性能

測試結果代表,除了標準rpc庫以外,rpcx的性能要優於其餘rpc框架。git

快速開始

基本安裝

go get -v github.com/smallnest/rpcx/...
服務端

新建server.go程序員

package main

import (
    "context"
    "flag"
    "fmt"

    example "github.com/rpcxio/rpcx-examples"
    "github.com/smallnest/rpcx/server"
)

var (
    addr = flag.String("addr", "localhost:8972", "server address")
)

type Arith struct{}

// the second parameter is not a pointer
func (t *Arith) Mul(ctx context.Context, args example.Args, reply *example.Reply) error {
    reply.C = args.A * args.B
    fmt.Println("C=", reply.C)
    return nil
}

func main() {
    flag.Parse()

    s := server.NewServer()
    //s.Register(new(Arith), "")
    s.RegisterName("Arith", new(Arith), "")
    err := s.Serve("tcp", *addr)
    if err != nil {
        panic(err)
    }
}
客戶端

client.gogithub

package main

import (
    "context"
    "flag"
    "fmt"

    "log"

    "github.com/smallnest/rpcx/protocol"

    example "github.com/rpcxio/rpcx-examples"
    "github.com/smallnest/rpcx/client"
)

var (
    addr = flag.String("addr", "localhost:8972", "server address")
)

func main() {
    flag.Parse()
    d := client.NewPeer2PeerDiscovery("tcp@"+*addr, "")
    opt := client.DefaultOption
    opt.SerializeType = protocol.JSON

    xclient := client.NewXClient("Arith", client.Failtry, client.RandomSelect, d, opt)
    defer xclient.Close()

    args := example.Args{
        A: 10,
        B: 20,
    }

    reply := &example.Reply{}
    err := xclient.Call(context.Background(), "Mul", args, reply)
    if err != nil {
        log.Fatalf("failed to call: %v", err)
    }

    log.Printf("%d * %d = %d", args.A, args.B, reply.C)

}
運行

服務端golang

go  run server.go

客戶端redis

go run client.go

這時服務端輸出:

C= 200
2020/07/21 15:19:02 server.go:358: INFO : client has closed this connection: 127.0.0.1:50186

客戶端輸出:

2020/07/21 15:19:02 10 * 20 = 200

跨語言

rpcx還提供了rpcx-gateway,您能夠使用任何編程語言編寫客戶端,以經過rpcx-gateway調用rpcx服務。

安裝rpcx-gateway

go  get  github.com/rpcxio/rpcx-gateway
示例

新建gateway.go文件,代碼以下:

package main

import (
    "errors"
    "flag"
    "fmt"
    "log"
    "strings"
    "time"

    gateway "github.com/rpcxio/rpcx-gateway"
    "github.com/rpcxio/rpcx-gateway/gin"
    "github.com/smallnest/rpcx/client"
)

var (
    addr       = flag.String("addr", ":9981", "http server address")
    registry   = flag.String("registry", "peer2peer://127.0.0.1:8972", "registry address")
    basePath   = flag.String("basepath", "/rpcx", "basepath for zookeeper, etcd and consul")
    failmode   = flag.Int("failmode", int(client.Failover), "failMode, Failover in default")
    selectMode = flag.Int("selectmode", int(client.RoundRobin), "selectMode, RoundRobin in default")
)

func main() {
    flag.Parse()

    d, err := createServiceDiscovery(*registry)
    if err != nil {
        log.Fatal(err)
    }

    httpServer := gin.New(*addr)
    gw := gateway.NewGateway("/", httpServer, d, client.FailMode(*failmode), client.SelectMode(*selectMode), client.DefaultOption)

    gw.Serve()
}

func createServiceDiscovery(regAddr string) (client.ServiceDiscovery, error) {
    i := strings.Index(regAddr, "://")
    if i < 0 {
        return nil, errors.New("wrong format registry address. The right fotmat is [registry_type://address]")
    }

    regType := regAddr[:i]
    regAddr = regAddr[i+3:]

    switch regType {
    case "peer2peer": //peer2peer://127.0.0.1:8972
        return client.NewPeer2PeerDiscovery("tcp@"+regAddr, ""), nil
    case "multiple":
        var pairs []*client.KVPair
        pp := strings.Split(regAddr, ",")
        for _, v := range pp {
            pairs = append(pairs, &client.KVPair{Key: v})
        }
        return client.NewMultipleServersDiscovery(pairs), nil
    case "zookeeper":
        return client.NewZookeeperDiscoveryTemplate(*basePath, []string{regAddr}, nil), nil
    case "etcd":
        return client.NewEtcdDiscoveryTemplate(*basePath, []string{regAddr}, nil), nil
    case "etcdv3":
        return client.NewEtcdV3DiscoveryTemplate(*basePath, []string{regAddr}, nil), nil
    case "consul":
        return client.NewConsulDiscoveryTemplate(*basePath, []string{regAddr}, nil), nil
    case "redis":
        return client.NewRedisDiscoveryTemplate(*basePath, []string{regAddr}, nil), nil
    case "mdns":
        return client.NewMDNSDiscoveryTemplate(10*time.Second, 10*time.Second, ""), nil
    default:
        return nil, fmt.Errorf("wrong registry type %s. only support peer2peer,multiple, zookeeper, etcd, consul and mdns", regType)
    }
}

運行 gateway.go

go run  gateway.go

注意:運行網關前,要保證rpcx服務端(server.go)啓動,這裏以php客戶端爲例

新建client.php

代碼以下:

<?php
$url = 'http://localhost:9981/';
$data = '{"A":10, "B":20}';

// use key 'http' even if you send the request to https://...
$options = array(
    'http' => array(
        'header'  => "Content-type: application/rpcx\r\n" .
            // "X-RPCX-MessageID: 12345678\r\n" .
            // "X-RPCX-MesssageType: 0\r\n" .
            "X-RPCX-SerializeType: 1\r\n" .
            "X-RPCX-ServicePath: Arith\r\n" .
            "X-RPCX-ServiceMethod: Mul\r\n",
        'method'  => 'POST',
        'content' => $data
    )
);
$context  = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if ($result === FALSE) { /* Handle error */ }

var_dump($result);
?>

訪問http://localhost/client.php

結果:

string(9) "{"C":200}"

server.go 輸出

C= 200

這樣就實現跨語言了。

其餘語言示例

https://github.com/rpcxio/rpc...

rpcx 3.0已針對如下目標進行了重構:

  1. 簡單:易於學習,易於開發,易於交互和易於部署
  2. 性能:高性能(> = grpc-go)
  3. 跨平臺:支持字節JSONProtobufMessagePack的原始切片。從理論上講,它能夠與java,php,python,c / c ++,node.js,c#和其餘平臺一塊兒使用
  4. 服務發現和服務治理:支持zookeeper,etcd和consul。

相關資料

https://github.com/rpcxio/rpc...

https://github.com/smallnest/...

links

  • 目錄
  • 上一節:
  • 下一節:
相關文章
相關標籤/搜索