学习Golang之服务器框架编写 – 名字发现服务

名字发现服务(后文简称Naming)一般使用在分布式系统中,当新节点加入时,如何让其他节点知道?

原始的做法是使用配置文件,对每个节点可相连的节点进行配置。这种方法费时费力还容易出错。所以现代系统中一般使用配置中心或者Naming对新加入的节点进行管理,让其他节点感知到它的存在,从而进行通信。

剔除节点的情形也类似,使用Naming机制能更方便的屏蔽失效节点。所以Blue也内置了一套简单的Naming。

Blue中的Naming为了解决什么问题?

首先要说明是的,Blue不是针对分布式系统开发的,只是参考了分布式系统的思想,希望使用Blue搭建的服务能方便的平行扩缩容,让Blue内建的RPC更容易使用。所以在功能上来说Naming主要提供:

  • 存储所有节点的有效信息;
  • 感知节点的加入与失效;
  • 广播变化节点的信息至相关节点;

实现的方案选择

其实go语言对于Naming的实现有大名鼎鼎的 etcd ,一个分布式、高可用的k-v存储系统。它的一个特性是提供了 gRPC naming and discovery ,依托于gRPC提供Naming。其实我设计Blue Naming的思想有些受到它的影响 – RPC必须很好的和Naming配合起来。(自信点的说法是不谋而合,在最开始构思Blue的时候就选定了gRPC作为内置RPC实现方案,但是那个时候我并不知道有 etcd 这个大杀器)

最后之所以没有选择 etcd 的原因是:功能太大而全了!它的成名主要归功于k8s使用其做Naming,追根溯源的话,它并不是以Naming为目的开发。

要说有直接拿来用且功能符合预期的轮子还有一个 consul ,其生来就是为Naming。但是经过评估后,发现把它融合进Blue并且不破坏设计目标比较难,所以也放弃了。

所以最终还是手动写一个Blue专有的Naming,它不具有普适性,但是确是最适合Blue的定制版。

Blue Naming的实现

程序启动流程

在新的Goroutine中将节点内存数据落地到文件,这是一个定时的异步过程,对于一个节点存储的信息如下:

type ServerInfo struct {
    app           string
    set           string     
    name          string
    addr          string
    isActive      bool
    lastHeartBeat time.Time
    update        chan bool
}

需要注意的是update字段,这是一个更新通知器,当该节点需要更新数据时管道会被写入数据。(update管道缓存大小默认10)

节点上报更新

在gRPC使用的provider.proto文件里,一共有三个接口提供节点与Naming的交互:

  • Registry           –  节点注册,节点初始化时调用
  • HeartBeat        –  节点心跳,节点定期调用
  • UpdateInfo      –  节点更新,从Naming主动推送的stream

具体交互序列图:

一些设计细节思考

为什么不用HeartBeat带回UpdateInfo

最初的设计是想用HeartBeat带回UpdateInfo,一方面可以简化设计,一方面可以提高网络利用率,但是最终因为实时性的原因放弃了这个方案。还是增加了UpdateInfo推送流;

为什么Naming收到第一个HeartBeat才将节点置active

在Naming收到第一个HeartBeat才将节点置位active,这么做的原因是,Blue无法保证自定义的程序在Registry到程序正常运行这段时间是否会有异常。(比如这中间有一个自定义的预处理,它存在一个严重错误,需要整个程序退出,如果在Registry阶段将其设置成active广播到其他节点是不合理的,这样会导致在至少两个超时时间内节点处于假死状态)

所以在Registry10秒后才会发第一个HeartBeat告知Naming:我启动好了,可以接受服务。同时也要求使用Blue的自定义程序能在10秒内将初始化工作完成,让节点进入可服务状态。

这里比较完美做法是,留下一个通知接口让使用者调用,显式告诉Blue节点已经准备好了,可以进行服务了。但同时考虑到不应该对使用者的使用方式做过多的干预和要求,这里暂时还是使用约定时长的方式。

Naming的可用性问题

目前Blue的Naming是单点的,但即使Naming停止了服务也不影响现有节点之间的通讯与服务,可能的故障分三种情况:

  1. Naming进程跪了。不要紧,我们有落地的文件,Naming重启的时候会加载文件里内容,虽然文件可能会有脏数据,但是在HeartBeat时会把错误的状态纠正;
  2. Naming所在的机器宕机了。这个还好,只需要尽快回复机器,重启Naming进程;
  3. Naming所在的机器宕机无法回复并且硬盘挂了。这个有点麻烦,换机器会导致Naming服务地址变动,最终需要修改节点的Naming配置;

高可用的解决方案很多,从主备到主主,甚至分布式,我个人更倾向于主主,可用性理论最高可以达到99.9%,后面有必要的话会尝试扩展。

(全文结束)


转载文章请注明出处:漫漫路 - lanindex.com

Leave a Comment

Your email address will not be published.