Nacos充当配置中心

什么是 Nacos

概览

Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。

Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。

简单的来说Nacos是一个动态服务发现、服务配置、服务元数据及流量管理的组件。目前已经更新到1.1.0版本

基本上Nacos = Spring Cloud Eureka + Spring Cloud Config

Nacos 可以与 Spring, Spring Boot, Spring Cloud 集成,甚至比 Spring Cloud Eureka, Spring Cloud Config更加简单和强大。

  • 通过 Nacos Server 和 spring-cloud-starter-alibaba-nacos-config 实现配置的动态变更。
  • 通过 Nacos Server 和 spring-cloud-starter-alibaba-nacos-discovery 实现服务的注册与发现。

动态配置服务

现在简单的入门一下Nacos的动态配置服务,Nacos可以和spring boot无缝接入。我们以一个spring boot的例子来简单说明使用方法。

1.安建Nacos环境

环境要求

  • 64 bit OS,支持 Linux/Unix/Mac/Windows,推荐选用 Linux/Unix/Mac。
  • 64 bit JDK 1.8+

这里我们使用linux系统,jdk使用1.8版本来安装Nacos

  1. 下载Nacos安装包,发布版本下载地址:https://github.com/alibaba/nacos/releases
    image
    2.上传服务器或者虚拟机
    image
    3.解压文件
    image
    4.启动nacos(启动的是单机版本),进入bin文件夹 cd nacos/bin
    image
    5.验证是否启动成功
    在浏览器输入http://(你的ip):8848/nacos
    image
    默认的账号密码是nacos/nacos,如果发现访问不到先查看是否防火墙阻止访问8848端口。

2.创建spring boot项目

创建项目过程如果不会可以查看我这篇博客

3.创建nacos测试项目

  1. 基本的注解注入配置信息
    创建成功后在pom引入nacos的依赖,我们现在要使用的配置中心依赖:
        <dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>nacos-config-spring-boot-starter</artifactId>
            <version>0.2.1</version>
        </dependency>

yml配置代码

server:
  port: 8000
nacos:
  server-addr: 192.168.253.128:8848  #自定义注解
#配置中心
  config:
    server-addr: 192.168.253.128:8848
#服务名称
spring:
  application:
    name: example-service

path: /login  #自定义注解

user:
  name: test  #自定义注解
  1. 配置注册

这里我们采用更贴合spring boot的注解注入
有时候我们服务端要知道什么时候配置被修改了,什么时候配置被添加移除等操作监听,nacos也帮我们实现了这些操作。
** 采用注解注入形式@NacosInjected注入可以触发nacosConfigPublishedEvent回调,也就是配置注册到nacos的时候产生的回调,其余方式不会触发 **
先创建一个NacosConfiguration

import com.alibaba.nacos.api.annotation.NacosProperties;
import com.alibaba.nacos.spring.context.annotation.EnableNacos;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableNacos(
        globalProperties =
        @NacosProperties(serverAddr = "${nacos.server-addr}")
)
public class NacosConfiguration {

}

再建立一个ConfigController帮助我们实时的去注册,注销,获取配置等操作

import com.alibaba.nacos.api.annotation.NacosInjected;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.annotation.NacosValue;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP;
import static com.xiongcp.test.main.config.NacosPropertySourceConfiguration.EXAMPLE;

/**
 * @author xiongcp
 * @description:
 * @date :2019/7/12 15:50
 */
@RestController
@RequestMapping("config")
public class ConfigController {

    @NacosValue(value = "${useLocalCache:false}", autoRefreshed = true)
    private boolean useLocalCache;
 
    //采用注解注入形式@NacosInjected注入可以触发nacosConfigPublishedEvent回调,也就是配置注册到nacos的时候产生的回调,其余方式不会触发
    @NacosInjected
    private ConfigService configService;

    @GetMapping(value = "/get")
    public boolean get() {
        return useLocalCache;
    }

    @GetMapping(value = "/register")
    public boolean register() {
        try {
            configService.publishConfig(EXAMPLE, DEFAULT_GROUP, "useLocalCache = true");
            return true;
        } catch (NacosException e) {
            e.printStackTrace();
            return false;
        }
    }

    @GetMapping(value = "/remove")
    public boolean remove() {
        try {
            configService.removeConfig(EXAMPLE, DEFAULT_GROUP);
            return true;
        } catch (NacosException e) {
            e.printStackTrace();
            return false;
        }
    }
}

上面是对配置进行注册,注销,获取等操作
再编写监控状态变化的配置类NacosEventListenerConfiguration

import com.alibaba.nacos.api.annotation.NacosInjected;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.AbstractListener;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.spring.context.event.config.NacosConfigListenerRegisteredEvent;
import com.alibaba.nacos.spring.context.event.config.NacosConfigPublishedEvent;
import com.alibaba.nacos.spring.context.event.config.NacosConfigReceivedEvent;
import com.alibaba.nacos.spring.context.event.config.NacosConfigRemovedEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP;
import static com.xiongcp.test.main.config.NacosPropertySourceConfiguration.*;

@Configuration
public class NacosEventListenerConfiguration {
    private static final Logger logger = LoggerFactory.getLogger(NacosEventListenerConfiguration.class);


    @NacosInjected
    private ConfigService configService;


    @PostConstruct
    public void init() throws NacosException {

        Listener listener = new AbstractListener() {
            @Override
            public void receiveConfigInfo(String configInfo) {
            }
        };

        // for NacosConfigListenerRegisteredEvent(true)
        //
        configService.addListener(EXAMPLE, DEFAULT_GROUP, listener);
        /*configService.addListener(FIRST_DATA_ID, DEFAULT_GROUP, listener);
        configService.addListener(BEFORE_OS_ENV_DATA_ID, DEFAULT_GROUP, listener);
        configService.addListener(AFTER_SYS_PROP_DATA_ID, DEFAULT_GROUP, listener);*/

        // for NacosConfigListenerRegisteredEvent(false)
        //configService.removeListener("example", DEFAULT_GROUP, listener);
    }

    
    @Bean
    public ApplicationListener<NacosConfigReceivedEvent> nacosConfigReceivedEventListener() {
        return new ApplicationListener<NacosConfigReceivedEvent>() {
            @Override
            public void onApplicationEvent(NacosConfigReceivedEvent event) {
                logger.info("Listening on NacosConfigReceivedEvent -  dataId : {} , groupId : {} , " + "content : {} , "
                        + "source : {}", event.getDataId(), event.getGroupId(), event.getContent(), event.getSource());
            }
        };
    }

    @Bean
    public ApplicationListener<NacosConfigRemovedEvent> nacosConfigRemovedEventListener() {
        return new ApplicationListener<NacosConfigRemovedEvent>() {
            @Override
            public void onApplicationEvent(NacosConfigRemovedEvent event) {
                logger.info("Listening on NacosConfigRemovedEvent -  dataId : {} , groupId : {} , " + "removed : {} , "
                        + "source : {}", event.getDataId(), event.getGroupId(), event.isRemoved(), event.getSource());
            }
        };
    }

    @Bean
    public ApplicationListener<NacosConfigListenerRegisteredEvent> nacosConfigListenerRegisteredEventListener() {
        return new ApplicationListener<NacosConfigListenerRegisteredEvent>() {
            @Override
            public void onApplicationEvent(NacosConfigListenerRegisteredEvent event) {
                logger.info("Listening on NacosConfigListenerRegisteredEvent -  dataId : {} , groupId : {} , " + "registered : {} , "
                        + "source : {}", event.getDataId(), event.getGroupId(), event.isRegistered(), event.getSource());
            }
        };
    }

    @Bean
    public ApplicationListener<NacosConfigPublishedEvent> nacosConfigPublishedEvent() {
        return new ApplicationListener<NacosConfigPublishedEvent>() {
            @Override
            public void onApplicationEvent(NacosConfigPublishedEvent event) {
                logger.info("Listening on NacosConfigPublishedEvent -  dataId : {} , groupId : {} , " + "published : {} , "
                        + "source : {}", event.getDataId(), event.getGroupId(), event.isPublished(), event.getSource());
            }
        };
    }
}

这样我们启动项目之后可以通过nacos后台修改配置的值,通过接口来动态的配置变量的值,更方便我们进行测试。

首先启动项目,会发现这一条监听消息
image
进入nacos后台发现没有配置信息
image
这个时候我们用postman或者直接使用浏览器
image
返回true表示注册成功
看一下nscos控制台
image
已经注册成功了
再看一下程序里面的回调信息的输出
image
发现注册成功,也有默认值的输出

接下来修改配置的值,这里直接通过nacos的后台进行配置
点击编辑
image
修改值为false
image
点击发布
image
确认发布
完成之后
看项目的回调输出,发现值被修改了image
我们通过写的接口查询是否修改成功,利用postman或者浏览器访问
image
发现值已经被修改了,操作成功。更新速度很快。

基本可以实时的更改项目里面的配置值

如果需要从nacos移除配置,也是可以实现的
postman访问remove接口
image

这样就能移除配置了
移除配置之后也会收到回调的信息
image

同时nacos控制台也会清空该配置
image

但是要注意的是已经配置的信息并不会更改,也就是说true修改为false不会因为移除了而更改为true

  1. 有时候需要将配置值做个性化的适配,nacos也是提供了first,before,after等注解配置,验证如下。
    创建config包,创建NacosPropertySourceConfiguration配置类,里面的代码是从阿里demo拷贝修改了一下移植过来的。
    NacosPropertySourceConfiguration.java
@Configuration
@NacosPropertySource(name = "first", dataId = FIRST_DATA_ID, first = true, autoRefreshed = true)
//dataId类似于key-values的key,用唯一的dataId可以去注册一个唯一的config信息
@NacosPropertySources({
        @NacosPropertySource(name = "before-os-env", dataId = BEFORE_OS_ENV_DATA_ID, before = SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME),
        @NacosPropertySource(name = "after-system-properties", dataId = AFTER_SYS_PROP_DATA_ID, after = SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)
})
public class NacosPropertySourceConfiguration {



    private static final Logger logger = LoggerFactory.getLogger(NacosPropertySourceConfiguration.class);

    public static final String FIRST_DATA_ID = "first-property-source-data-id";

    public static final String BEFORE_OS_ENV_DATA_ID = "before-os-env-property-source-data-id";

    public static final String AFTER_SYS_PROP_DATA_ID = "after-system-properties-property-source-data-id";

    public static final String EXAMPLE = "example";


    static {
        String serverAddr = "192.168.253.128:8848";
        try {
            //创建Nacos的ConfigService
            ConfigService configService = NacosFactory.createConfigService(serverAddr);
            //发布到Nacos
            // Publish for FIRST_DATA_ID
            publishConfig(configService, FIRST_DATA_ID, "user.name = Mercy Ma");

            // Publish for BEFORE_OS_ENV_DATA_ID
            publishConfig(configService, BEFORE_OS_ENV_DATA_ID, "path = /home/my-path");


            // Publish for AFTER_SYS_PROP_DATA_ID
            publishConfig(configService, AFTER_SYS_PROP_DATA_ID, "user.name = mercyblitz");

        } catch (NacosException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 注册配置
     * @param configService
     * @param dataId
     * @param propertiesContent
     * @throws NacosException
     */
    private static void publishConfig(ConfigService configService, String dataId, String propertiesContent) throws NacosException {
        configService.publishConfig(dataId, DEFAULT_GROUP, propertiesContent);
    }

    /**
     * "before-os-env" overrides OS Environment variables $PATH
     * @Value() 读取的yml的配置文件的值
     */
    @Value("${path}")
    private String path;

    /**
     * There are three definitions of "user.name" from
     * FIRST_DATA_ID,
     * SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME,
     * AFTER_SYS_PROP_DATA_ID
     * <p>
     * Thus, "user.name = Mercy Ma" will be loaded from FIRST_DATA_ID, others will be ignored.
     */
    @Value("${user.name}")
    private String userName;

    @PostConstruct
    public void init() {
        logger.info("${path} : {}", path); // -> "home/my-path"
        logger.info("${user.name} : {}", userName); // -> "Mercy Ma"
        logger.info("Java System ${user.name} : {}", System.getProperty("user.name"));
        logger.info("OS Env ${PATH} : {}", System.getenv("PATH"));
    }
}

启动spring boot项目,我们查看log文件
image
我们可以发现init方法里面读取到的path被覆盖了,并不是yml文件里面的值,配置被修改了。这个也就是注解里面before = SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME的作用

user.name输出的是Mercy Ma而不是mercyblitz是因为dataId为FIRST_DATA_ID的位置里面配置了first = true,那么就只会加载第一次的配置,忽略后面的配置。

autoRefreshed = true 表示自动刷新配置,在nacos后台修改配置后会自动更新,默认是false
我们可以查看下nacos的后台配置文件数据
image

可以发现我们注册进去的几个配置信息,在里面可以进行查看修改等操作。

总结:nacos配置中心友好方便,功能齐全,后续会对nacos的其他功能进行说明。
项目github地址:https://github.com/xiongcp/NacosTest


版权声明:本文为qq_32532321原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。