一块儿学习nacos配置中心

1、介绍

nacos是阿里18年开源的做为配置中心及服务发现的中间件,本文主要讨论其做为配置中心的一些功能及实现。 下图描述了spring cloud config Appollo Nacos三个配置中心的一些特性,我的比较倾向于nacos,由于nacos 部署、使用特别方便,跟spring整个生态无缝结合。以前使用过百度公司的disconf做为配置中心,可是使用起来不是很方便,代码陈旧复杂,何况百度公司也不作任何维护了,因此选择了放弃,进而拥抱nacos。      java

2、nacos架构图:

3、配置

在系统开发过程当中,开发者一般会将一些须要变动的参数、变量等从代码中分离出来独立管理,以独立的配置文件的形式存在。目的是让静态的系统工件或者交付物(如 WAR,JAR 包等)更好地和实际的物理运行环境进行适配。配置管理通常包含在系统部署的过程当中,由系统管理员或者运维人员完成。配置变动是调整系统运行时的行为的有效手段。web

配置管理spring

系统配置的编辑、存储、分发、变动管理、历史版本管理、变动审计等全部与配置相关的活动。数据库

配置项编程

一个具体的可配置的参数与其值域,一般以 param-key=param-value 的形式存在。例如咱们常配置系统的日志输出级别(logLevel=INFO|WARN|ERROR) 就是一个配置项。bootstrap

配置集缓存

一组相关或者不相关的配置项的集合称为配置集。在系统中,一个配置文件一般就是一个配置集,包含了系统各个方面的配置。例如,一个配置集可能包含了数据源、线程池、日志级别等配置项。bash

配置集 ID网络

Nacos 中的某个配置集的 ID。配置集 ID 是组织划分配置的维度之一。Data ID 一般用于组织划分系统的配置集。一个系统或者应用能够包含多个配置集,每一个配置集均可以被一个有意义的名称标识。Data ID 一般采用类 Java 包(如 com.taobao.tc.refund.log.level)的命名规则保证全局惟一性。此命名规则非强制。架构

配置分组

Nacos 中的一组配置集,是组织配置的维度之一。经过一个有意义的字符串(如 Buy 或 Trade )对配置集进行分组,从而区分 Data ID 相同的配置集。当您在 Nacos 上建立一个配置时,若是未填写配置分组的名称,则配置分组的名称默认采用 DEFAULT_GROUP 。配置分组的常见场景:不一样的应用或组件使用了相同的配置类型,如 database_url 配置和 MQ_topic 配置。

配置快照

Nacos 的客户端 SDK 会在本地生成配置的快照。当客户端没法链接到 Nacos Server 时,可使用配置快照显示系统的总体容灾能力。配置快照相似于 Git 中的本地 commit,也相似于缓存,会在适当的时机更新,可是并无缓存过时(expiration)的概念。

4、使用

1.java项目

主要采用nacos提供的SDK,采用nacos-client 1.0.0版本

maven坐标

<dependency>

          <groupId>com.alibaba.nacos</groupId>

          <artifactId>nacos-client</artifactId>

          <version>1.0.0</version>

 </dependency>
复制代码

获取配置

描述 用于服务启动的时候从 Nacos 获取配置

public String getConfig(String dataId, String group, long timeoutMs) throws NacosException
复制代码

dataId string 配置 ID

group string 配置分组

timeout long 读取配置超时时间,单位ms,推荐值3000

返回值

string 配置值

请求示例

try {
    String serverAddr = "{serverAddr}";
    String dataId = "{dataId}";
    String group = "{group}";
    Properties properties = new Properties();
    properties.put("serverAddr", serverAddr);
    ConfigService configService = NacosFactory.createConfigService(properties);
    String content = configService.getConfig(dataId, group, 5000);
    System.out.println(content);
} catch (NacosException e) {
    // TODO Auto-generated catch block
 e.printStackTrace();
}
复制代码

异常说明 读取配置超时或网络异常,抛出 NacosException 异常。

监听配置 描述 若是但愿 Nacos 推送配置变动,可使用 Nacos 动态监听配置接口来实现。

public void addListener(String dataId, String group, Listener listener) 
复制代码

请求参数

dataId string 配置 ID

group string 配置分组

listener Listener 监听器,配置变动进入监听器的回调函数。

返回值

string 配置值,初始化或者配置变动的时候经过回调函数返回该值。

请求示例

String serverAddr = "{serverAddr}";
    String dataId = "{dataId}";
    String group = "{group}";
    Properties properties = new Properties();
properties.put("serverAddr", serverAddr);
    ConfigService configService = NacosFactory.createConfigService(properties);
    String content = configService.getConfig(dataId, group, 5000);
System.out.println(content);
configService.addListener(dataId, group, new Listener() {
        @Override
 public void receiveConfigInfo(String configInfo) {
            System.out.println("recieve1:" + configInfo);
        }
        @Override
 public Executor getExecutor() {
            return null;
        }
    });
复制代码

2.spring web项目

maven坐标

<dependency>

        <groupId>com.alibaba.nacos</groupId>

       <artifactId>nacos-spring-context</artifactId>

       <version>${latest.version}</version>

 </dependency>
复制代码

最新版本能够在 maven 仓库,地址:mvnrepository.com/artifact/co…

配置管理服务

添加 @EnableNacosConfig 注解启用 Nacos Spring 的配置管理服务。

如下示例中,咱们使用 @NacosPropertySource 加载了 dataId 为 example 的配置源,并开启自动更新:

/**
 * 添加 @EnableNacosConfig 注解启用 Nacos Spring 的配置管理服务
 */
@Configuration
@EnableNacosConfig(globalProperties = @NacosProperties(serverAddr = "192.168.10.105:8848"))

/**
 * @NacosPropertySource 加载了 dataId 为 example 的配置源
 *
 * <p>
 * Nacos 控制台添加配置:
 * <p>
 * Data ID:example
 * <p>
 * Group:DEFAULT_GROUP
 * <p>
 * 配置内容:useLocalCache=true
 */
@NacosPropertySource(dataId = "example", autoRefreshed = true)
public class NacosConfiguration {

}
复制代码

获取属性值

经过 Nacos 的 @NacosValue 注解设置属性值。

/**
 * 经过 Nacos 的 @NacosValue 注解设置属性值
 *
 */
@Controller
@RequestMapping("config")
public class ConfigController {

    @NacosInjected
 private ConfigService configService;

    @NacosValue(value = "${useLocalCache:false}", autoRefreshed = true)
    private String useLocalCache;

    @RequestMapping(value = "/get", method = GET)
    @ResponseBody
 public String get() {
        return useLocalCache;
    }
 }
复制代码

测试

启动 Tomcat,访问http://localhost:8080/config/get尝试获取配置信息。

因为此时还未发布过配置,因此返回内容是 false。

在配置管理页面修改dataId 为example,内容为useLocalCache=true

再次访问 http://localhost:8080/config/get

此时返回内容为true,说明程序中的useLocalCache值已经被动态更新了。

3.springBoot项目

maven坐标

<dependency>

       <groupId>com.alibaba.boot</groupId>

       <artifactId>nacos-config-spring-boot-starter</artifactId>

       <version>${latest.version}</version>

 </dependency>
复制代码

注意:版本 0.2.x.RELEASE 对应的是 Spring Boot 2.x 版本,版本 0.1.x.RELEASE 对应的是 Spring Boot 1.x 版本。

配置管理服务 在 application.properties 中配置 Nacos server 的地址:

nacos.config.server-addr=192.168.10.105:8848

使用 @NacosPropertySource 加载 dataId 为 example 的配置源,并开启自动更新:

@SpringBootApplication
@NacosPropertySource(dataId = "example", autoRefreshed = true)
public class NacosConfigApplication {

    public static void main(String[] args) {
        SpringApplication.run(NacosConfigApplication.class, args);
    }
}
 
复制代码

获取属性值 经过 Nacos 的 @NacosValue 注解设置属性值

@Controller
@RequestMapping("config")
public class ConfigController {

    @NacosValue(value = "${useLocalCache:false}", autoRefreshed = true)
    private boolean useLocalCache;

    @RequestMapping(value = "/get", method = GET)
    @ResponseBody
 public boolean get() {
        return useLocalCache;
    }
}
复制代码

测试

启动 NacosConfigApplication,访问 http://localhost:8080/config/get

返回内容是 false。

在配置管理页面修改dataId 为example,内容为useLocalCache=true

再次访问 http://localhost:8080/config/get

此时返回内容为true,说明程序中的useLocalCache值已经被动态更新了。

4.spring cloud:

引入依赖:

依赖的版本0.1.x对应spring boot 1.x,0.2.x对应spring boot 2.x,而且log4j2日志的版本须要在2.7.0以上,推荐2.10.0版本

<dependency>  
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    <version>0.1.1.RELEASE</version>
</dependency>  
复制代码

增长配置:

spring.cloud.nacos.config.context-path=nacos 
spring.cloud.nacos.config.shared-dataids=common.properties 
spring.cloud.nacos.config.refreshable-dataids=common.properties 
spring.cloud.nacos.config.ext-config[0].dataId=ext.properties 
spring.cloud.nacos.config.ext-config[0].refresh=true 
spring.cloud.nacos.config.namespace=af0a428a-ff43-4971-8b3d-4eaf4ffcfbc9 
spring.cloud.nacos.config.prefix=${spring.application.name} 
spring.cloud.nacos.config.file-extension=properties 
spring.cloud.nacos.config.server-addr=127.0.0.1:8848 
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 
复制代码

如上配置须要添加到bootstrap配置文件才可生效,可定义shared-dataids获取一些公共配置,使用ext-config获取额外配置,经过prefix和file-extension肯定dataId,以上配置的namespace会做用于全部shared、 ext和应用的dataId,不像spring boot方式,不一样的NacosPropertySource注解中的namespace会分别生效

spring bean增长@RefreshScope,经过@Value(value = "${xx}") 注入配置,该方式能够达到实时刷新效果,spring cloud经过监听变动会销毁bean,下次使用生成bean时从新注入属性达到刷新效果。

页面管理配置

登陆管理 Nacos1.0.1 版本支持简单登陆功能,默认用户名/密码为: nacos/nacos。

命名空间管理 Nacos 基于Namespace 帮助用户逻辑隔离多个命名空间,这能够帮助用户更好的管理测试、预发、生产等多环境服务和配置,

让每一个环境的同一个配置(如数据库数据源)能够定义不一样的值。

配置管理 Nacos支持基于Namespace和Group的配置分组管理,以便用户更灵活的根据本身的须要按照环境或者应用、

模块等分组管理微服务以及Spring的大量配置,在配置管理中主要提供了配置历史版本、回滚、订阅者查询等核心管理能力。

多配置格式编辑器

Nacos支持 YAML、Properties、TEXT、JSON、XML、HTML 等常见配置格式在线编辑、语法高亮、格式校验,帮助用户高效编辑的同时大幅下降格式错误带来的风险。

Nacos支持配置标签的能力,帮助用户更好、更灵活的作到基于标签的配置分类及管理。同时支持用户对配置及其变动进行描述,方面多人或者跨团队协做管理配置。

编辑DIFF Nacos支持编辑DIFF能力,帮助用户校验修改内容,下降改错带来的风险。

示例代码 Nacos提供示例代码能力,可以让新手快速使用客户端编程消费该配置,大幅下降新手使用门槛。

监听者查询 Nacos提供配置订阅者即监听者查询能力,同时提供客户端当前配置的MD5校验值,以便帮助用户更好的检查配置变动是否推送到 Client 端。

配置的版本及一键回滚 Nacos经过提供配置版本管理及其一键回滚能力,帮助用户改错配置的时候可以快速恢复,下降微服务系统在配置管理上的必定会遇到的可用性风险。

5、nacos简单实现介绍

client拉取配置和感知配置变动的核心接口是ConfigService,定义了发布、删除、获取配置,添加删除监听器、获取server状态这些接口

String getConfig(String dataId, String group, long timeoutMs) throws NacosException;

void addListener(String dataId, String group, Listener listener) throws NacosException;

boolean publishConfig(String dataId, String group, String content) throws NacosException;

boolean removeConfig(String dataId, String group) throws NacosException;

void removeListener(String dataId, String group, Listener listener);

String getServerStatus();  
复制代码

实现类NacosConfigService对配置的增删改查操做经过一个HttpAgent来访问server暴露的http服务来实现,NacosConfigService内部经过ClientWorker类来对不一样http请求获取的配置进行check刷新

final ScheduledExecutorService executor;  
final ExecutorService executorService;  
/**
 * groupKey -> cacheData
 */
AtomicReference<Map<String, CacheData>> cacheMap = new AtomicReference<Map<String, CacheData>>(  
    new HashMap<String, CacheData>());
复制代码

ClientWorker内部的核心成员如上,cacheMap 维护了不一样groupKey(经过dataId,group,namespace组成)到CacheData(配置数据)映射,每次注册监听器以后把对应配置信息放入这个map,以后经过executorService每10ms提交一个LongPollingRunnable的线程

executor.scheduleWithFixedDelay(new Runnable() {  
    public void run() {
        try {
            checkConfigInfo();
        } catch (Throwable e) {
            LOGGER.error("[" + agent.getName() + "] [sub-check] rotate check error", e);
        }
    }
}, 1L, 10L, TimeUnit.MILLISECONDS);
复制代码

checkConfigInfo方法以下

public void checkConfigInfo() {  
    // 分任务
    int listenerSize = cacheMap.get().size();
    // 向上取整为批数
    int longingTaskCount = (int)Math.ceil(listenerSize / ParamUtil.getPerTaskConfigSize());
    if (longingTaskCount > currentLongingTaskCount) {
        for (int i = (int)currentLongingTaskCount; i < longingTaskCount; i++) {
            // 要判断任务是否在执行 这块须要好好想一想。 任务列表如今是无序的。变化过程可能有问题
            executorService.execute(new LongPollingRunnable(i));
        }
        currentLongingTaskCount = longingTaskCount;
    }
}
复制代码

经过executorService提交一个长轮询检查线程,代码比较长就不贴了,流程就是对每一个CacheData(配置数据),检查配置当前版本跟本地配置文件最后变动时间是否一致肯定配置是否刷新,刷新则通知监听器作对应处理,而后使用http请求(默认超时30s)server端这个地址/v1/cs/config/listener(长轮询),server端这个地址会保持链接,若是配置没有变动,请求会在停留30s后返回,若是有配置更变,请求会立刻返回,client接收到配置变动,会更新本地配置文件而且更新版本号。

server端经过LongPollingService来完成保持链接的能力,核心成员以下:

final ScheduledExecutorService scheduler;

/**
 * 长轮询订阅关系
 */
final Queue<ClientLongPolling> allSubs;  
复制代码

ClientLongPolling是一个线程,经过scheduler默认30s定时处理请求,若是server端接收到配置变动请求,则经过发布LocalDataChangeEvent事件,LongPollingService继承监听器,触发监听执行DataChangeTask,内部经过allSubs维护的异步请求上下文AsyncContext直接返回response。

spring boot集成nacos经过NacosPropertySourcePostProcessor,该类实现BeanFactoryPostProcessor及EnvironmentAware,经过nacos client获取配置放入spring 上下文,经过是否刷新来决定是否注册监听。

spring cloud经过NacosPropertySourceLocator,在PropertySourceBootstrapConfiguration这个ApplicationContextInitializer里加载属性源,监听ApplicationReadyEvent事件后为属性源注册监听处理变动。

写在最后

server端本地可经过standalone模式启动,也可源码直接运行,方便调试,client使用也很方便,支持配置分组和命名空间的隔离,支持配置刷新回滚,配置灰发功能还有待开发,权限管理不足,整体使用仍是很是简单方便,spring 生态集成度很高,比较推荐使用。

相关文章
相关标签/搜索