nacos配置中心的底层原理以及实现

简介: nacos配置中心的底层原理以及实现

一,nacos的配置中心

主要就是通过这个nacos来作为一个配置中心,来统一管理这个配置

1,nacos客户端源码分析

nacos客户端所有的这个文件配置实现主要是在这个NacosNamingService的类下面,那么这个配置中心主要是在这个NacosConfigService的这个类下面。该接口下面主要有一些获取配置,发布配置,增加监听器,删除配置,删除监听器等操作。

public interface ConfigService {
    //获取配置
    String getConfig();
    //删除配置
    boolean removeConfig(String dataId, String group);
    //发布
    boolean publishConfig();
    //监听
    void addListener();
    //删除监听器
    void removeListener();
}

1.1,nacos客户端获取服务配置

在加载完所有的context上下文之后,客户端就回去拉取这个注册中心里面的这个全部配置文件

@Override
public String getConfig(String dataId, String group, long timeoutMs) throws NacosException {
    return getConfigInner(namespace, dataId, group, timeoutMs);
}

然后在这个getConfigInner方法里面,就是具体的拉取配置这个实现

private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException{
    // 优先使用本地配置
    String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant);
    //如果本地配置不为空,则直接返回
    if (content != null) {
        return content;
    }
    //如果本地配置为空,就会去服务端那边拉取这个全部的配置文件
    //需要通过这个http请求发起这个远程调用
    try{
        //拉取这个需要的配置
        String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs);
        //保存这个结果到本地
        cr.setContent(ct[0]);
    }
}

这个读取的本地配置的具体实现如下,主要是通过这个getFailover方法实现

public static String getFailover(String serverName, String dataId, String group, String tenant) {
    //获取这个本地文件,
    File localPath = getFailoverFile(serverName, dataId, group, tenant);
    //如果本地文件为空,则直接return返回
    if (!localPath.exists() || !localPath.isFile()) {
        return null;
    }
    //本地文件不为空,则读取
    return readFile(localPath);
}

如果本地为空,则需要去向这个服务端的配置中心发起http请求,并且最后会通过这个接口回调来判断这个响应的状态码。主要是在这个getServerConfig的方法里面具体实现

public String[] getServerConfig(String dataId, String group, String tenant, long readTimeout) throws NacosException {
    HttpRestResult<String> result = null;
    //建立http请求
    result = agent.httpGet(Constants.CONFIG_CONTROLLER_PATH, null, params, agent.getEncode(), readTimeout);
    switch (result.getCode()) {
            case HttpURLConnection.HTTP_OK: ...省略
            case HttpURLConnection.HTTP_NOT_FOUND:
            case HttpURLConnection.HTTP_CONFLICT:
            case HttpURLConnection.HTTP_FORBIDDEN:
            default:
}

1.2,nacos的服务配置监听

在整个容器启动完成之后,就会去调用这个监听器。nacos主要是在这个NacosContextRefresher类下面来实现这个监听,其实现了这个ApplicationListener这个接口,就是一个nacos的一个上下文的一个刷新流。构造方法如下

public NacosContextRefresher(NacosRefreshProperties refreshProperties,NacosRefreshHistory refreshHistory, ConfigService configService) {
    /刷新配置文件
  this.refreshProperties = refreshProperties;
    //刷新历史文件
  this.refreshHistory = refreshHistory;
  this.configService = configService;
}

在这个类里面,会调用一个onApplicationEvent的事件方法,里面就会去进行一个nacos的监听的一个注册。

@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
  // many Spring context
  if (this.ready.compareAndSet(false, true)) {
        //nacos的监听注册
    this.registerNacosListenersForApplications();
  }
}

其注册的nacos的监听器的具体方法如下,就是会去获取nacos的全部的配置文件,然后在获取id之后,通过这个id对这个服务进行一个监听。

private void registerNacosListenersForApplications() {
  if (refreshProperties.isEnabled()) {
    for (NacosPropertySource nacosPropertySource : NacosPropertySourceRepository
        .getAll()) {
            //获取id
      String dataId = nacosPropertySource.getDataId();
      registerNacosListener(nacosPropertySource.getGroup(), dataId);
    }
  }
}

其监听这个nacos的主要方法registerNacosListener的具体实现如下,当配置发生变化的时候,这个监听方法就会发起一个调用,就会对立面的这个配置进行一个更新和替换。每一次更新都会有一个历史版本,

private void registerNacosListener(final String group, final String dataId){
    Listener listener = listenerMap.computeIfAbsent(dataId, i -> new Listener() {
        //当配置发生变化的时候,这个监听方法就会发起一个调用
    @Override
    public void receiveConfigInfo(String configInfo) {
            //记录这个历史版本
            refreshHistory.add(dataId, md5);
            //发布这个监听事件
            applicationContext.publishEvent(
            new RefreshEvent(this, null, "Refresh Nacos config"));
        }
}

最后会去调用一个refresh方法,会进行一个环境的刷新,会将新的参数和原来的参数进行一个比较,通过发布这个环境变更事件,对做出改变的值进行一个更新操作。

public synchronized Set<String> refresh() {
  Set<String> keys = refreshEnvironment();
  this.scope.refreshAll();
  return keys;
}

如果感知到这个对应的配置有改变的操作之后,就会清除当前的配置实例,会将新的实例重新通过这个bean工厂进行一个重新getBean的一个操作。

1.3,客户端总结

就是在这个客户端进行启动的时候,就会优先拉取本地的配置,如果本地配置不存在,那么就会和这个服务端建立这个http请求,然后去拉取这个服务端的全部配置,就是配置中心的全部配置。在拉取到全部配置之后,会去获取每一个配置文件的dataId,然后通过这个id对服务端的每一个配置文件进行一个监听的操作。每当服务端这边的配置文件出现修改的时候,就可以通过这个监听器进行到一个感知,然后这个客户端也会对对应的配置文件进行修改,每一份修改的配置都会存储在这个nacos配置文件里面,会作为一个历史文件保留。


2,nacos配置中心服务端详解

2.1,服务端获取全部配置

这个主要是在这个ConfigController这个类下面,在服务端的nacos-config的模块下面。

里面有一个重要的方法,就是getConfig的这个方法,

@GetMapping
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
public void getConfig(HttpServletRequest request, HttpServletResponse response,
        @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)
        throws IOException, ServletException, NacosException {
    // check tenant
    ParamUtils.checkTenant(tenant);
    tenant = NamespaceUtil.processNamespaceParameter(tenant);
    // check params
    ParamUtils.checkParam(dataId, group, "datumId", "content");
    ParamUtils.checkParam(tag);
    final String clientIp = RequestUtil.getRemoteIp(request);
    //这里的方法就是记性具体的获取配置信息
    inner.doGetConfig(request, response, dataId, group, tenant, tag, clientIp);
}

接下来就想看看这个doGetConfig方法。主要是从这个本地文件读取这个配置,而不是读取这个数据库的配置。这个文件主要存储在这个nacos的data的文件目录下。

public String doGetConfig(HttpServletRequest request, HttpServletResponse response, String dataId, String group, String tenant, String tag, String clientIp) throws IOException, ServletException{
    File file = null;
    //md5对这些文件里面的数据进行一个加密的操作
    md5 = cacheItem.getMd54Beta();
    //从磁盘里面获取这个文件的信息
    file = DiskUtil.targetBetaFile(dataId, group, tenant);
}

2.2,服务端将配置存储磁盘

主要在这个DumpService抽象类下面,有从内存中将全部配置文件存入到这个磁盘里面。

通过快捷键ctrl + alt + b可以查看这个抽象类下面的全部实现,主要有如下两个类,分别是EmbeddedDumpService和这个ExternalDumpService类。

c4e32da7f294431eae5881c42dae43bb.png

然后这个实现类里面会有一个初始化方法,会通过这个bean的前置处理器去初始化这个实例。然后通过这个dumpOperate方法来实现这个具体的配置文件的存储。

@PostConstruct
@Override
protected void init() throws Throwable {
    //存储这个配置文件
    dumpOperate(processor, dumpAllProcessor, dumpAllBetaProcessor, dumpAllTagProcessor);
}

在这个dumpOperate方法里面,来实现这个存储的具体实现。在这里面有大量的代码,其主要是一些全量加载和一些增量加载。

protected void dumpOperate(){
    TimerContext.start(dumpFileContext);
    try{
        Runnable dumpAll = () -> dumpAllTaskMgr.addTask(DumpAllTask.TASK_ID, new DumpAllTask());
        Runnable dumpAllBeta = () -> dumpAllTaskMgr.addTask(DumpAllBetaTask.TASK_ID, new DumpAllBetaTask());
        Runnable dumpAllTag = () -> dumpAllTaskMgr.addTask(DumpAllTagTask.TASK_ID, new DumpAllTagTask());
    }
    Runnable clearConfigHistory = () -> {
         LOGGER.warn("clearConfigHistory start");
         if (canExecute()) {
               try {
                   Timestamp startTime = getBeforeStamp(TimeUtils.getCurrentTime(), 24 * getRetentionDays());
                   //用于分页,每次获取磁盘里面的1000行数据
                   int totalCount = persistService.findConfigHistoryCountByTime(startTime);
                   if (totalCount > 0) {
                       int pageSize = 1000;
                       int removeTime = (totalCount + pageSize - 1) / pageSize;
                       while (removeTime > 0) {
                           persistService.removeConfigHistory(startTime, pageSize);
                           removeTime--;
                      }
                  }
              } catch (Throwable e) {     
           }
       }
    };
  //加载配置信息
    try {
        //判断是增量获取还是全量获取,主要是通过这个时间是否大于6小时
        dumpConfigInfo(dumpAllProcessor);
    }           
}

服务端总结

就是每个配置文件在注册之后,都会现存在这个mysql里面,最后会将这个mysql里面的数据存入到磁盘里面,在客户端来拉取这个配置信息的时候,就会直接去读这个本地磁盘里面的数据。

相关文章
|
6月前
|
存储 Kubernetes 安全
Nacos-Controller 2.0:使用 Nacos 高效管理你的 K8s 配置
无论是使用 Nacos-Controller 实现配置的双向同步,还是直接在应用中接入 Nacos SDK 以获得更高级的配置管理特性,都能显著提升配置管理的灵活性、安全性和可维护性。使用 Nacos,您能够更好地管理和优化您的应用配置,从而提高系统的稳定性和可靠性。
512 49
|
10月前
|
存储 网络协议 Nacos
高效搭建Nacos:实现微服务的服务注册与配置中心
Nacos(Dynamic Naming and Configuration Service)是阿里巴巴开源的一款动态服务发现、配置管理和服务管理平台。它旨在帮助开发者更轻松地构建、部署和管理分布式系统,特别是在微服务架构中。
1698 82
高效搭建Nacos:实现微服务的服务注册与配置中心
|
10月前
|
JSON Java Nacos
SpringCloud 应用 Nacos 配置中心注解
在 Spring Cloud 应用中可以非常低成本地集成 Nacos 实现配置动态刷新,在应用程序代码中通过 Spring 官方的注解 @Value 和 @ConfigurationProperties,引用 Spring enviroment 上下文中的属性值,这种用法的最大优点是无代码层面侵入性,但也存在诸多限制,为了解决问题,提升应用接入 Nacos 配置中心的易用性,Spring Cloud Alibaba 发布一套全新的 Nacos 配置中心的注解。
919 150
|
6月前
|
存储 人工智能 测试技术
Nacos托管LangChain应用Prompts和配置,助力你的AI助手快速进化
AI 应用开发中,总有一些让人头疼的问题:敏感信息(比如 API-KEY)怎么安全存储?模型参数需要频繁调整怎么办?Prompt 模板改来改去,每次都得重启服务,太麻烦了!别急,今天我们就来聊聊如何用 Nacos 解决这些问题。
|
11月前
|
监控 Java 测试技术
Nacos 配置中心变更利器:自定义标签灰度
本文是对 MSE Nacos 应用自定义标签灰度的功能介绍,欢迎大家升级版本进行试用。
1029 215
|
8月前
|
Cloud Native Java Nacos
springcloud/springboot集成NACOS 做注册和配置中心以及nacos源码分析
通过本文,我们详细介绍了如何在 Spring Cloud 和 Spring Boot 中集成 Nacos 进行服务注册和配置管理,并对 Nacos 的源码进行了初步分析。Nacos 作为一个强大的服务注册和配置管理平台,为微服务架构提供
3069 14
|
11月前
|
Java 网络安全 Nacos
Nacos作为流行的微服务注册与配置中心,其稳定性与易用性广受好评
Nacos作为流行的微服务注册与配置中心,其稳定性与易用性广受好评。然而,“客户端不发送心跳检测”是使用中常见的问题之一。本文详细探讨了该问题的原因及解决方法,包括检查客户端配置、网络连接、日志、版本兼容性、心跳检测策略、服务实例注册状态、重启应用及环境变量等步骤,旨在帮助开发者快速定位并解决问题,确保服务正常运行。
179 5
|
11月前
|
网络安全 Nacos 开发者
Nacos作为流行的微服务注册与配置中心,“节点提示暂时不可用”是常见的问题之一
Nacos作为流行的微服务注册与配置中心,其稳定性和易用性备受青睐。然而,“节点提示暂时不可用”是常见的问题之一。本文将探讨该问题的原因及解决方案,帮助开发者快速定位并解决问题,确保服务的正常运行。通过检查服务实例状态、网络连接、Nacos配置、调整健康检查策略等步骤,可以有效解决这一问题。
239 4
|
11月前
|
Java 网络安全 Nacos
Nacos作为流行的微服务注册与配置中心,其稳定性和易用性备受青睐。
Nacos作为流行的微服务注册与配置中心,其稳定性和易用性备受青睐。然而,实际使用中常遇到“客户端不发送心跳检测”的问题。本文深入探讨该问题的原因及解决方案,帮助开发者快速定位并解决问题,确保服务正常运行。通过检查客户端配置、网络连接、日志、版本兼容性、心跳策略、注册状态、重启应用和环境变量等步骤,系统地排查和解决这一问题。
204 3
|
11月前
|
安全 Nacos 数据库
Nacos是一款流行的微服务注册与配置中心,但直接暴露在公网中可能导致非法访问和数据库篡改
Nacos是一款流行的微服务注册与配置中心,但直接暴露在公网中可能导致非法访问和数据库篡改。本文详细探讨了这一问题的原因及解决方案,包括限制公网访问、使用HTTPS、强化数据库安全、启用访问控制、监控和审计等步骤,帮助开发者确保服务的安全运行。
526 3

热门文章

最新文章