1. 配置信息的发布

    • 配置信息发布请求URL: POST: /v1/cs/configs
    • nacos在STANDALONE模式或集群模式没有指定用mysql情况下使用derby数据库,在集群模式且指定mysql情况下使用mysql
    • 持续化到数据库后立马同步集群中的其他节点
    /**
* 增加或更新非聚合数据。
*
* @throws NacosException
*/
@PostMapping
@Secured(action = ActionTypes.WRITE, parser = ConfigResourceParser.class)
public Boolean publishConfig(HttpServletRequest request, HttpServletResponse response,
@RequestParam("dataId") String dataId, @RequestParam("group") String group,
@RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY)
String tenant,
@RequestParam("content") String content,
@RequestParam(value = "tag", required = false) String tag,
@RequestParam(value = "appName", required = false) String appName,
@RequestParam(value = "src_user", required = false) String srcUser,
@RequestParam(value = "config_tags", required = false) String configTags,
@RequestParam(value = "desc", required = false) String desc,
@RequestParam(value = "use", required = false) String use,
@RequestParam(value = "effect", required = false) String effect,
@RequestParam(value = "type", required = false) String type,
@RequestParam(value = "schema", required = false) String schema)
throws NacosException {
//省略参数验证代码
..................... //persistService.insertOrUpdate插入或更新数据库
//EventDispatcher.fireEvent 通知集群中其他节点
final Timestamp time = TimeUtils.getCurrentTime();
String betaIps = request.getHeader("betaIps");
ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content);
if (StringUtils.isBlank(betaIps)) {
if (StringUtils.isBlank(tag)) {
persistService.insertOrUpdate(srcIp, srcUser, configInfo, time, configAdvanceInfo, false);
EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, dataId, group, tenant, time.getTime()));
} else {
persistService.insertOrUpdateTag(configInfo, tag, srcIp, srcUser, time, false);
EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, dataId, group, tenant, tag, time.getTime()));
}
} else { // beta publish
persistService.insertOrUpdateBeta(configInfo, betaIps, srcIp, srcUser, time, false);
EventDispatcher.fireEvent(new ConfigDataChangeEvent(true, dataId, group, tenant, time.getTime()));
}
return true;
}
  • EventDispatcher.fireEvent 触发 AsyncNotifyService.onEvent事件
  • AsyncNotifyService.onEvent事件中遍历serverList通知集群各个节点
  • 如果某个节点不健康或同步失败延迟一段时间后重新通知:schedule(asyncTask, delay, TimeUnit.MILLISECONDS);

@Service
public class AsyncNotifyService extends AbstractEventListener { //onEvent事件遍历serverList通知集群各个节点
@Override
public void onEvent(Event event) {
// 并发产生 ConfigDataChangeEvent
if (event instanceof ConfigDataChangeEvent) {
ConfigDataChangeEvent evt = (ConfigDataChangeEvent) event;
long dumpTs = evt.lastModifiedTs;
String dataId = evt.dataId;
String group = evt.group;
String tenant = evt.tenant;
String tag = evt.tag;
List<?> ipList = serverListService.getServerList(); // 其实这里任何类型队列都可以
Queue<NotifySingleTask> queue = new LinkedList<NotifySingleTask>();
for (int i = 0; i < ipList.size(); i++) {
queue.add(new NotifySingleTask(dataId, group, tenant, tag, dumpTs, (String) ipList.get(i), evt.isBeta));
}
EXECUTOR.execute(new AsyncTask(httpclient, queue));
}
} // 同步任务逻辑
class AsyncTask implements Runnable {
@Override
public void run() {
executeAsyncInvoke();
} private void executeAsyncInvoke() {
while (!queue.isEmpty()) {
NotifySingleTask task = queue.poll();
String targetIp = task.getTargetIP();
if (serverListService.getServerList().contains(
targetIp)) {
// 启动健康检查且有不监控的ip则直接把放到通知队列,否则通知
if (serverListService.isHealthCheck() && ServerListService.getServerListUnhealth().contains(targetIp)) {
// target ip 不健康,则放入通知列表中
// get delay time and set fail count to the task
// schedule(asyncTask, delay, TimeUnit.MILLISECONDS);
asyncTaskExecute(task);
} else {
// /v1/cs/communication/dataChange?dataId={2}&group={3}
HttpGet request = new HttpGet(task.url);
request.setHeader(NotifyService.NOTIFY_HEADER_LAST_MODIFIED, String.valueOf(task.getLastModified()));
request.setHeader(NotifyService.NOTIFY_HEADER_OP_HANDLE_IP, LOCAL_IP);
httpclient.execute(request, new AsyncNotifyCallBack(httpclient, task));
}
}
}
}
} class AsyncNotifyCallBack implements FutureCallback<HttpResponse> {
// 同步其他节点失败
@Override
public void failed(Exception ex) {
long delayed = System.currentTimeMillis() - task.getLastModified();
//get delay time and set fail count to the task
//延迟一段时间后重新通知
//schedule(asyncTask, delay, TimeUnit.MILLISECONDS);
asyncTaskExecute(task);
MetricsMonitor.getConfigNotifyException().increment();
}
} }
  1. 配置信息的获取
  • 读取配置信息的时候加读锁

    1. lockResult>0 加锁成功,获取配置信息后返回HttpServletResponse.SC_OK 或 HttpServletResponse.SC_NOT_FOUND
    2. lockResult==0对应的配置信息不存在返回 HttpServletResponse.SC_NOT_FOUND
    3. lockResult<0 已有线程加了写锁返回 HttpServletResponse.SC_CONFLICT
  • 单机且不用mysql情况下从数据库中获取,其他配置从文件中获取配置信息(本地文件相当于mysql的缓存)

@Service
public class ConfigServletInner {
/**
* 同步配置获取接口
*/
public String doGetConfig(HttpServletRequest request, HttpServletResponse response, String dataId, String group,
String tenant, String tag, String clientIp) throws IOException, ServletException { int lockResult = tryConfigReadLock(groupKey);
if (lockResult > 0) {
//代码挺多总体逻辑归纳如下
//单机且不用mysql情况下从数据库中获取,其他配置从文件中获取配置信息
//数据库情况下判断从config_info,还是config_info_beta,还是config_info_tag表获取数据
//文件情况下判断从还是BASE_DIR还是BETA_DIR,还是TAG_DIR目录获取配置文件
//本地文件获取比mysql中获取更快,本地文件相当于mysql的缓存
}
}
}

为了查询效率,mysql中的数据缓存到每个集群节点的文件中,缓存时机有3中情况

  • 当nacos启动的时候如果当前时间距离最后一次心跳时间超过6个小时,则全量缓存mysql数据
  • 启动定时任务每6个小时重新全量缓存mysql数据
  • @GetMapping("/dataChange")时处理单个数据
 //@PostConstruct init() 调用 dumpConfigInfo
private void dumpConfigInfo(DumpAllProcessor dumpAllProcessor) throws IOException {
int timeStep = 6;
Boolean isAllDump = true;
// initial dump all
FileInputStream fis = null;
Timestamp heartheatLastStamp = null;
try {
if (isQuickStart()) {
File heartbeatFile = DiskUtil.heartBeatFile();
if (heartbeatFile.exists()) {
fis = new FileInputStream(heartbeatFile);
String heartheatTempLast = IoUtils.toString(fis, Constants.ENCODE);
heartheatLastStamp = Timestamp.valueOf(heartheatTempLast);
//当前时间距离最后一次心跳时间超过6个小时
if (TimeUtils.getCurrentTime().getTime() - heartheatLastStamp.getTime() < timeStep * 60 * 60 * 1000) {
isAllDump = false;
}
}
}
if (isAllDump) {
LogUtil.defaultLog.info("start clear all config-info.");
DiskUtil.clearAll();
//
dumpAllProcessor.process(DumpAllTask.TASK_ID, new DumpAllTask());
}
} catch (IOException e) {
LogUtil.fatalLog.error("dump config fail" + e.getMessage());
throw e;
}
}
 @PostConstruct
public void init() {
//启动定时任务定时缓存数据
TimerTaskService.scheduleWithFixedDelay(dumpAll, initialDelay, DUMP_ALL_INTERVAL_IN_MINUTE, TimeUnit.MINUTES);
}
   /**
* 通知配置信息改变
*/
@GetMapping("/dataChange")
public Boolean notifyConfigInfo(HttpServletRequest request,
@RequestParam("dataId") String dataId, @RequestParam("group") String group,
@RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY)
String tenant,
@RequestParam(value = "tag", required = false) String tag) {
dataId = dataId.trim();
group = group.trim();
String lastModified = request.getHeader(NotifyService.NOTIFY_HEADER_LAST_MODIFIED);
long lastModifiedTs = StringUtils.isEmpty(lastModified) ? -1 : Long.parseLong(lastModified);
String handleIp = request.getHeader(NotifyService.NOTIFY_HEADER_OP_HANDLE_IP);
String isBetaStr = request.getHeader("isBeta");
//其他节点通知变更后缓存数据
if (StringUtils.isNotBlank(isBetaStr) && trueStr.equals(isBetaStr)) {
dumpService.dump(dataId, group, tenant, lastModifiedTs, handleIp, true);
} else {
dumpService.dump(dataId, group, tenant, tag, lastModifiedTs, handleIp);
}
return true;
}
  1. 配置信息的轮询

nacos配置中心之服务器端的更多相关文章

  1. SpringBoot使用Nacos配置中心

    本文介绍SpringBoot如何使用阿里巴巴Nacos做配置中心. 1.Nacos简介 Nacos是阿里巴巴集团开源的一个易于使用的平台,专为动态服务发现,配置和服务管理而设计.它可以帮助您轻松构建云 ...

  2. Nacos配置中心

    本文介绍spring cloud 集成 nacos案例 官方文档:https://nacos.io/zh-cn/docs/what-is-nacos.html](https://nacos.io/zh ...

  3. Spring Cloud 系列之 Alibaba Nacos 配置中心

    Nacos 介绍 Nacos 是 Alibaba 公司推出的开源工具,用于实现分布式系统的服务发现与配置管理.英文全称 Dynamic Naming and Configuration Service ...

  4. Nacos配置中心使用

    在系统开发过程中,开发者通常会将一些需要变更的参数.变量等从代码中分离出来独立管理,以独立的配置文件的形式存在.目的是让静态的系统工件或者交付物(如 WAR,JAR 包等)更好地和实际的物理运行环境进 ...

  5. 微服务从nacos配置中心获得配置信息

    一,安装nacos, 略 二,创建父工程和微服务工程 service1, service2,以idea为例 1, new -> project -> Maven -> 填写group ...

  6. Nacos配置中心和服务的注册发现

    在上一篇中,我们已经把Nacos的集群搭建好了,那么既然已经搭建好了,就要在咱们的项目中去使用.Nacos既可以做配置中心,也可以做注册中心.我们先来看看在项目中如何使用Nacos做配置中心. Nac ...

  7. Spring Cloud Config、Apollo、Nacos配置中心选型及对比

    Spring Cloud Config.Apollo.Nacos配置中心选型及对比 1.Nacos 1.1 Nacos主要提供以下四大功能 2.Spring Cloud Config 3.Apollo ...

  8. Spring Cloud Alibaba(5)---Nacos(配置中心)

    Nacos(配置中心) 有关Spring Cloud Alibaba之前写过四篇文章,这篇也是在上面项目的基础上进行开发. Spring Cloud Alibaba(1)---入门篇 Spring C ...

  9. 【Nacos】Springboot整合Nacos配置中心(二) 多环境配置

    本篇随笔接上一篇文章:Springboot整合Nacos配置中心(一),主要记录Nacos多环境的配置的方法 Nacos多环境的配置 方法一: 1.在项目中的bootstrap.yaml文件中配置激活 ...

随机推荐

  1. Educational Codeforces Round 91 (Rated for Div. 2) B. Universal Solution (贪心)

    题意:石头剪刀布,bot有一串字符,表示他要出什么,你需要事先确定你的出招方案,然后遍历bot的字符串,从\(i\)位置开始跑一个循环,每次跑都要记录你赢的次数贡献给\(sum\),现要求\(\fra ...

  2. CQRS+Event Sourcing

    using System; using System.Collections.Generic; using System.Linq; namespace CQRS { public class Eve ...

  3. 4.PowerShell DSC核心概念之配置

    什么是配置 DSC 配置是定义某一特殊类型函数的 PowerShell 脚本. 配置的语法 Configuration MyDscConfiguration { #配置块 Import-DscReso ...

  4. Redis 数据迁移 & 数据审计

    Redis 数据迁移 安装迁移工具 # 安装依赖 [root@dbtest03 ~]# yum install -y automake libtool autoconf bzip2 git # 拉取工 ...

  5. K8S(16)集成实战-使用spinnaker进行自动化部署

    K8s集成实战-使用spinnaker进行自动化部署 1 spinnaker概述和选型 1.1 概述 1.1.1 主要功能 Spinnaker是一个开源的多云持续交付平台,提供快速.可靠.稳定的软件变 ...

  6. HTTP常见状态码(200、301、302、404、500、502)详解

         概述 运维工作中,在应用部署的时候,通常遇到各种HTTP的状态码,我们比较常见的如:200.301.302.404.500.502 等,有必要整理一份常见状态码的文档,加深印象,方便回顾. ...

  7. 数理统计10(习题篇):寻找UMVUE

    利用L-S定理,充分完备统计量法是寻找UMVUE的最方便方法,不过实际运用时还需要一些小技巧,比如如何写出充分完备统计量.如何找到无偏估计.如何求条件期望,等等.课本上的例题几乎涵盖了所有这些技巧,我 ...

  8. 关于rand()

    虽然很早就知道rand是伪随机了,但是一般都懒得用srand. 直到模拟银行家算法时不用srand就造成数据实在有点假(-_-||) 所以要记得srand((int)time(0))啊

  9. HDU 3949 XOR (线性基第k小)题解

    题意: 给出\(n\)个数,求出子集异或第\(k\)小的值,不存在输出-1. 思路: 先用线性基存所有的子集,然后对线性基每一位进行消元,保证只有\(d[i]\)的\(i\)位存在1,那么这样变成了一 ...

  10. Linux 驱动框架---linux 驱动

    总述 Linux 系统下的驱动最后都是以如下这个结构体呈现在系统中的,注意其中的dev_pm_ops是内核新增的内容来准备替换platform_driver中的电源管理相关的内容.这里内容是先进行总体 ...