29

Golang之rpc框架rpcx

 3 years ago
source link: https://studygolang.com/articles/29967
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言

远程过程调用(Remote Procedure Call,缩写为 RPC)是一个计算机通信协议。通过该协议程序员可以实现像调取本地函数一样,调取远程服务的函数。这里介绍一个高效的rpc库(rpcx)。

rpcx 是一个分布式的Go语言的 RPC 框架,支持Zookepper、etcd、consul多种服务发现方式,多种服务路由方式, 是目前性能最好的 RPC 框架之一。

官方资料: https://books.studygolang.com...

项目: https://github.com/smallnest/...

功能

  • 支持原始Go功能。无需定义原型文件。
  • 可插拔 可以扩展功能,例如服务发现,跟踪。
  • 支持TCP,HTTP, QUICKCP
  • 支持多种编解码器,例如JSON,Protobuf, MessagePack 和原始字节。
  • 服务发现。支持peer2peer,已配置的peer, zookeeperetcdconsulmDNS
  • 容错:故障转移,故障转移,故障转移。
  • 负载平衡:支持随机,轮循,一致哈希,加权,网络质量和地理位置。
  • 支持压缩。
  • 支持传递元数据。
  • 支持授权。
  • 支持心跳和单向请求。
  • 其他功能:指标,日志,超时,别名,断路器。
  • 支持双向通讯。
  • 支持通过HTTP访问,因此您可以使用任何编程语言编写客户端。
  • 支持API网关。
  • 支持备份请求,分叉和广播。

rpcx使用二进制协议且与平台无关,这意味着您可以使用其他语言(例如Java,Python,nodejs)开发服务,还可以使用其他编程语言来调用Go中开发的服务。

性能

测试结果表明,除了标准rpc库之外,rpcx的性能要优于其他rpc框架。

快速开始

基本安装

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.go

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)

}

运行

服务端

go  run server.go

客户端

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

  • 目录
  • 上一节:
  • 下一节:

有疑问加站长微信联系

iiUfA3j.png!web

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK