【go-zero框架】2.服务注册与发现

文章目录

  • 1 server端的服务注册
    • 1.1 用法
    • 1.2 原理
  • 2 client端的服务发现
    • 2.1 用法
    • 2.2 原理

go-zero支持三种服务注册与发现方式:

  • 直连:指定目标地址endpoints
  • 基于etcd
  • 基于k8s:依赖k8s的集群管理机制,服务发现时直接去k8s的Endpoints里获

本篇讨论etcd的服务注册与发现方式~

1 server端的服务注册

1.1 用法

只需要在rpc的服务配置yaml文件中,定义etcd配置即可:

Rpc:
  Etcd:
     Hosts:
     - 192.168.0.111:2379
     - 192.168.0.112:2379
     - 192.168.0.113:2379
     Key: user.rpc
  • Host: etcd集群地址
  • Key: 服务标识,go-zero将以此key作为etcd注册key的前缀

使用配置启动服务,可以看到,go-zero已经帮我们把key注册到了etcd中了:

$ etcdctl --endpoints=121.0.0.1:2379 --prefix=true get user.rpc
user.rpc/7952388177561657362
localhost:9000
  • key:{服务标识}/{etcd租约id}
  • value:服务请求地址。

1.2 原理

go-zero在启动服务时的服务注册过程如下:

rpc服务入口初始化的调用链 zrpc.MustNewServer->zrpc.NewServer->internal.NewRpcPubServer

在internal.NewRpcPubServer中定义出了etcd服务注册的func registerEtcd, 代码:

func NewRpcPubServer(etcd discov.EtcdConf, listenOn string, middlewares ServerMiddlewaresConf, opts ...ServerOption) (Server, error) {
	registerEtcd := func() error {
		//...
		
		// etcd身份认证
		if etcd.HasAccount() {
			pubOpts = append(pubOpts, discov.WithPubEtcdAccount(etcd.User, etcd.Pass))
		}
		
		// tls认证
		if etcd.HasTLS() {
			pubOpts = append(pubOpts, discov.WithPubEtcdTLS(etcd.CertFile, etcd.CertKeyFile,
				etcd.CACertFile, etcd.InsecureSkipVerify))
		}
		
		// 是否指定了leaseId
		if etcd.HasID() {
			pubOpts = append(pubOpts, discov.WithId(etcd.ID))
		}
		
		// 将key写入etcd,并开启一个goroutine持续续租
		pubClient := discov.NewPublisher(etcd.Hosts, etcd.Key, pubListenOn, pubOpts...)
		return pubClient.KeepAlive()
	}
	server := keepAliveServer{
		registerEtcd: registerEtcd,
		Server:       NewRpcServer(listenOn, middlewares, opts...),
	}

	return server, nil
}

那么在服务启动时,通过调用链:s.Start->rs.server.Start->keepAliveServer.Start->registerEtcd

就实际调用了registerEtcd()方法,完成服务的注册。

2 client端的服务发现

2.1 用法

client端在与server端建立连接时,在代码中添加etcd配置即可:

func main() {
    conn := zrpc.MustNewClient(zrpc.RpcClientConf{
        Etcd: discov.EtcdConf{
            Hosts: []string{"127.0.0.1:2379"},
            Key:   "user.rpc",  // 服务标识,与服务端yaml中定义的保持一致
        },
    })
    client := greet.NewGreetClient(conn.Conn())
    resp, err := client.Ping(context.Background(), &greet.Request{})
    //...
}

这样客户端就可以直接通过etcd去发现目的服务了。

2.2 原理

go-zero在客户端连接时的服务发现过程如下:

初始化客户端NewClient的调用链:zrpc.MustNewClient->NewClient->internal.NewClient->cli.dial->grpc.DialContext->cc.idlenessMgr.ExitIdleMode->m.enforcer.ExitIdleMode->idler.ExitIdleMode->cc.resolverWrapper.start->ccr.cc.resolverBuilder.Build

最后调到的实际是go-zero中定义的discovBuilder.Build,它实现了grpc resolver的Build,完成实际的服务发现,即从etcd查找与监听服务注册的key的过程。

看一下discovBuilder.Build的实现:

type discovBuilder struct{}

func (b *discovBuilder) Build(target resolver.Target, cc resolver.ClientConn, _ resolver.BuildOptions) (resolver.Resolver, error) {
	//...
	
	// subscriber订阅etcd事件,监听key的变化
	sub, err := discov.NewSubscriber(hosts, targets.GetEndpoints(target))
	if err != nil {
		return nil, err
	}
    
    // 取出当前etcd中的值,也就是目标地址(多个),采用某种负载均衡策略选择一个进行连接
	update := func() {
		var addrs []resolver.Address
		// sub.Values中保存了实时的value, 有事件变化时会更新
		for _, val := range subset(sub.Values(), subsetSize) {
			addrs = append(addrs, resolver.Address{
				Addr: val,
			})
		}
		
		// 有变更时将地址更新给grpc
		if err := cc.UpdateState(resolver.State{
			Addresses: addrs,
		}); err != nil {
			logx.Error(err)
		}
	}
	sub.AddListener(update)
	update()

	return &nopResolver{cc: cc}, nil
}

你可能感兴趣的:(后端框架与模型,golang,开发语言,web服务框架,rpc)