几个方面谈一下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. 字体图标转base64

    如果你在阿里矢量库下载了字体图标在项目引入无法显示时,可以把图标转成base64 在线转换的链接 https://transfonter.org/ css字体图标的制作

  2. SpringBoot 整合NoSql

    通用配置 maven依赖 添加Spring-Web和Spring-Security依赖,使用Spring-Security是因为使用SpringBoot的Redis依赖时,必须添加Spring-Sec ...

  3. Python3.7.1学习(三)求两个list的差集、并集与交集

    在python3.7.1对列表的处理中,会经常使用到Python求两个list的差集.交集与并集的方法. 下面就以实例形式对此加以分析. # 求两个list的差集.并集与交集# 一.两个list差集# ...

  4. Java,你告诉我 fail-fast 是什么鬼?

    本篇我们来聊聊 Java 的 fail-fast 机制,文字一如既往的有趣哦. 01.前言 说起来真特么惭愧:十年 IT 老兵,Java 菜鸟一枚.今天我才了解到 Java 还有 fail-fast ...

  5. C语言|博客作业09

    这个作业属于哪个课程 C语言程序设计II 这个作业的要求在哪里 https://edu.cnblogs.com/campus/zswxy/CST2019-1/homework/10027 我在这个课程 ...

  6. scrapy_redis分布式爬虫

    文章来源:https://github.com/rmax/scrapy-redis Scrapy-Redis Documentation: https://scrapy-redis.readthedo ...

  7. API的描述语言--Swagger

    Swagger是一种Rest API的表示方式. 有时也可以作为Rest API的交互式文档,描述形式化的接口描述,生成客户端和服务端的代码. 一,描述语言:Spec Swagger API Spec ...

  8. 词袋模型(BOW,bag of words)和词向量模型(Word Embedding)概念介绍

    例句: Jane wants to go to Shenzhen. Bob  wants to go to Shanghai. 一.词袋模型 将所有词语装进一个袋子里,不考虑其词法和语序的问题,即每个 ...

  9. springboot+swagger接口文档企业实践(上)

    目录 1.引言 2.swagger简介 2.1 swagger 介绍 2.2 springfox.swagger与springboot 3. 使用springboot+swagger构建接口文档 3. ...

  10. altium designer 20.0.8

    altium designer 20.0.8 download : http://dl3.downloadly.ir/Files/Software/Altium_Designer_20.0.8_Bet ...