浅谈Go与RPC(二)

125 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第19天,点击查看活动详情

今天开学RPC的相关操作,先用一个简单示例来看看如何快速完成一次RPC操作

简单示例

安装

首先,需要先安装rpcx一些基础功能

go get -u -v github.com/smallnest/rpcx/...

如果要使用etcd作为注册中心,需要加上etcd这个标签tag

go get -u -v -tags "etcd" github.com/smallnest/rpcx/...

为了后续方便使用,这里可以安装所有的tags,有些可能现在用不到

go get -u -v -tags "reuseport quic kcp zookeeper etcd consul ping" github.com/smallnest/rpcx/...

这里加上的tags对应关系如下:

  • quic: 支持 quic 协议
  • kcp: 支持 kcp 协议
  • zookeeper: 支持 zookeeper 注册中心
  • etcd: 支持 etcd 注册中心
  • consul: 支持 consul 注册中心
  • ping: 支持网络负载均衡
  • reuseport: 支持 reuseport功能

Service端实现

定义一个结构体


import "context"

type Args struct {
	A int
	B int
}

type Reply struct {
	C int
}

type YYQQ int

func (t *YYQQ) Mul(ctx context.Context, args *Args, reply *Reply) error {
	reply.C = args.A * args.B
	return nil
}
  • YYQQ 是一个GO类型,定义了一个方法 Mul。
  • Mul函数的第 1 个参数是 context.Context上下文
  • Mul函数的第 2 个参数是 args, args 包含了请求的数据 A 和
  • Mul函数的第 3 个参数是 reply, 包含了返回数据的结构体Reply
  • Mul函数的 返回类型是 error ,可以返回nil
  • Mul函数把 A * B 的结果赋值给Reply.C

现在我们已经定义了一个叫做 YYQQ 的 service, 并且为它实现了 Mul 方法

接着我们把这个服务注册给服务器,注册一个服务代码如下:

package main

import (
   "flag"
   "github.com/smallnest/rpcx/server"
   "review/example"
)

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

func main() {
   flag.Parse()
   s := server.NewServer()
   s.RegisterName("YYQQ", new(example.YYQQ), "")
   err := s.Serve("tcp", *addr)
   if err != nil {
      panic(err)
   }
}

把服务的名称设置为YYQQ,通过s.Register(new(example.YYQQ),"")进行服务注册

这里使用服务的 类型名称 作为调用服务名

Client端实现

package main

import (
   "context"
   "flag"
   "github.com/smallnest/rpcx/client"
   "log"
   "review/example"
)

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

func main() {
   flag.Parse()
   // #1
   d, _ := client.NewPeer2PeerDiscovery("tcp@"+*addr, "")
   
   // #2
   xclient := client.NewXClient("YYQQ", client.Failtry, client.RandomSelect, d, client.DefaultOption)
   defer xclient.Close()

   // #3
   args := &example.Args{
      A: 10,
      B: 20,
   }

   // #4
   reply := &example.Reply{}

   // #5
   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)
}
  1. 第一步定义用NewPeer2PeerDiscovery()点对点方式来实现服务发现,客户端直连服务器来获取服务地址
  2. 创建了 XClient, 并且传进去了 FailMode、 SelectMode 和默认选项。 FailMode 告诉客户端如何处理调用失败,重试、快速返回,或者 尝试另一台服务器。 SelectMode 告诉客户端如何在有多台服务器提供了同一服务的情况下选择服务器
  3. 定义了请求:想要获得 10 * 20 的返回结果。 当然我们可以自己算出结果是 200,可以与服务器返回的数据比较是否一致
  4. 定义了响应对象, 默认值是0值, 事实上 rpcx 会通过它来判断返回结果的类型,然后把结果反序列化到这个对象上
  5. 最后调用了远程服务并且同步获取结果

客户端返回结果:

image.png

服务端返回结果:

image.png

异步调用Service

// #5 异步调用
call, err := xclient.Go(context.Background(), "Mul", args, reply, nil)
if err != nil {
   log.Fatalf("failed to call: %v", err)
}

replyWait := <-call.Done
if replyWait.Error != nil {
   log.Fatalf("failed to call: %v", replyWait.Error)
} else {
   log.Printf("%d * %d = %d", args.A, args.B, reply.C)
}

这里需要使用xclient.Go来替换xclient.Call,把结构返回到一个管道中,就可以通过监听管道来获取返回值

总结

今天浅谈了Go与RPC(二),这章介绍了如何快速实现一个RPC的例子,接下来会继续深入了解RPC的相关知识,对于一个涉世未深的我来说,还有许多地方需要学习,有错误的地方欢迎大家指出,共同进步!!