什么是配置中心?
配置中心最基础的功能就是存储一个键值对,用户发布一个配置(configKey),然后客户端获取这个配置项(configValue);进阶的功能就是当某个配置项发生变更时,不停机就可以动态刷新服务内部的配置项 。
例如:
1. 在生产环境上我们可能把我们的日志级别调整为 error 级别,但是,在系统出问题我们希望对它 debug 的时候,我们需要动态的调整系统的行为的能力,把日志级别调整为 debug 级别。
2. 当你设计一个电商系统时,设计大促预案一定会考虑,同时涌进来超过一亿人并发访问的时候,系统是扛不住时,我们一般会采用限流,降级。系统的限流和降级本质上来讲就是从日常的运行态切换到大促态的一个行为的动态调整,这个本身天然就是配置起到作用的一个相应的场景。
配置中心的选型
在面向分布式的微服务系统中,如何通过更高效的配置管理方式,实现微服务系统架构持续“无痛”的演进,并动态调整和控制系统的运行时态,配置中心的选型和设计起着举足轻重的作用。市场上主流配置中心有Apollo(携程开源),nacos(阿里开源),Spring Cloud Config(Spring Cloud 全家桶成员)。我们在对这些配置中心进行选型时重点要从产品功能、使用体验、实施过程和性能等方面进行综合考量。
小结面试分析
什么是配置中心?(存储项目配置信息的一个服务)
为什么要使用配置中心? (集中管理配置信息,动态发布配置信息)
市场上有哪些主流的配置中心?(Apollo(携程开源),nacos(阿里开源)……)
nacos配置入门
需求:基于nacos配置中心,实现日志级别的动态配置,然后基于日志级别动态调整服务中日志信息的输出,可以减少了后期的日志配置修改。
基础实现步骤:
第一步:添加依赖----在已有的sca-provider项目中添加如配置依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
第二步:创建日志对象--log对象可以自己基于SLF4J API创建,也可以基于@Slf4j对象指定类进行描述,来创建日志对象. 示例代码如下:
@Slf4j //lombok提供的日志注解
@RestController
public class ProviderController {
//@Value("")--默认读取项目配置文件中的配置内容
//如果配置文件不写端口号使用默认端口,此注解会报错.8080写上不会影响其他端口的值
@Value("${server.port:8080}")
public String server;
/**
* 基于此方法实现一个字符串的回显
* echo: 回显的意思
* rest: 一种软件架构编码风格,可以基于这种风格定义url */
@GetMapping("/provider/echo/{msg}")
public String restEcho(@PathVariable String msg){
//需求:通过配置中心中动态日志级别配置,控制日志信息的输出
//常用日志级别: trace<debug<info<warn<error
//很多系统的默认日志级别是info,调试程序时经常会用debug
log.info("日志信息为: {}",System.currentTimeMillis()); //{}-在日志中表示占位符
return "从消费端调用提供端的此方法"+msg+";本服务端口号"+server;
}
}
第三步:修改配置文件---将项目中的application.yml的名字修改为bootstrap.yml配置文件(启动优先级最高)
spring:
application:
name: sca-provider #nacos中服务名称
cloud:
nacos:
discovery: #服务的注册和发现
server-addr: localhost:8848 #nacos的端口号
config: #服务配置(将可能变换的数据写到配置中心)
server-addr: localhost:8848
file-extension: yml #读取
#日志配置---有配置中心可以写在配置中心中,重复书写这里的会覆盖配置中心的
#logging:
# level:
# com.cy: error #日志打印级别
# #下面是springboot的日志配置
# file:
# path: E:\TheFourthStage/doc/loggers #日志文件输出的保存地址
# pattern: #限制日志输出格式以及位置
# console: '%-5level %msg %n' #在控制台输出日志的格式
# file: '%d %-5level %thread %logger %msg %n' #在文件中输出日志的格式
# %d 表示日期时间
# %-5level 日志级别
# %thread 线程名
# %logger 日志输出方(哪个类输出的日志)
# %msg 具体的日志信息
# %n 换行
第四步:在nacos配置中心点击右上方加号新建配置,配置内容如下:
其中Data ID的值要与bootstrap.yml中定义的spring.application.name的值相同(服务名-假如有多个服务一般会创建多个配置实例,不同服务对应不同的配置实例)。
第五步:配置中心动态发布测试-----启动sca-provider服务,然后打开浏览器,http://localhost:8081/provider/getLoggerLevel}这个路径代表的资源进行访问,检测idea控制台日志输出.,访问成功后,打开idea控制台,检测日志输出.然后再打开nacos控制台动态更新日志级别,再访问资源并检测后台日志输出
nacos动态刷新机制分析
对于nacos配置中心而言,有系统内部对配置变化的感知,还有外部系统对配置的感知,假如我们系统在浏览器中能看到日志级别的变化,该如何实现呢?我们现在来实现一个案例.
第一步:创建ProviderLogController中添加一个获取日志级别(trace<debug<info<warn<error)的方法
@Slf4j
@RefreshScope //@RefreshScope 注解用于告诉spring,一般配置中心数据发生变化,自动重新创建它描述的类的实例,
@RestController
@RequestMapping("/provider")
public class ProviderLoggerController {
public ProviderLoggerController(){
System.out.println("getLoggerLevel()");
}
//此属性会在对象构建时初始化,服务器启动时会将所有配置信息(配置文件或配置中心)读取到Environment对象
//@Value用于告诉spring从Environment对象中读取配置信息,将读取到配置内容赋值给对象的属性
@Value("${logging.level.com.cy:error}")
private String loggerLevel;
@RequestMapping("/getLoggerLevel")
public String getLoggerLevel(){
//日志的输出会随着配置中心日志级别的更新进行调整
log.trace("跟踪日志信息:trace");
log.debug("调视日志信息:debug");
log.info("常规日志信息:info");
log.warn("警告日志信息:warn");
log.error("错误日志:error");
return "动态日志级别是:"+loggerLevel;
}
}
第二步:在类的上面添加一个@RefreshScope注解------作用是在配置中心的相关配置发生变化以后,能够及时看到类中属性值的更新(底层是通过重新创建Controller对象的方式,对属性进行了重新初始化),Controller编写好以后,启动配置中心服务,然后进行访问测试。打开浏览器直接在地址栏输入http://localhost:8081/provider/doGetLogLevel,检测输出结果是否为我们配置中配置的信息。
错误注意:假如对配置的信息访问不到,请检测项目配置文件的名字是否为bootstrap.yml,检查配置文件中spring.application.name属性的值是否与配置中心的data-id名相同,还有你读取的配置信息缩进以及空格写的格式是否正确
小结面试分析
1. 配置中心一般都会配置什么内容?------(可能会经常变化的配置信息,例如连接池,日志、线程池、限流熔断规则)
2. 什么信息一般不会写到配置中心?------(服务端口,服务名,服务的注册地址,配置中心)
项目中为什么要定义bootstrap.yml文件?------(此文件被读取的优先级比较高,可以在服务启动时读取配置中心的数据)
3. Nacos配置中心宕机了,我们的服务还可以读取到配置信息吗?--------(可以从内存,客户端获取了配置中心的配置信息以后,会将配置信息在本地内存中存储一份.)
4. 微服务应用中我们的客户端如何获取配置中心的信息?-------(我们的服务一般首先会从内存读取配置信息,同时我们的微服务还可以定时向nacos配置中心发请求拉取(pull)更新的配置信息)
5. 微服务应用中客户端如何感知配置中心数据变化?-----(当数据发生变化时,nacos找到它维护的客户端,然后通知客户端去获取更新的数据,客户端获取数据以后更新本地内存,并在下次访问资源时,刷新@Value注解描述的属性值,但是需要借助@RefreshScope注解对属性所在的类进行描述)
6. 服务启动后没有从配置中心获取我们的配置数据是什么原因?-------------------(依赖,配置文件名字bootstrap.yml,配置中心的dataId名字是否正确,分组是否正确,配置的名字是否正确,缩进关系是否正确,假如是动态发布,类上是否有@RefreshScope注解)
7. 你项目中使用的日志规范是什么?------(SLF4J)
Nacos配置管理模型
命名空间设计
Nacos中的命名空间一般用于配置隔离,这种命名空间的定义一般会按照环境(开发,生产等环境)进行设计和实现.我们默认创建的配置都存储到了public命名空间
创建新的开发环境并定义其配置,然后从开发环境的配置中读取配置信息
第一步:创建新命名空间
在指定命名空间下添加配置,也可以直接取配置列表中克隆
克隆成功以后,我们会发现在指定的命名空间中有了我们克隆的配置
此时我们修改dev命名空间中Data Id的sca-provider配置
第二步:修改项目module中的配置文件bootstrap.yml,添加如下配置-----添加namespace(注意缩进),namespace后面的字符串为命名空间的id,可直接从命名空间列表中进行拷贝.
server:
port: 8081 #provider的端口号
spring:
application:
name: sca-provider #nacos中服务名称
cloud:
nacos:
discovery: #服务的注册和发现
server-addr: localhost:8848 #nacos的端口号
config: #服务配置(将可能变换的数据写到配置中心)
server-addr: localhost:8848
file-extension: yml #读取
namespace: a2b8c24f-386b-42a7-ad1f-f3a8dc4ae046
第三步:重启服务,继续刷新http://localhost:8081/config/doGetLogLevel地址。检测输出,看看输出的内容是什么,是否为dev命名空间下配置的内容
分组设计及实现
当我们在指定命名空间下,按环境或服务做好了配置以后,有时还需要基于服务做分组配置,例如,一个服务在不同时间节点(节假日,活动等)切换不同的配置,可以在新建配置时指定分组名称,如图所示:
配置发布以后,修改boostrap.yml配置类,在其内部通过group指定我们刚刚创建的分组
server:
port: 8081 #provider的端口号
spring:
application:
name: sca-provider #nacos中服务名称
cloud:
nacos:
discovery: #服务的注册和发现
server-addr: localhost:8848 #nacos的端口号
config: #服务配置(将可能变换的数据写到配置中心)
server-addr: localhost:8848
group: DEV_GROUP_51 # Group, default is DEFAULT_GROUP
file-extension: yml #读取
namespace: a2b8c24f-386b-42a7-ad1f-f3a8dc4ae046
在指定的Controller类中添加属性和方法用于获取和输出DEV_GROUP_51配置中设置的线程数
package com.cy.controller;
@RefreshScope
@RestController
public class ProviderThreadController {
@Value("${server.tomcat.threads.max:200}") //默认线程为200
private Integer maxThread;
@RequestMapping("/provider/doGetMaxThread")
public String doGetMaxThread(){
return "server.threads.max is "+maxThread;
}
}
然后重启服务,进行测试,检测内容输出,如图所示:
共享配置设计及读取
当同一个namespace的多个配置文件中都有相同配置时,可以对这些配置进行提取,然后存储到nacos配置中心的一个或多个指定配置文件,哪个微服务需要,就在服务的配置中设置读取即可。例如:
第一步:在nacos中创建一个共享配置文件,例如:
第二步:在指定的微服务配置文件(bootstrap.yml)中设置对共享配置文件的读取,例如:
见红色区域内容。
spring:
application:
name: sca-provider
cloud:
nacos:
config:
server-addr: localhost:8848
# 命名空间
namespace: 83ed55a5-1dd9-4b84-a5fe-a734e4a6ec6d
# 分组名
# group: DEFAULT_GROUP
# 配置中心文件扩展名
file-extension: yml
# 共享配置
shared-configs[0]:
data-id: app-public-dev.yml
refresh: true #默认false,共享配置更新,引用此配置的地方是否要更新
第三步:在指定的Controller类中读取和应用共享配置即可,例如:
package com.cy.controller;
@RefreshScope
@RestController
public class ProviderPageController {
@Value("${page.pageSize:10}")
private Integer pageSize;
@GetMapping("/provider/doGetPageSize")
public String doGetPageSize(){
//return String.format()
return "page size is "+pageSize;
}
}
第四步:启动服务进行访问测试。
小节面试分析
- acos配置管理模型的背景? (环境不同配置不同)
- Nacos配置中的管理模型是怎样的? (namespace,group,service/data-id)
- Nacos客户端(微服务)是否可以读取共享配置? (可以)
总结(Summary)
重难点分析
1. 配置中心的选型。 (市场活跃度、稳定性、性能、易用)
2. Nacos配置中心基本应用。 (新建,修改、删除配置以后,在Nacos客户端应用配置)
3. 配置管理模型应用。 (namespace,group,service/dataId)
4. Nacos配置变更的动态感知。 (底层原理分析)
FAQ分析
1. 为什么需要配置中心?(动态管理发布配置,无需重启服务,更好保证服务的可用)
2. 配置中一般要配置什么内容?(经常变化的配置数据-日志级别,线程池、连接池、…)
3. 市面上有哪些主流配置中心?(Nacos,….)
4. 配置中心选型时要重点考虑哪些因素?(市场活跃度、稳定性、性能、易用)
5. Nacos客户端(微服务业务)如何动态感知配置中心数据变化的?(nacos2.0之前nacos客户端采用长轮询机制每隔30秒拉取nacos配置信息.)
5. Nacos配置管理模型是怎样的?(命名空间-namespace,分组-group,服务实例-dataId)