几个方面谈一下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. Git常用命令(基础)

    Workspace:工作区 Index / Stage:暂存区 Repository:仓库区(或本地仓库) Remote:远程仓库 一.新建代码库 # 在当前目录新建一个Git代码库 $ git in ...

  2. 理解Spark运行模式(三)(STANDALONE和Local)

    前两篇介绍了Spark的yarn client和yarn cluster模式,本篇继续介绍Spark的STANDALONE模式和Local模式. 下面具体还是用计算PI的程序来说明,examples中 ...

  3. Mybatis加入日志

    *在mybatis-config.xml核心配置文件中加入如下设置,在configration中标签中加入 <!--打印日志,方便看输出SQL --> <settings> & ...

  4. iOS界面流畅技巧之微博 Demo 性能优化技巧

    微博 Demo 性能优化技巧 我为了演示 YYKit 的功能,实现了微博和 Twitter 的 Demo,并为它们做了不少性能优化,下面就是优化时用到的一些技巧. 预排版 当获取到 API JSON ...

  5. 全栈项目|小书架|微信小程序-登录及token鉴权

    小程序登录 之前也写过微信小程序登录的相关文章: 微信小程序~新版授权用户登录例子 微信小程序-携带Token无感知登录的网络请求方案 微信小程序开通云开发并利用云函数获取Openid 也可以通过官方 ...

  6. CentOS 7下配置ISO镜像文件为本地yum源

    环境限制外网怎么办?离线环境怎么解决依赖?yum源配起来,可以解决大部分包的安装^_^ 环境: 虚拟机:VMware Workstation Pro 12.x Linux系统版本:CentOS-7-x ...

  7. C#Windows Forms (Demo.SYS)--xdd

    private void Show_background_picture()//随机更换背景 { ";//默认值 Random ran = new Random(); , );//返回一个1 ...

  8. SpringCloud Alibaba微服务实战一 - 基础环境准备

    Springcloud Aibaba现在这么火,我一直想写个基于Springcloud Alibaba一步一步构建微服务架构的系列博客,终于下定决心从今天开始本系列文章的第一篇 - 基础环境准备. 该 ...

  9. sql server建库建表(数据库和数据表的常用操作)

    数据库和数据表 (开发常用 操作) 一,数据库的创建 一个SQLServer 是由两个文件组成的:数据文件(mdf) 和日志文件(ldf),所以我们创建数据库就是要为其指定数据库名.数据文件和日志文件 ...

  10. Android项目依赖库管理方式简介

    在实际的android项目开发过程中,我们一般都会用一些现有的第三方库来实现我们的需求,避免重复造轮子.普遍使用到的,例如:网络请求库.图片处理库.界面UI库(自定义View.动画效果等).各种第三方 ...