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里面的数据存入到磁盘里面,在客户端来拉取这个配置信息的时候,就会直接去读这个本地磁盘里面的数据。

相关文章
|
3天前
|
Dubbo 关系型数据库 MySQL
nacos常见问题之命名空间配置数据上线修改如何解决
Nacos是阿里云开源的服务发现和配置管理平台,用于构建动态微服务应用架构;本汇总针对Nacos在实际应用中用户常遇到的问题进行了归纳和解答,旨在帮助开发者和运维人员高效解决使用Nacos时的各类疑难杂症。
104 1
|
3天前
|
存储 运维 监控
NACOS 配置中心和注册中心是分两个集群部署还是放在一个集群中
【2月更文挑战第33天】NACOS 配置中心和注册中心是分两个集群部署还是放在一个集群中
89 2
|
3天前
|
SpringCloudAlibaba 应用服务中间件 Nacos
【微服务 SpringCloudAlibaba】实用篇 · Nacos配置中心(下)
【微服务 SpringCloudAlibaba】实用篇 · Nacos配置中心
16 0
|
3天前
|
JSON SpringCloudAlibaba Java
【微服务 SpringCloudAlibaba】实用篇 · Nacos配置中心(上)
【微服务 SpringCloudAlibaba】实用篇 · Nacos配置中心
17 1
|
3天前
|
Nacos
nacos 配置页面的模糊查询
nacos 配置页面的模糊查询
|
3天前
|
机器学习/深度学习 Java Nacos
Nacos 配置中心(2023旧笔记)
Nacos 配置中心(2023旧笔记)
21 0
|
3天前
|
存储 前端开发 Java
第十一章 Spring Cloud Alibaba nacos配置中心
第十一章 Spring Cloud Alibaba nacos配置中心
29 0
|
3天前
|
敏捷开发 API 持续交付
云效产品使用常见问题之把云效上的配置发到Nacos上面去如何解决
云效作为一款全面覆盖研发全生命周期管理的云端效能平台,致力于帮助企业实现高效协同、敏捷研发和持续交付。本合集收集整理了用户在使用云效过程中遇到的常见问题,问题涉及项目创建与管理、需求规划与迭代、代码托管与版本控制、自动化测试、持续集成与发布等方面。
|
3天前
|
SpringCloudAlibaba Java Nacos
SpringCloud Alibaba微服务 -- Nacos使用以及注册中心和配置中心的应用(保姆级)
SpringCloud Alibaba微服务 -- Nacos使用以及注册中心和配置中心的应用(保姆级)
|
3天前
|
Nacos
nacos手动创建配置命名空间隔离
nacos手动创建配置命名空间隔离
25 1

热门文章

最新文章