6.2 Golang之rpc框架rpcx

前言

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

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

官方资料https://books.studygolang.com/go-rpc-programming-guide/

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

功能

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

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

性能

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

快速开始

基本安装

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

新建server.go

  1. package main
  2. import (
  3. "context"
  4. "flag"
  5. "fmt"
  6. example "github.com/rpcxio/rpcx-examples"
  7. "github.com/smallnest/rpcx/server"
  8. )
  9. var (
  10. addr = flag.String("addr", "localhost:8972", "server address")
  11. )
  12. type Arith struct{}
  13. // the second parameter is not a pointer
  14. func (t *Arith) Mul(ctx context.Context, args example.Args, reply *example.Reply) error {
  15. reply.C = args.A * args.B
  16. fmt.Println("C=", reply.C)
  17. return nil
  18. }
  19. func main() {
  20. flag.Parse()
  21. s := server.NewServer()
  22. //s.Register(new(Arith), "")
  23. s.RegisterName("Arith", new(Arith), "")
  24. err := s.Serve("tcp", *addr)
  25. if err != nil {
  26. panic(err)
  27. }
  28. }
客户端

client.go

  1. package main
  2. import (
  3. "context"
  4. "flag"
  5. "fmt"
  6. "log"
  7. "github.com/smallnest/rpcx/protocol"
  8. example "github.com/rpcxio/rpcx-examples"
  9. "github.com/smallnest/rpcx/client"
  10. )
  11. var (
  12. addr = flag.String("addr", "localhost:8972", "server address")
  13. )
  14. func main() {
  15. flag.Parse()
  16. d := client.NewPeer2PeerDiscovery("tcp@"+*addr, "")
  17. opt := client.DefaultOption
  18. opt.SerializeType = protocol.JSON
  19. xclient := client.NewXClient("Arith", client.Failtry, client.RandomSelect, d, opt)
  20. defer xclient.Close()
  21. args := example.Args{
  22. A: 10,
  23. B: 20,
  24. }
  25. reply := &example.Reply{}
  26. err := xclient.Call(context.Background(), "Mul", args, reply)
  27. if err != nil {
  28. log.Fatalf("failed to call: %v", err)
  29. }
  30. log.Printf("%d * %d = %d", args.A, args.B, reply.C)
  31. }
运行

服务端

  1. go run server.go

客户端

  1. go run client.go

这时服务端输出:

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

客户端输出:

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

跨语言

rpcx还提供了rpcx-gateway,您可以使用任何编程语言编写客户端,以通过rpcx-gateway调用rpcx服务。

安装rpcx-gateway

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

新建gateway.go文件,代码如下:

  1. package main
  2. import (
  3. "errors"
  4. "flag"
  5. "fmt"
  6. "log"
  7. "strings"
  8. "time"
  9. gateway "github.com/rpcxio/rpcx-gateway"
  10. "github.com/rpcxio/rpcx-gateway/gin"
  11. "github.com/smallnest/rpcx/client"
  12. )
  13. var (
  14. addr = flag.String("addr", ":9981", "http server address")
  15. registry = flag.String("registry", "peer2peer://127.0.0.1:8972", "registry address")
  16. basePath = flag.String("basepath", "/rpcx", "basepath for zookeeper, etcd and consul")
  17. failmode = flag.Int("failmode", int(client.Failover), "failMode, Failover in default")
  18. selectMode = flag.Int("selectmode", int(client.RoundRobin), "selectMode, RoundRobin in default")
  19. )
  20. func main() {
  21. flag.Parse()
  22. d, err := createServiceDiscovery(*registry)
  23. if err != nil {
  24. log.Fatal(err)
  25. }
  26. httpServer := gin.New(*addr)
  27. gw := gateway.NewGateway("/", httpServer, d, client.FailMode(*failmode), client.SelectMode(*selectMode), client.DefaultOption)
  28. gw.Serve()
  29. }
  30. func createServiceDiscovery(regAddr string) (client.ServiceDiscovery, error) {
  31. i := strings.Index(regAddr, "://")
  32. if i < 0 {
  33. return nil, errors.New("wrong format registry address. The right fotmat is [registry_type://address]")
  34. }
  35. regType := regAddr[:i]
  36. regAddr = regAddr[i+3:]
  37. switch regType {
  38. case "peer2peer": //peer2peer://127.0.0.1:8972
  39. return client.NewPeer2PeerDiscovery("tcp@"+regAddr, ""), nil
  40. case "multiple":
  41. var pairs []*client.KVPair
  42. pp := strings.Split(regAddr, ",")
  43. for _, v := range pp {
  44. pairs = append(pairs, &client.KVPair{Key: v})
  45. }
  46. return client.NewMultipleServersDiscovery(pairs), nil
  47. case "zookeeper":
  48. return client.NewZookeeperDiscoveryTemplate(*basePath, []string{regAddr}, nil), nil
  49. case "etcd":
  50. return client.NewEtcdDiscoveryTemplate(*basePath, []string{regAddr}, nil), nil
  51. case "etcdv3":
  52. return client.NewEtcdV3DiscoveryTemplate(*basePath, []string{regAddr}, nil), nil
  53. case "consul":
  54. return client.NewConsulDiscoveryTemplate(*basePath, []string{regAddr}, nil), nil
  55. case "redis":
  56. return client.NewRedisDiscoveryTemplate(*basePath, []string{regAddr}, nil), nil
  57. case "mdns":
  58. return client.NewMDNSDiscoveryTemplate(10*time.Second, 10*time.Second, ""), nil
  59. default:
  60. return nil, fmt.Errorf("wrong registry type %s. only support peer2peer,multiple, zookeeper, etcd, consul and mdns", regType)
  61. }
  62. }

运行 gateway.go

  1. go run gateway.go

注意:运行网关前,要保证rpcx服务端(server.go)启动,这里以php客户端为例

新建client.php

代码如下:

  1. <?php
  2. $url = 'http://localhost:9981/';
  3. $data = '{"A":10, "B":20}';
  4. // use key 'http' even if you send the request to https://...
  5. $options = array(
  6. 'http' => array(
  7. 'header' => "Content-type: application/rpcx\r\n" .
  8. // "X-RPCX-MessageID: 12345678\r\n" .
  9. // "X-RPCX-MesssageType: 0\r\n" .
  10. "X-RPCX-SerializeType: 1\r\n" .
  11. "X-RPCX-ServicePath: Arith\r\n" .
  12. "X-RPCX-ServiceMethod: Mul\r\n",
  13. 'method' => 'POST',
  14. 'content' => $data
  15. )
  16. );
  17. $context = stream_context_create($options);
  18. $result = file_get_contents($url, false, $context);
  19. if ($result === FALSE) { /* Handle error */ }
  20. var_dump($result);
  21. ?>

访问http://localhost/client.php

结果:

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

server.go 输出

  1. C= 200

这样就实现跨语言了。

其他语言示例

https://github.com/rpcxio/rpcx-gateway/blob/master/examples/README.md

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/rpcx-gateway

https://github.com/smallnest/rpcx

links

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