几个方面谈一下Nacos的设计(作为注册中心,基于此时的develop分支)

原创博文,转载请注明来源

客户端与集群的交互

首先需要声明的是Nacos Cluster虽然内部使用了Raft协议但是对于Nacos客户端,Cluster实例是无状态的。客户端配置集群地址有两种方式:

1.通过配置serverAddr列表,客户端将访问集群时,随机从列表中选择一个实例访问:

NamingService configService = NacosFactory.createNamingService("10.22.0.137:30253,10.22.0.137:30254,10.22.0.137:30255");

当然,一般情况下我们并不会直接配置Nacos实例的IP,可用用域名,以便能动态发现。

2.通过Properties配置endpoint,定时访问,感知集群变化,并随机从接口返回的列表中选择一个实例访问,客户端会与Endpoint创建LONG PULL。


Properties properties = new Properties();
properties.put(PropertyKeyConst.ENDPOINT,"10.18.90.16");
properties.put(PropertyKeyConst.ENDPOINT_PORT,"8850"); NamingService configService = NacosFactory.createNamingService(properties);

数据同步

实例信息同步

实例信息的由一个叫 Distro (com.alibaba.nacos.naming.consistency.ephemeral.distro.DistroConsistencyServiceImpl)的一致性协议维护,有如下几个特点:

  1. 最终一致性,由实例间通过http同步(com.alibaba.nacos.naming.consistency.ephemeral.distro.TaskDispatcher.TaskScheduler)到除了自己的每个实例节点,且携带自己全量数据

  2. 数据不持久化,保存在内存(com.alibaba.nacos.naming.consistency.ephemeral.distro.DataStore)
  3. 每个节点通过算法,只接受一部分请求(com.alibaba.nacos.naming.web.DistroFilter),如果不属于实例自己的请求过来则通过算法确定并转发。

举例解释一下第3点:现有Nacos集群实例A,B,C 共3个。从客户端与集群的交互知道,客户端随机从A,B,C中随机选择一个实例访问,客户端NACOS-DEMO选择访问B的注册实例接口,如果NACOS-DEMO的请求应该属于C处理的话,本次请求将会被B实例中的DistroFilter拦截掉,并由B转发到C。理解起来,其实挺绕的,但是为什么这么设计呢?毕竟A,B,C的实例数据都会最终一致的,我随机访问任意一个实例就好了

我的解释是:

由于数据是最终一致的,中间会存在同步过程,所以如果存在写了马上查的场景,则很有可能查不到的情况(客户端写和查两次请求落在了两个不同的实例)。但是如果通过算法,一个实例的增删改查都在同一个确定的实例,就不会出现这种情况了。

服务集群信息

  • 通过Open API服务创建接口创建,则直接通过raft协议 持久化到集群内所有节点
  • 如果实例注册时,如果服务集群不存在,则静默创建,前面说了,实例注册时,会通过Distro 协议通过实例数据到集群内其它节点,由于实例信息也附带了服务集群等信息,所以实例上也顺便同步了服务集群等信息。所以,默认情况下,静默创建的服务集群等信息也是没有持久化的。当实例注册时ephemeral字段设置为false,除了上诉的同步方式外,还会调用raft协议,该协议会同步到其它节点并持久化(Raft协议中规定,只有leader才能处理客户端请求,所有当发现自己不是leader时,会转发请求,实现如下图所示):



ephemeral字段的介绍

(Nacos 在 1.0.0版本 instance级别增加了一个ephemeral字段,该字段表示注册的实例是否是临时实例还是持久化实例。如果是临时实例,则不会在 Nacos 服务端持久化存储,需要通过上报心跳的方式进行包活,如果一段时间内没有上报心跳,则会被 Nacos 服务端摘除。在被摘除后如果又开始上报心跳,则会重新将这个实例注册。持久化实例则会持久化被 Nacos 服务端,此时即使注册实例的客户端进程不在,这个实例也不会从服务端删除,只会将健康状态设为不健康)

关于priv-raft协议

Raft协议第8节部分内容:

Clients of Raft send all of their requests to the leader.

When a client first starts up, it connects to a randomlychosen

server. If the client’s first choice is not the leader,

that server will reject the client’s request and supply information

about the most recent leader it has heard from

(AppendEntries requests include the network address of

the leader). If the leader crashes, client requests will time

out; clients then try again with randomly-chosen servers


Raft的客户将所有请求发送给leader。当客户机第一次启动时,

它连接到随机选择的服务器。如果客户机的首选不是leader,

服务器将拒绝客户机的请求,并提供它最近听到的leader的信息。

如果leader崩溃,客户端请求将超时;然后,客户端再次尝试随机选择的服务器

Nacos官网说,自己实现的事一个轻量级的raft协议,原因我认为至少有如下两点:

  1. 客户端是随机选择并访问Nacos实例的
  2. Nacos实例分为leader和flower,leader负责写入,leader和flower都可以查询,标准的raft协议,客户端所有请求会发送给leader保证强一致性,nacos的实现是最终一致性。

Raft协议论文

Nacos集群在k8s中的实践

Nacos在VM环境下,部署集群就比较简单了,如下图:



只需要在部署Nacos 实例时,在conf/Cluster.conf 中把自己和集群内其它实例的地址整合到一起即可,比如上图的架构,Cluster.conf文件可以是这样:

ip1:8848

ip2:8848

ip3:8848

这样就构成了集群。Nacos实例会轮询Cluster.conf 文件,以保证集群在有新的实例加入时能相互发现以实现实例的扩缩容,具体代码实现在(com.alibaba.nacos.naming.cluster.ServerListManager.ServerListUpdater)。

所以在k8s环境下Nacos的集群应该怎么玩?毋庸置疑的是,我们不再需要手动去更改Cluster.conf文件来维护集群,必须使用k8s的机制去完成自动扩缩容,那么问题来了,k8s完成扩缩容,,集群自己怎么知道呢?

其实是peer-finder-plugin 和 上面说的自动轮询Cluster.conf机制来达到的。

分析:

通过Helm使用官方的chart包部署的nacos集群,每一个pod有两个容器:

  • peer-finder-plugin-install : 是一个initContainers ,这个镜像唯一的作用是安装 peer-finder插件相关的脚本到指定目录(plugins/peer-finder/),结束后就死亡。
  • nacos-cluster:nacos本体镜像,镜像启动时,除了启动自身的服务外,还会同时调用peer-finder-plugin-install 安装好的脚本启动peer-finder(nacos dockerfile

    peer-finder这个插件很简单,使用go语言写的一个定时轮询的程序,核心代码:

peer-finder轮询(1秒)指定的k8s service ,如果service下面的pod地址列表发生变化,则重新写入Cluster.conf文件。这里的k8s service就必须是headless类型的了,因为只有解析headless提供的域名,才能获取所有pod的地址列表

pod启动时序图:

调用关系图:

从源码看Nacos的设计的更多相关文章

  1. 从微信小程序开发者工具源码看实现原理(一)- - 小程序架构设计

    使用微信小程序开发已经很长时间了,对小程序开发已经相当熟练了:但是作为一名对技术有追求的前端开发,仅仅熟练掌握小程序的开发感觉还是不够的,我们应该更进一步的去理解其背后实现的原理以及对应的考量,这可能 ...

  2. Alink漫谈(二) : 从源码看机器学习平台Alink设计和架构

    Alink漫谈(二) : 从源码看机器学习平台Alink设计和架构 目录 Alink漫谈(二) : 从源码看机器学习平台Alink设计和架构 0x00 摘要 0x01 Alink设计原则 0x02 A ...

  3. 从源码看JDK提供的线程池(ThreadPoolExecutor)

    一丶什么是线程池 (1)博主在听到线程池三个字的时候第一个想法就是数据库连接池,回忆一下,我们在学JavaWeb的时候怎么理解数据库连接池的,数据库创建连接和关闭连接是一个比较耗费资源的事情,对于那些 ...

  4. ibatis源码学习1_整体设计和核心流程

    背景介绍ibatis实现之前,先来看一段jdbc代码: Class.forName("com.mysql.jdbc.Driver"); String url = "jdb ...

  5. 从微信小程序开发者工具源码看实现原理(四)- - 自适应布局

    从前面从微信小程序开发者工具源码看实现原理(一)- - 小程序架构设计可以知道,小程序大部分是通过web技术进行渲染的,也就是最终通过浏览器的dom tree + cssom来生成渲染树:既然最终是通 ...

  6. 从jvm源码看synchronized

    从jvm源码看synchronized 索引 synchronized的使用 修饰实例方法 修饰静态方法 修饰代码块 总结 Synchronzied的底层原理 对象头和内置锁(ObjectMonito ...

  7. 从linux源码看socket(tcp)的timeout

    从linux源码看socket(tcp)的timeout 前言 网络编程中超时时间是一个重要但又容易被忽略的问题,对其的设置需要仔细斟酌.在经历了数次物理机宕机之后,笔者详细的考察了在网络编程(tcp ...

  8. 从Linux源码看Socket(TCP)的listen及连接队列

    从Linux源码看Socket(TCP)的listen及连接队列 前言 笔者一直觉得如果能知道从应用到框架再到操作系统的每一处代码,是一件Exciting的事情. 今天笔者就来从Linux源码的角度看 ...

  9. 从Linux源码看Socket(TCP)的accept

    从Linux源码看Socket(TCP)的accept 前言 笔者一直觉得如果能知道从应用到框架再到操作系统的每一处代码,是一件Exciting的事情. 今天笔者就从Linux源码的角度看下Serve ...

随机推荐

  1. 4.vim编辑器

    把光标移动文件头 gg 把光标移动文件尾 G 移动到行首 ^ 移动到行尾 $ 移动到指定行 :n 回车

  2. 生产者-消费者模型在Hudi中的应用

    介绍 生产者-消费者模型用于解耦生产者与消费者,平衡两者之间的能力不平衡,该模型广泛应用于各个系统中,Hudi也使用了该模型控制对记录的处理,即记录会被生产者生产至队列中,然后由消费者从队列中消费,更 ...

  3. Alibaba Nacos 学习(三):Spring Cloud Nacos Discovery - FeignClient,Nacos 服务注册与发现

    Alibaba Nacos 学习(一):Nacos介绍与安装 Alibaba Nacos 学习(二):Spring Cloud Nacos Config Alibaba Nacos 学习(三):Spr ...

  4. PHP安全之道学习笔记2:编码安全指南

    编码安全指南 编程本身就应该是一门艺术,而安全编程更是一种在刀尖上舞蹈的艺术,不仅要小心脚下的锋利寒刃,更要小心来自网络黑客或攻击者的狂轰乱炸. - by code artist 1.hash比较的缺 ...

  5. HBase 基本入门

    目录 一.简介 有什么特性 与RDBMS的区别 二.数据模型 三.安装HBase 四.基本使用 表操作 五.FAQ 参考文档 无论是 NoSQL,还是大数据领域,HBase 都是非常"炙热& ...

  6. 根据json数据中某一个属性 处理数组重组的方法 (二种)

    需求:根据role 的不同分组 渲染页面 进行后期操作 后台返回数据:   因为后台返回的json数据不是我们想要的 所以就得自己来了~  要啥样整啥样 js: 第一种处理方法 使用方法: 1: th ...

  7. linux runlevel运行级别

    1.linux通过设定runlevel来设定系统使用不同的服务启动,从而使得linux运行的环境有所不同. 2.当系统启动后会按照以下步骤完成初始化:运行/sbin/init程序加载器配置文件/etc ...

  8. 各种优化方法总结比较(sgd/momentum/Nesterov/adagrad/adadelta)

    前言 这里讨论的优化问题指的是,给定目标函数f(x),我们需要找到一组参数x,使得f(x)的值最小. 本文以下内容假设读者已经了解机器学习基本知识,和梯度下降的原理. Batch gradient d ...

  9. ExtentTestNGIReporterListener

    package com.testng.config; import com.aventstack.extentreports.ExtentReports; import com.aventstack. ...

  10. 在docker中加入加速器的方法

    前提条件:在一台Linux中安装好了docker 目的:在docker中加如这入个加速器的目的,是让docker pull 时能速度快一点,但是好像docker push速度并没有加快. 换句话说,就 ...