目录
2.1 创建新的开发环境并定义其配置,然后从开发环境的配置中读取配置信息
4.2 在指定的微服务配置文件(bootstrap.yml)中设置对共享配置文件的读取
一 配置中心
1 为什么需要配置中心?
除了代码之外,软件还有一些配置信息,比如数据库的用户名和密码,还有一些我们不想写死在代码里的东西,例如像线程池大小、队列长度等运行参数,以及日志级别、算法策略等, 还有一些是软件运行环境的参数,如Java 的内存大小,应用启动的参数,包括操作系统的一些 参数配置…… 所有这些东西,我们都叫做软件配置。以前,我们把软件配置写在一个配置文件中,就像 Windows 下的 ini 文件,或是 Linux 下的 conf 文件。然而,在分布式系统下,这样的方式就变得非常不好管理,并容易出错。假如生产环境下,项目现在正在运行,此时修改了配置文件,我们需要让这些配置生效,通常的做法是不是要重启服务。但重启是不是会带来系统服务短时间的暂停,从而影响用户体验呢,还有可能会带来经济上的很大损失(例如双11重启下服务)。基于这样的背景,配置中心诞生了。以下举例:
1.1 在服务提供方打印日志
package com.jt;
import org.slf4j.Logger;//Java中的日志规范
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
public class ProviderApplication {
//创建Java的日志对象(SLF4J是Java中的日志规范,是日志对外的窗口,门面模式)
//目前市场上对SLF4J规范的实现有两种:log4j 和 logback
private static final Logger log =
LoggerFactory.getLogger(ProviderController.class);
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class,args);
}
/*定义Controller对象(这个对象在spring MVC中给它定义是handler)
* 基于此对象处理客户端的请求*/
@RestController
public class ProviderController{
/*获取端口号
* @Value默认读取项目而皮质文件中的配置内容*/
@Value("${server.port:8080}")
private String server;
//http://localhost:8082/provider/echo/tedu
@GetMapping("/provider/echo/{msg}")
public String doRestEcho1(@PathVariable String msg) throws InterruptedException {
//休眠50秒
//Thread.sleep(50000);
//日志的级别trace<debug<info<warn<error,
// 默认debug和trace不显示,想显示在控制台需要在配置文件中配置日志
log.trace("==trace==");
log.debug("==debug==");
log.info("==info==");
log.warn("==warn==");
log.error("==error==");
return server+": say hello "+msg;
}
}
}
1.2 重启后,网页输入提供方的url,查看控制台打印效果
会发现,没有打印debug和trace,这是因为它们日志级别太低,如果想要打印他们呢?就要在配置文件中进行配置日志
1.3 服务提供方配置文件中配置日志
server:
port: 8082
spring:
application:
name: sca-provider #服务名
#由于debug日志级别低,正常情况下不会在控制台显示,
#需要在配置文件配置日志,控制台才返回debug日志
#然后重启服务,但是重启服务会造成系统短时间的暂停,所以需要配置中心
logging:
level:
com.jt: debug
最后,重启服务器,网页输入url,查看控制台日志:
拓展:
1.4 由此引发的问题分析
假如生产环境下,项目现在正在运行,此时修改了配置文件,或者修改日志级别查看不同级别的日志情况,而我们需要让这些配置生效,通常的做法是不是要重启服务。但重启是不是会带来系统服务短时间的暂停,从而影响用户体验呢,还有可能会带来经济上的很大损失(例如双11重启下服务)。基于这样的背景,配置中心诞生了。
2 什么是配置中心
配置中心最基础的功能就是存储一个键值对,用户发布一个配置(configKey),然后客户端获取这个配置项(configValue);进阶的功能就是当某个配置项发生变更时,不停机就可以动态刷新服务内部的配置项,例如,在生产环境上我们可能把我们的日志级别调整为 error 级别,但是,在系统出问题我们希望对它 debug 的时候,我们需要动态的调整系统的行为的能力,把日志级别调整为 debug 级别。还有,当你设计一个电商系统时,设计大促预案一定会考虑,同时涌进来超过一亿人并发访问的时候,假如系统是扛不住的,你会怎么办,在这个过程中我们一般会采用限流,降级。系统的限流和降级本质上来讲就是从日常的运行态切换到大促态的一个行为的动态调整,这个本身天然就是配置起到作用的一个相应的场景。
3 配置中心的选型
在面向分布式的微服务系统中,如何通过更高效的配置管理方式,实现微服务系统架构持续“无痛”的演进,并动态调整和控制系统的运行时态,配置中心的选型和设计起着举足轻重的作用。市场上主流配置中心有Apollo(携程开源),nacos(阿里开源),Spring Cloud Config(Spring Cloud 全家桶成员)。我们在对这些配置中心进行选型时重点要从产品功能、使用体验、实施过程和性能等方面进行综合考量。本次课程我们选择nacos,此组件不仅提供了注册中心,还具备配置中心的功能。
4 小节面试分析
- 什么是配置中心?(存储项目配置信息的一个服务)
- 为什么要使用配置中心?(集中管理配置信息,动态发布配置信息)
- 市场上有哪些主流的配置中心?(Apollo,nacos,……)
二 Nacos配置入门案例
1 provider的pom.xml文件添加配置中心依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2 修改配置文件
将项目中的application.yml的名字修改为bootstrap.yml配置文件(启动优先级最高):
文件内容:注意:配置文件的格式要书写正确,上下左右单词对位,否则没有效果
server:
port: 8081
spring:
application:
name: sca-provider #服务名
cloud:
nacos:
discovery: #注册中心
server-addr: localhost:8848
config: #配置中心配置
server-addr: localhost:8848
file-extension: yml
#由于debug日志级别低,正常情况下不会在控制台显示,
#需要在配置文件配置日志,控制台才返回debug日志
#然后重启服务,但是重启服务会造成系统短时间的暂停,所以需要配置中心
#logging:
#level:
#com.jt: debug
3 Nacos新建基本配置
打开nacos配置中心,新建配置,如图所示:
其中Data IDs的值要与bootstrap.yml中定义的spring.application.name的值相同(服务名-假如有多个服务一般会创建多个配置实例,不同服务对应不同的配置实例)。
4 Controller处理器操作
在ProviderController中添加一个获取日志级别(debug<info<warn<error)的方法,代码如下:
package com.jt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
public class ProviderApplication {
//创建Java的日志对象(SLF4J是Java中的日志规范,是日志对外的窗口,门面模式)
//目前市场上对SLF4J规范的实现有两种:log4j 和 Logback
private static final Logger log =
LoggerFactory.getLogger(ProviderController.class);
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class,args);
}
/*定义Controller对象(这个对象在spring MVC中给它定义是handler)
* 基于此对象处理客户端的请求*/
@RefreshScope//动态刷新配置
@RestController
public class ProviderController{
public ProviderController(){
System.out.println("==ProviderController()==");
}
/*获取端口号
* @Value默认读取项目而配置文件中的配置内容
* 8080为没有读到server.port的值时,给定的默认值*/
@Value("${server.port:8080}")
private String server;
//http://localhost:8082/provider/echo/tedu
@GetMapping("/provider/echo/{msg}")
public String doRestEcho1(@PathVariable String msg) throws InterruptedException {
//休眠50秒
//Thread.sleep(50000);
return server+": say hello "+msg;
}
@Value("${logging.level.com.jt:error}")
private String logLevel;
@GetMapping("/provider/doGetLogLevel")
public String doGetLogLevel(){
//日志的级别trace<debug<info<warn<error,
// 默认debug和trace不显示,想显示在控制台需要在配置文件中配置日志
log.trace("==log.trace==");//跟踪
log.debug("==log.debug==");//调试
log.info("==log.info==");//常规信息
log.warn("==log.warn==");//警告
log.error("==log.error==");//错误信息
return "log level is " +logLevel;
}
}
}
其中,@RefreshScope的作用是在配置中心的相关配置发生变化以后,能够及时看到更新(底层是通过重新创建Controller对象的方式,对属性进行了重新初始化),Controller编写好以后,启动配置中心服务,然后进行访问测试。,打开浏览器直接在地址栏输入http://localhost:8081/provider/doGetLogLevel,检测输出结果是否为我们配置中配置的信息,如图所示。
其中,后端控制台打印日志级别:
错误说明,假如对配置的信息访问不到,请检测项目配置文件的名字是否为bootstrap.yml,检查配置文件中spring.application.name属性的值是否与配置中心的data-id名相同,还有你读取的配置信息缩进以及空格写的格式是否正确.
5 Nacos配置动态更新实现
当我们在nacos服务配置列表修改后发布后,刷新页面显示的不一样且后端也会改变它的打印日志级别
修改Nacos的日志级别配置并重新重新发布,如图所示:
刷新浏览器url,检测其配置输出。
后台打印日志的级别变化:
6 配置中心面试分析
- 配置中心一般都会配置什么内容?(可能会经常变化的配置信息,例如连接池,日志、线程池、限流熔断规则)
- 什么信息一般不会写到配置中心?(服务端口,服务名,服务的注册地址,配置中心)
- 项目中为什么要定义bootstrap.yml文件?(此文件被读取的优先级比较高,可以在服务启动时读取配置中心的数据)
- Nacos配置中心宕机了,我们的服务还可以读取到配置信息吗?(可以从内存,客户端获取了配置中心的配置信息以后,会将配置信息在本地内存中存储一份.)
- 微服务应用中我们的客户端如何获取配置中心的信息?(为了考虑性能我们的服务一般首先会从内存读取配置信息,同时我们的微服务还可以定时向nacos配置中心发请求间隔30秒拉取(pull)更新配置信息,但是在一定时间间隔内还可能会出现不一致的配置,所以对nacos服务端而言,当配置变化时,会通知客户端然后更新客户端.)--长轮询,短轮询
- 微服务应用中客户端如何感知配置中心数据变化?(当数据发生变化时,nacos找到它维护的客户端,然后通知客户端去获取更新的数据,客户端获取数据以后更新本地内存,并在下次访问资源时,刷新@Value注解描述的属性值,但是需要借助@RefreshScope注解对属性所在的类进行描述)
- 服务启动后没有从配置中心获取我们的配置数据是什么原因?(依赖单词或者包有误,配置文件名字bootstrap.yml未修改,配置中心的dataId名字是否正确,分组是否正确,配置的名字是否正确,缩进关系是否正确,假如是动态发布,类上是否有@RefreshScope注解)
- 你项目中使用的日志规范是什么?(SLF4J)
- 你了解项目中的日志级别吗?(debug,info,error,…,可以基于日志级别控制日志的输出)
三 Nacos配置管理模型
1 结构
Nacos 配置管理模型由三部分构成,如图所示:
其中:
- Namespace:命名空间,对不同的环境进⾏隔离,⽐如隔离开发环境和⽣产环境。
- Group:分组,将若⼲个服务或者若⼲个配置集归为⼀组。
- Service/DataId:某⼀个服务或配置集,一般对应一个配置文件。
2 命名空间设计
Nacos中的命名空间一般用于配置隔离,这种命名空间的定义一般会按照环境(开发,生产等环境)进行设计和实现.我们默认创建的配置都存储到了public命名空间,
2.1 创建新的开发环境并定义其配置,然后从开发环境的配置中读取配置信息
第一步:创建新命名空间
命名空间成功创建以后,命名空间的ID会使用UUID法师随机的分配,在如下列表进行呈现。.
第二步: 在指定命名空间下添加配置,也可以直接取配置列表已有的进行选中后克隆
克隆成功以后,我们会发现在指定的命名空间中有了我们克隆的配置,如下所示:
第三步:此时我们修改dev命名空间中Data Id的sca-provider配置
第四步: 修改项目module中的配置文件bootstrap.yml,添加如下配置,关键代码如下:
server:
port: 8081
spring:
application:
name: sca-provider #服务名
cloud:
nacos:
discovery: #注册中心
server-addr: localhost:8848
config: #配置中心配置
server-addr: localhost:8848
file-extension: yml
#主要就是添加命名空间的ID,去指定使用哪个配置,不写的话就是默认public的配置
namespace: 6b757dbd-24ea-4843-abca-7a0f75a653fa
namespace后面的字符串为命名空间的id(系统使用UUID随机生成),可直接从命名空间列表中进行拷贝.
重启服务,继续刷新http://localhost:8081/config/doGetLogLevel地址。检测输出,看看输出的内容是什么,是否为dev命名空间下配置的内容,如图所示:
后台打印的日志级别:
拓展:我们还可以创建生产环境,依次类推进行设计和实现即可!
3 分组设计及实现
1) 当我们在指定命名空间下,按环境或服务做好了配置以后,有时还需要基于服务做分组配置,例如,一个服务在不同时间节点(节假日,活动等)切换不同的配置,可以在新建配置时指定分组名称,如图所示:
tomcat配置线程参数的了解:
2) 配置发布以后,修改boostrap.yml配置类,并在其内部指定我们刚刚创建的分组名:
server:
port: 8081
tomcat: #线程池配置
threads:
max: 300
#min-spare: 8
spring:
application:
name: sca-provider #服务名
cloud:
nacos:
discovery: #注册中心
server-addr: localhost:8848
config: #配置中心配置
server-addr: localhost:8848
file-extension: yml
namespace: 6b757dbd-24ea-4843-abca-7a0f75a653fa #命名空间ID
group: DEV_GROUP_51 #分组组名
3) 在provider启动类中ProviderController下添加属性和方法用于获取和输出DEV_GROUP_51配置中设置的线程数,代码如下:
@Value("${server.tomcat.threads.max:200}")
private Integer maxThread;
@RequestMapping("/provider/doGetMaxThread")
public String doGetMaxThread(){
return "server.threads.max is "+maxThread;
}
4) 然后重启服务,网页输入url进行测试,检测内容输出,如图所示:
4 共享配置设计及读取
当同一个namespace的多个配置文件中都有相同配置时,可以对这些配置进行提取,然后存储到nacos配置中心的一个或多个指定配置文件,哪个微服务需要,就在服务的配置中设置读取即可。例如:
4.1 在nacos中创建一个共享配置文件
4.2 在指定的微服务配置文件(bootstrap.yml)中设置对共享配置文件的读取
server:
port: 8081
tomcat: #线程池配置
threads:
max: 300
#min-spare: 8
spring:
application:
name: sca-provider #服务名
cloud:
nacos:
discovery: #注册中心
server-addr: localhost:8848
config: #配置中心配置
server-addr: localhost:8848
file-extension: yml
namespace: 6b757dbd-24ea-4843-abca-7a0f75a653fa #命名空间ID
#group: DEV_GROUP_51 #分组组名
# 共享配置
shared-configs[0]: #0表示一个数组的下标
data-id: app-common-dev.yml
group: DEFAULT_GROUP #默认分组
refresh: true #默认false
#配置日志(经常变化的数据一般会写在配置中心)
logging:
level:
com.jt: debug
4.3 在指定的业务类中读取和应用共享配置即可
provider启动类的ProviderController类下编辑以下代码:
@Value("${page.pageSize:10}")
private Integer pageSize;
@GetMapping("/provider/doGetPageSize")
public String doGetPageSize(){
//return String.format()
return "page size is "+pageSize;
}
4.4 重启服务器,页面输入url进行访问测试
5 配置管理模型面试点
- Nacos配置管理模型的背景?(环境不同配置不同,开发环境和生产环境等)
- Nacos配置中的管理模型是怎样的?(namespace,group,service/data-id)
- Nacos客户端(微服务)是否可以读取共享配置?(可以)
四 总结(Sammary)
1 重难点
- 配置中心的选型。(市场活跃度、稳定性、性能、易用)
- Nacos配置中心基本应用。(新建,修改、删除配置以后,在Nacos客户端应用配置)
- 配置管理模型应用。(namespace,group,service/dataId)
- Nacos配置变更的动态感知。(底层原理分析)
2 FAQ(常见问题)分析
- 为什么需要配置中心?(动态管理发布配置,无需重启服务,更好保证服务的可用)
- 配置中一般要配置什么内容?(经常变化的配置数据-日志级别,线程池、连接池、…)
- 市面上有哪些主流配置中心?(Nacos,阿波罗….)
- 配置中心选型时要重点考虑哪些因素?(市场活跃度、稳定性、性能、易用)
- Nacos客户端(微服务业务)如何动态感知配置中心数据变化的?(nacos2.0之前nacos客户端采用长轮询机制拉取nacos服务的配置信息,pull(每隔30秒)+“push(是在服务端的配置信息变更以后,通知服务端中处于等待状态的客户端队列中的客户端对象,并更新客户端对应的响应数据,然后返回响应)”)
- Nacos配置管理模型是怎样的?(命名空间-namespace,分组-group,服务实例-dataId)