应用程序在启动和运行的时候往往需要读取一些配置信息,配置基本上伴随着应用程序的整个生命周期,比如:数据库连接参数、启动参数等。
特点
配置对于程序是只读的,程序通过读取配置来改变自己的行为,但是程序不应该去改变配置
配置贯穿于应用的整个生命周期,应用在启动时通过读取配置来初始化,在运行时根据配置调整行为。比如:启动时需要读取服务的端口号、系统在运行过程中需要读取定时策略执行定时任务等。
常见的有程序内部hard code ,配置文件,环境变量,启动参数,基于数据库等
同一份程序在不同的环境(开发测试生产’)、不同的集群(如不同的数据中心)经常需要有不同的配置,所以需要有完善的环境、集群配置
在微服务架构中,当系统从一个单体应用,被拆分成分布式系统上一个个服务节点后,配置文件也必须跟着迁移(分割) , 这样配置就分散了,不仅如此,分散中还包含着冗余,如下图:
下图显示了配置中心的功能,配置中心将配置从各应用中剥离出来,对配置进行统一管理)应用自身不需要自己去管理配置。
Nacos是阿里的一个开源产品,它是针对微服务架构中的服务发现、配置管理、服务治理的综合型解决方案。
服务发现与服务健康检查
Nacos使服务更容易注册,并通过DNS或HTTP接口发现其他服务, Nacos还提供服务的实时健康检查,以防止向不健康的主机或服务实例发送请求。
动态配置管理
动态配置服务允许您在所有环境中以集中和动态的方式管理所有服务的配置。Nacos消除了在更新配置时重新部署应用程序,这使配置的更改更加高效和灵活。
动态DNS服务
Nacos提供基于DNS协议的服务发现能力,旨在支持异构语言的服务发现,支持将注册在Nacos上的服务以域名的方式暴露端点,让三方应用方便的查阅及发现。
服务和元数据管理
Nacos能让您从微服务平台建设的视角管理数据中心的所有服务及元数据,包括管理服务的描述、生命周期、服务的静态依赖分析、服务的健康状态、服务的流量管理、路由及安全策略。
window直接运行
默认端口8848
启动nacos成功后,可通过nacos提供的http api验证nacos服务运行是否正常。
下边我们通过curl工具来测试nacos的open api :
curl是开发中常用的命令行工具,可以用作HTTP协议测试。
本教程下载curl的windows版本: curl-7.66.0 _2-win64-mingw,下载地址: htts://curl.haxx.se/windows/
发布配置
curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=HelloWorld"
true响应成功
获取配置
curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test"
得到我们刚才发布的内容HelloWord
1.3.2 直接支持8.0+
打开nacos控制台,并点击菜单配置管理~>配置列表:
在Nacos添加如下的配置:
Data ID:nacos-simple-demo.yaml
Group:DEFAULT_GROUP
配置内容:
common:
config1: something
发布完成后,我们就能看到了
那我不是使用了持久化到数据库么?他在那个表里面呢?
<dependencies>
<dependency>
<groupId>com.alibaba.nacosgroupId>
<artifactId>nacos-clientartifactId>
<version>1.3.2version>
dependency>
dependencies>
package com.wqh.nacos;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import java.util.Properties;
/**
* @author 王庆华
* @version 1.0
* @date 2021/4/27 22:18
* @Description TODO
* @pojectname nacos
*/
public class SimpleDemo {
public static void main(String[] args) throws NacosException {
//使用nacos client远程获取nacos服务上的配置信息
//nacos服务的地址
String serverAddr = "127.0.0.1:8848";
//dataId
String dataId = "nacos-simple-demo.yaml";
//Group
String group = "DEFAULT_GROUP";
Properties properties = new Properties();
properties.put("serverAddr",serverAddr);//指定地址
//获取配置
ConfigService configService = NacosFactory.createConfigService(properties);
String config = configService.getConfig(dataId, group, 5000);
System.out.println(config);
}
}
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
common:
config1: something
这样我们就能快速获取到我们发布的配置了
对于Nacos配置管理,通过Namespace、group、Data ID能够定位到一个配置集。
配置集(Data ID)
在系统中,一个配置文件通常就是一个配置集, 一个配置集可以包含了系统的各种配置信息,例如,一个配置集可能包含了数据源、线程池、日志级别等配置项。每个配置集都可以定义一个有意义的名称,就是配置集的ID即Data ID.
配置项
就是我们的配置内容
配置集中包含的一个个配置内容就是配置项。它代表-个具体的可配置的参数与其值域,通常以key=value的形式存在。例如我们常配置系统的日志输出级别( logLevel=INFO |WARN|ERROR)就是一个配置项。
配置分组(Group)
配置分组是对配置集进行分组,通过一个有意义的字符串(如Buy或Trade ) 来表示
不同的配置分组下可以有相同的配置集( DataID)。当您在Nacos上创建一个配置时 ,如果未填写配置分组的名称,则配置分组的名称默认采用DEFAULT. GROUP。配置分组的常见场景:可用于区分不同的项目或应用,例如:学生管理系统的配
置集可以定义一个group为: STUDENT GROUP。
命名空间(Namespace)
命名空间( namespace )可用于进行不同环境的配置隔离。
例如可以隔离开发环境、测试环境和生产环境,因为它们的配置可能各不相同,或者是隔离不同的用户,不同的开发人员使用同一个nacos管理各自的配置,可通过namespace隔离。
不同的命名空间下,可以存在相同名称的配置分组(Group)或配置集。
实践
Nacos抽象定义了Namespace、Group、 Data ID的概念,具体这几个概念代表什么,取决于我们把它们看成什么,这里推荐给大家一种用法,如下图:
Namespace :代表不同环境,如开发、测试、生产环境。
Group :代表某项目,如XX医疗项目、xx电商项目
Datald :每个项目下往往有若干个工程,每个配置集(Datald)是一个工程的主配置文件
获取某配置集的代码:需要指定
1.nacos服务地址,必须指定
2.namespace,不指定默认为public
3.group,不指定默认为DEFAULT_GROUP
4.dateId,必须指定
namespace的设计是nacos基于此做多环境以及多租户(多个用户共同使用nacos )数据(配置和服务)隔离的。
●从一个租户(用户)的角度来看,如果有多套不同的环境,那么这个时候可以根据指定的环境来创建不同的namespce ,以此来实现多环境的隔离。例如,你可能有开发,测试和生产三个不同的环境,那么使用一套nacos集群可以分别建以下三个不同的namespace.如下图所示:
●从多个租户(用户)的角度来看,每个租户(用户)可能会有自己的namespace,每个租户(用户)的配置数据以及注册的服务数据都会归属到自己的namespace下,以此来实现多租户间的数据隔离。例如超级管理员分配了三个租户,分别为张三、李四和王五。分配好了之后,各租户用自己的账户名和密码登录后,创建自己的命名空间。如下图所示:
新建命名空间
这里有一个坑,我创建新的命名空间一开始是一直没反应的,关了我们的nacos重新启动就OK了
如果您在编写程序获取配置集过程中没有感知到这个参数的输入,那么nacos统一会使用一 个默认的namespace作为输入, nacos config会使用一个空字符串作为默认的参数来初始化,对应界面上就是public命名空间。
我们测试下,我们以前是在public下有一个nacos-simple-demo.yaml,我们现在在新建的test命名空间下,在创建一个
/**
* @author 王庆华
* @version 1.0
* @date 2021/4/27 22:18
* @Description TODO
* @pojectname 面试相关
*/
public class SimpleDemo {
public static void main(String[] args) throws NacosException {
//使用nacos client远程获取nacos服务上的配置信息
//nacos服务的地址
String serverAddr = "127.0.0.1:8848";
//dataId
String dataId = "nacos-simple-demo.yaml";
//Group
String group = "DEFAULT_GROUP";
//指定namespace
String namespace = "404b3c96-0446-4821-815f-aecc0980da0a";
Properties properties = new Properties();
properties.put("serverAddr",serverAddr);//指定地址
properties.put("namespace",namespace);
//获取配置
ConfigService configService = NacosFactory.createConfigService(properties);
String config = configService.getConfig(dataId, group, 5000);
System.out.println(config);
}
}
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
common:
config1:somethingaaa
主要就是配置列表的页面和操作,其中修改的操作是带比较界面的
配置集导出
就比如我们的测试环境中很多配置跟开发环境相同,我们就不用一个个去发布配置了,而是可以导出配置,批量加载进去
克隆
将我们这边的命名空间的配置克隆到其他命名空间
可以查看历史的修改记录,还可以回滚到以前的版本
Nacos提供配置订阅者即监听者查询能力,同时提供客户端当前配置的MD5校验值,以便帮助用户更好的检查配置变更是否推送到Client 端。
package com.wqh.nacos;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import java.util.Properties;
import java.util.concurrent.Executor;
/**
* @author 王庆华
* @version 1.0
* @date 2021/4/27 22:18
* @Description TODO
* @pojectname 面试相关
*/
public class SimpleDemo {
public static void main(String[] args) throws NacosException {
//使用nacos client远程获取nacos服务上的配置信息
//nacos服务的地址
String serverAddr = "127.0.0.1:8848";
//dataId
String dataId = "nacos-simple-demo.yaml";
//Group
String group = "DEFAULT_GROUP";
//指定namespace
String namespace = "404b3c96-0446-4821-815f-aecc0980da0a";
Properties properties = new Properties();
properties.put("serverAddr",serverAddr);//指定地址
properties.put("namespace",namespace);
//获取配置
ConfigService configService = NacosFactory.createConfigService(properties);
String config = configService.getConfig(dataId, group, 5000);
System.out.println(config);
configService.addListener(dataId, group, new Listener() {
@Override
public Executor getExecutor() {
return null;
}
//当配置有变化的时候 ,获取通知
@Override
public void receiveConfigInfo(String s) {
System.out.println(s);
}
});
while (true){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
监听的是我们test环境下的配置文件
我们去监听查询看看。
已经开始监听了,那我们现在修改这个配置
再去看客户端IDEA的运行结果
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
common:
config1:somethingaaa
common:
config1:somethingFFFF
Web应用程序发展的早期,大部分web工程师将所有的功能模块打包到一起并放在一个web容器中运行 ,所有功能模块使用同一个数据库,同时,它还提供API或者访问的web模块等。
尽管也是模块化逻辑,但是最终它还是会打包并部署为单体式应用,这种将所有功能都部署在一个web容器中运行的系统就叫做单体架构(也叫:巨石型应用)
单体架构有很多好处:
开发效率高、容易测试、容易部署
缺点
复杂性逐渐变高,可维护性逐渐变差、版本迭代速度逐渐变慢
**无法按需伸缩:**通过冗余部署完整应用的方式来实现水平扩展,无法针对某业务按需伸缩。
一个微服务一般完成某个特定的功能 ,比如订单服务、用户服务等等。每一个微服务都是完整应用,都有自己的业务逻辑和数据库。一些微服务还会发 布AP给其它微服务和应用客户端使用。
每一个业务模块都使用独立的服务完成,这种微服务架构模式也影响了应用和数据库之间的关系,不像传统多个业务模块
共享-个数据库 ,微服务架构每个服务都有自己的数据库。
好处
分而治之,职责单一;易于开发、理解和维护、方便团队的拆分和管理
可伸缩;能够单独的对指定的服务进行伸缩
局部容易修改,容易替换,容易部署,有利于持续集成和快速迭代
不会受限于任何技术栈
用户通过Nacos Server的控制台集中对多个服务的配置进行管理。
各服务统一从Nacos Server中获取各自的配置,并监听配置的变化。
我们在命名空间为dev的环境下测试
首先在nacos发布配置,我们规划了两个服务service1、service2 ,并且想对这两个服务的配置进行集中维护。
在Nacos添加如下的配置:
规范依赖
<packaging>pompackaging>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.1.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Greenwich.RELEASEversion>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenicesartifactId>
<version>2.1.3.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
Spring Cloud是什么:
Spring Cloud是一系列框架的有序集合。 它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发, 如服务发现注册、配置中心I消息总线、负载均衡、断路器、数据监控等,都可以用SpringBoot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,集成最多的组件要属Netflix公司,通过Spring Boot风格进行
再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、 易部署和易维护的分布式系统开发工具包。
Spring Cloud Alibaba 是什么
Spring Cloud Alibaba Nacos Discovery是Spring Cloud Alibaba的子项目,而Spring Cloud Alibaba是阿里巴巴公司提供的开源的基于Spring cloud的微服务套件合集,它致力于提供微服务开发的一站式解决方案,可以理解为spring cloud是一套微服务开发的 标准, spring cloud alibaba 与spring cloud Netflix是实现。使用Spring Cloud Alibaba方案,开发者只需要添加一些注解和少量配置,就可以将Spring Cloud应用
接入阿里分布式应用解决方案,通过阿里中间件来迅速搭建分布式应用系统。
由于Nacos是阿里的中间件,因此,若开发Spring cloud微服务应用,使用Spring Cloud Alibaba NacosConfig来集成Nacos的配置管理功能是比较明智的选择。
新建项目service1
首先新增一个名为service1工程 ,并添加group ID为com. alibaba.cloud和artifact ID为spring-cloud-starter-alibaba-nacos-config的starter.
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
<version>2.1.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<version>2.1.3.RELEASEversion>
dependency>
dependencies>
配置文件编写
一般来说 ,spring boot的配置将在application.yml(也可以是application.properties)文件中编写,由于使用外部配置中心,必须将原先的application.yml重命名为bootstrap.yml , bootstrap.ym如下所示:
为什么不写进我们的application.yml中呢?
因为我们的bootstarp的加载顺序优先于我们的application
server:
port: 56010 #启动端口号 命令行注入
spring:
application:
name: service1
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848 #配置中心
file-extension: yaml #配置文件后缀名 dataId = application.name + file-extension
namespace: ea8fdc12-3509-472b-b8a9-5145f7e5ce85 #开发环境
group: TEST_GROUP #测试组
编写启动客户端
@SpringBootApplication
public class Service1Bootstrap {
public static void main(String[] args) {
SpringApplication.run(Service1Bootstrap.class,args);
}
}
service2的方式都一样,不过就是名称改一改,2的server.prot端口号换一个就行了
测试
@SpringBootApplication
@RestController
public class Service1Bootstrap {
public static void main(String[] args) {
SpringApplication.run(Service1Bootstrap.class,args);
}
@Value("${common.name}")
private String config1;
@GetMapping("/configs")
public String getConfig(){
//读取配置信息
return config1;
}
}
启动访问我们的localhost:56010/configs,得到我们想要的配置
注意nacos更改配置后,我们刷新是获取不到最新的配置值的,是没通知到么?
不是,实际上这个通知已经产达到了,但是我们的value注解并没有把最新的配置进行赋值的,怎么做到动态更新呢?
利用Spring的上下文机制来动态更新
@SpringBootApplication
@RestController
public class Service1Bootstrap {
public static void main(String[] args) {
SpringApplication.run(Service1Bootstrap.class,args);
}
@Autowired
ConfigurableApplicationContext applicationContext;
@GetMapping("/configs")
public String getConfig(){
//读取配置信息
return applicationContext.getEnvironment().getProperty("common.name");
}
}
使用这种方式,nacos配置中心更改配置之后,我们直接刷新请求就可以动态更新了
在没有明确指定${spring.cloud.nacos.config.namespace}配置的情况下,默认使用的是Nacos上Public这个namespace.如果需要使用自定义的命名空间,可以通过以下配置来实现:
假如我们一个service要使用一些公共的配置,不止一个的时候怎么解决
Spring Cloud Alibaba Nacos Config可支持自定义Data ld的配置。一个完整的配置案例如下所示:
server:
port: 56010 #启动端口号 命令行注入
spring:
application:
name: service1
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848 #配置中心
file-extension: yaml #配置文件后缀名 dataId = application.name + file-extension
namespace: ea8fdc12-3509-472b-b8a9-5145f7e5ce85 #开发环境
group: TEST_GROUP #测试组
#扩展dataId
#1.DataId 在默认的组DEFAULT_GROUP下,不支持配置的动态刷新
ext-config[0]:
data-id: ext-config-common01.properties
#dataId 不在默认的组,不支持动态刷新
ext-config[1]:
data-id: ext-config-common02.properties
group: GLOBALE_GROUP
#dataId 既不在默认的组,也支持动态刷新
ext-config[2]:
data-id: ext-config-common03.properties
group: REFREH_GROUP
refresh: true
配置1
配置2
配置3
测试
@SpringBootApplication
@RestController
public class Service1Bootstrap {
public static void main(String[] args) {
SpringApplication.run(Service1Bootstrap.class,args);
}
@Autowired
ConfigurableApplicationContext applicationContext;
@GetMapping("/configs")
public String getConfig(){
//读取配置信息
return applicationContext.getEnvironment().getProperty("common.name");
}
@GetMapping("/configs2")
public String getConfig2(){
String name = applicationContext.getEnvironment().getProperty("common.name");
String age = applicationContext.getEnvironment().getProperty("common.age");
String address = applicationContext.getEnvironment().getProperty("common.address");
String birthday = applicationContext.getEnvironment().getProperty("common.birthday");
String fullname = applicationContext.getEnvironment().getProperty("common.fullname");
return name + "+" + age + "+" + address + "+" + birthday + "+" + fullname;
}
}
能读取到么?不在一个组
是可以读取到的,然后我们再去分别修改修改这三个配置,然后刷新我们的请求看看能不能动态刷新
我们可以看到,只有我们的第三个配置文件可以动态刷新
为了更加清晰的在多个应用间配置共享的Data Id ,你可以通过以下的方式来配置:
spring:
cloud:
nacos:
config:
shared-dataids: ext-config-common01. properties,ext-config-common02.properties
refreshable-dataids: ext-config-common01.properties
#刷新某个配置文件
可以看到:
●通过(spring.cloud. nacos . config. shared-dataids 来支持多个共享Data ld的配置,多个之间用逗号隔开。
●通过spring.cloud.nacos . config. refreshable-dataids来支持哪些共享配置的Data ld在配置变化时,应用中是否可动态刷新,感知到最新的配置值,多个Data Id之间用逗号隔开。如果没有明确配置,默认情况下所有共享配置的Data ld都不支持动态刷新。
server:
port: 56010 #启动端口号 命令行注入
spring:
application:
name: service1
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848 #配置中心
file-extension: yaml #配置文件后缀名 dataId = application.name + file-extension
namespace: ea8fdc12-3509-472b-b8a9-5145f7e5ce85 #开发环境
group: TEST_GROUP #测试组
shared-dataids: ext-config-common01.properties,ext-config-common02.properties,ext-config-common03.properties
refreshable-dataids: ext-config-common01.properties
为啥我第二个配置文件和第三个配置文件不生效了呢?
因为这种共享的id方式只认我们的DEFAULT_GROUP的东西其他的不认
享配置的Data ld在配置变化时,应用中是否可动态刷新,感知到最新的配置值,多个Data Id之间用逗号隔开。如果没有明确配置,默认情况下所有共享配置的Data ld都不支持动态刷新。
server:
port: 56010 #启动端口号 命令行注入
spring:
application:
name: service1
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848 #配置中心
file-extension: yaml #配置文件后缀名 dataId = application.name + file-extension
namespace: ea8fdc12-3509-472b-b8a9-5145f7e5ce85 #开发环境
group: TEST_GROUP #测试组
shared-dataids: ext-config-common01.properties,ext-config-common02.properties,ext-config-common03.properties
refreshable-dataids: ext-config-common01.properties
为啥我第二个配置文件和第三个配置文件不生效了呢?
因为这种共享的id方式只认我们的DEFAULT_GROUP的东西其他的不认
Spring Cloud Alibaba Nacos Config目前提供了三种配置能力从Nacos拉取相关的配置。
A:通过spring. cloud.nacos . config. shared-dataids支持多个共享Data Id的配置
B:通过spring. cloud. nacos . config . ext-config[n].data-id的方式支持多个扩展Data ld的配置,多个Data Id同时配置时,他的优先级关系是‘spring. cloud. nacos . config. ext -config[n].data-id其中 n的值越大,优先级越高.
C:通过内部相关规则(应用名、扩展名)自动生成相关的Data ld配置
当三种方式共同使用时,他们的一个优先级关系是:C>B>A
通过设置spring.cloud.nacos.config.enabled = false来完全关闭Spring Cloud Nacos Config
3个或3个以上Nacos节点才能构成集群
(1 )安装3个以上Nacos
我们可以复制之前已经解压好的nacos文件夹,分别命名为nacos. nacos1、 nacos2
(2)配置集群配置文件
在所有nacos目录的conf目录下,有文件cluster . conf . example ,将其命名为cluster . conf , 并将每行配置成ip:port. ( 请配置3个或3个以上节点)
(3 )集群模式启动
分别执行nacos目录的bin目录下的startup :
startup -m cluster
所有客户端,分别指定nacos集群中的若干节点:
spring:
application:
name: xXxx
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848,127.0.0.1: 8849,127.0.0.1: 8850