10

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

 3 years ago
source link: https://www.lanindex.com/%e5%ad%a6%e4%b9%a0golang%e4%b9%8b%e6%9c%8d%e5%8a%a1%e5%99%a8%e6%a1%86%e6%9e%b6%e7%bc%96%e5%86%99-%e5%90%8d%e5%ad%97%e5%8f%91%e7%8e%b0%e6%9c%8d%e5%8a%a1/
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.

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

2020/10/02 · Leave a comment

名字发现服务(后文简称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的实现

程序启动流程

Naming%E5%90%AF%E5%8A%A81.png

在新的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

具体交互序列图:

Naming%E6%B3%A8%E5%86%8C%E4%B8%8E%E6%9B%B4%E6%96%B0.png

一些设计细节思考

为什么不用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 Cancel reply

Your email address will not be published.

在此浏览器中保存我的名字、电邮和网站。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK