Nacos服务注册中心
一、Nacos服务注册中心
1. 注册中心简介
背景分析
在微服务中,首先需要面对的问题就是如何查找服务(软件即服务),其次,就是如何在不同的服务之间进行通信?如何更好更方便的管理应用中的每一个服务,如何建立各个服务之间联系的纽带,由此注册中心诞生(例如淘宝网卖家提供服务,买家调用服务)。
市面上常用注册中心有Zookeeper(雅虎Apache),Eureka(Netfix),Nacos(Alibaba),Consul(Google),那他们分别都有什么特点,我们如何进行选型呢?我们主要从社区活跃度,稳定性,功能,性能等方面进行考虑即可.本次微服务的学习,我们选择Nacos,它很好的支持了阿里的双11活动,不仅可以做注册中心,还可以作为配置中心,稳定性和性能都很好。
Nacos概述
Nacos(DynamicNaming and Configuration Service)
是一个应用于服务注册与发现、配置管理的平台。它孵化于阿里巴巴,成长于十年双十一的洪峰考验,沉淀了简单易用、稳定可靠、性能卓越的核心竞争力。其官网地址如下:https://nacos.io/zh-cn/docs/quick-start.html
2. 构建Nacos服务
2.1 准备工作
第一: 确保你电脑已配置JAVA_HOME环境变量(Nacos启动时需要)
第二: 确保你的MySQL版本为5.7以上(MariaDB10.5以上)
2.2 下载与安装
第一步: Nacos下载,可在浏览器直接输入如下地址
https://github.com/alibaba/nacos/releases
第二步: 选择对应版本,直接下载
第三步: 解压Nacos(最好不要解压到中文目录下),其目录结构如下
2.3 初始化配置
第一步: 打开操作系统命令行,登陆mysql,执行课前资料中的sql脚本,不是nacos/conf目录下自带的。
例如,我们可以使用mysql自带客户端,在命令行首先登录mysql,然后执行如下指令:
source d:/nacos-mysql.sql
执行成功以后,会创建一个nacos_config数据库,打开数据库会看到一些表,例如;
说明:在执行此文件时,要求mysql的版本大于5.7版本(MariaDB最好10.5.11),否则会出现如下错误:
第二步: 打开/nacos/conf/application.properties
里打开默认配置,并基于你当前环境配置要连接的数据库,连接数据库时使用的用户名和密码(假如前面有"#"要将其去掉):
### If use MySQL as datasource:
spring.datasource.platform=mysql
### Count of DB:
db.num=1
### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=root
3. 服务启动与访问
第一步: 启动Nacos服务(nacos的bin目录去通过指令启动)。
Linux/Unix/Mac启动命令(standalone代表着单机模式运行,非集群模式):
./startup.sh -m standalone
Windows启动命令(standalone代表着单机模式运行,非集群模式):
startup.cmd -m standalone
说明:
- 执行执行令时要么配置环境变量,要么直接在nacos/bin目录下去执行.
- nacos启动时需要本地环境变量中配置了JAVA_HOME(对应jdk的安装目录),
- 一定要确保你连接的数据库(nacos_config)是存在的.
- 假如所有的配置都正确,还连不上,检查一下你有几个数据库(mysql,…)
第二步: 访问Nacos服务。
打开浏览器,输入http://localhost:8848/nacos地址,出现如下登陆页面:
其中,默认账号密码为nacos/nacos.
3.1 业务描述
创建两个项目Module分别为服务提供者和服务消费者(假如已有则无需创建),两者都要注册到NacosServer中(这个server本质上就是一个web服务,端口默认为8848),然后服务提供者可以为服务消费者提供远端调用服务(例如支付服务为服务提供方,订单服务为服务消费方),如图所示:
3.2 生产者服务创建及注册
第一步: 创建服务提供者工程(module名为sca-provider,假如已有则无需创建),继承parent工程(01-sca),其pom.xml
文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>01-sca</artifactId>
<groupId>com.jt</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sca-provider</artifactId>
<dependencies>
<!--Web服务-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--服务的注册和发现(我们要讲服务注册到nacos)-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
</project>
第二步: 创建并修改配置文件application.yml
(或者application.properties),实现服务注册,关键代码如下:
server:
port: 8081
spring:
application:
name: sca-provider #进行服务注册必须配置服务名
cloud:
nacos:
discovery:
server-addr: localhost:8848
注意: 服务名不要使用下划线(“_”),应使用横杠(“-”),这是规则。
第三步: 创建启动类(假如已有则无需定义),关键代码如下:
package com.jt;
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
第四步: 启动启动类,然后刷先nacos服务,检测是否服务注册成功,如图所示:
第五步: 停掉sca-provider服务,然后不断刷新nacos服务列表,检查服务的健康状态。
3.3 消费者服务发现及调用
第一步: 在sca-provider项目中创建服务提供方对象,基于此对象对外提供服务,例如:
package com.jt.provider.controller;
/**定义Controller对象(这个对象在spring mvc中给他的定义是handler),
* 基于此对象处理客户端的请求*/
@RestController
public class ProviderController{
//@Value默认读取项目配置文件中配置的内容
//8080为没有读到server.port的值时,给定的默认值
@Value("${server.port:8080}")
private String server;
//http://localhost:8081/provider/echo/tedu
@GetMapping("/provider/echo/{msg}")
public String doRestEcho1(@PathVariable String msg){
return server+" say hello "+msg;
}
}
第二步: 创建服务消费者工程(module名为sca-consumer,假如已有则无需创建),继承parent工程(01-sca),其pom.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>01-sca</artifactId>
<groupId>com.jt</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sca-consumer</artifactId>
<dependencies>
<!--Web服务-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--服务的注册和发现(我们要讲服务注册到nacos)-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
</project>
第三步: 创建sca-consumer服务中的配置文件application.yml,关键代码如下:
server:
port: 8090
spring:
application:
name: sca-consumer #服务注册时,服务名必须配置
cloud:
nacos:
discovery:
server-addr: localhost:8848 #从哪里去查找服务
第四步: 创建消费端启动类并实现服务消费,关键代码如下:
package com.jt;
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
}
第五步: 在sca-consumer启动类中添加如下方法,用于创建RestTemplate对象
@Bean
public RestTemplate restTemplate(){//基于此对象实现远端服务调用
return new RestTemplate();
}
第六步: 定义sca-consumer服务的消费端Controller,在此对象方法内部实现远端服务调用
package com.jt.consumer.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* 定义服务消费端Controller,在这个Controller对象
* 的方法中实现对远端服务sca-provider的调用
*/
@RestController
public class ConsumerController {
/**
* 从spring容器获取一个RestTemplate对象,
* 基于此对象实现远端服务调用
*/
@Autowired
private RestTemplate restTemplate;
/**
* 在此方法中通过一个RestTemplate对象调用远端sca-provider中的服务
* @return
* 访问此方法的url: http://localhost:8090/consumer/doRestEcho1
*/
@GetMapping("/consumer/doRestEcho1")
public String doRestEcho01(){
//1.定义要调用的远端服务的url
String url="http://localhost:8081/provider/echo/8090";
//2.基于restTemplate对象中的相关方法进行服务调用
return restTemplate.getForObject(url, String.class);
}
}
第七步: 启动消费者服务,并在浏览器输入http://localhost:8090/consumer/doRestEcho1
地址进行访问测试。
3.4 小节面试分析
- 为什么要将服务注册到nacos?(为了更好的查找这些服务)
- 在Nacos中服务提供者是如何向Nacos注册中心(Registry)续约的?(5秒心跳)
- 对于Nacos服务来讲它是如何判定服务实例的状态?(检测心跳包,15,30)
- 服务消费方是如何调用服务提供方的服务的?(RestTemplate)
二、服务负载均衡设计及实现
业务描述
一个服务实例可以处理请求是有限的,假如服务实例的并发访问比较大,我们会启动多个服务实例,让这些服务实例采用一定策略均衡(轮询,权重,随机,hash等)的处理并发请求,在Nacos中服务的负载均衡(Nacos客户端负载均衡)是如何应用的?
1. LoadBalancerClient应用
LoadBalancerClient对象可以从nacos中基于服务名获取服务实例,然后在工程中基于特点算法实现负载均衡方式的调用,案例实现如下:
第一步: 修改ConsumerController类,注入LoadBalancerClient对象,并添加doRestEcho2方法,然后进行服务访问.
@Autowired
private LoadBalancerClient loadBalancerClient;
@Value("${spring.application.name:8090}")
private String appName;
@GetMapping("/consumer/doRestEcho02")
public String doRestEcho02(){
ServiceInstance serviceInstance = loadBalancerClient.choose("sca-provider");
String url = String.format("http://%s:%s/provider/echo/%s",serviceInstance.getHost(),serviceInstance.getPort(),appName);
System.out.println("request url:"+url);
return restTemplate.getForObject(url, String.class);
}
}
第二步: 打开Idea服务启动配置,如图所示:
第三步: 修改并发运行选项(假如没有找到这个选项我们需要通过搜索引擎基于组合查询的方法,去找到对应的解决方案,例如搜索 idea allow parallel run),如图所示:
第四步: 修改sca-provider的配置文件端口,分别以8081,8082端口方式进行启动。
server:
port: 8082
spring:
application:
name: sca-provider
cloud:
nacos:
server-addr: localhost:8848
第五步: 启动成功以后,访问nacos的服务列表,检测服务是否成功注册,如图所示:
第六步: 启动sca-consumer项目模块,打开浏览器对consumer服务进行访问,访问时不断刷新,检测页面数据变化。
说明,这里多个实例并发提供服务的方式为负载均衡,这里的负载均衡实现默认是因为Nacos集成了Ribbon来实现的,Ribbon配合RestTemplate,可以非常容易的实现服务之间的访问。Ribbon是Spring Cloud核心组件之一,它提供的最重要的功能就是客户端的负载均衡(客户端可以采用一定算法,例如轮询访问,访问服务端实例信息),这个功能可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡方式的服务调用。
2. @LoadBalanced
当使用RestTemplate进行远程服务调用时,假如需要负载均衡,还可以在RestTemplate对象构建时,使用@LoadBalanced对构建RestTemplate的方法进行修饰,例如在ConsumerApplication中构建名字为loadBalancedRestTemplate的RestTemplate对象:
@Bean
@LoadBalanced
public RestTemplate loadBalancedRestTemplate(){
return new RestTemplate();
}
在需要RestTemplate实现负载均衡调用的地方进行依赖注入.例如在ConsumerController类中添加loadBalancedRestTemplate属性
@Autowired
private RestTemplate loadBalancedRestTemplate;
接下来,可以在对应的服务端调用方的方法内,基于RestTemplate借助服务名进行服务调用, 例如:
@GetMapping("/consumer/doRestEcho3")
public String doRestEcho03(){
String url=String.format("http://%s/provider/echo/%s","sca-provider",appName);
//向服务提供方发起http请求,获取响应数据
return loadBalancedRestTemplate.getForObject(
url,//要请求的服务的地址
String.class);//String.class为请求服务的响应结果类型
}
RestTemplate在发送请求的时候会被LoadBalancerInterceptor拦截,它的作用就是用于RestTemplate的负载均衡,LoadBalancerInterceptor将负载均衡的核心逻辑交给了loadBalancer,核心代码如下所示(了解):
public ClientHttpResponse intercept(final HttpRequest request,
final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
final URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
return this.loadBalancer.execute(serviceName,
requestFactory.createRequest(request, body, execution));
}
@LoadBalanced注解是属于Spring,而不是Ribbon的,Spring在初始化容器的时候,如果检测到Bean被@LoadBalanced注解,Spring会为其设置LoadBalancerInterceptor的拦截器。
3. Ribbon负载均衡策略
基于Ribbon方式的负载均衡,Netflix默认提供了七种负载均衡策略,对于SpringCloud Alibaba解决方案中又提供了NacosRule策略,默认的负载均衡策略是轮训策略。如图所示:
当系统提供的负载均衡策略不能满足我们需求时,我们还可以基于IRule接口自己定义策略.
4. 小节面试分析
1.@Bean注解的作用?
一般用于配置类内部,描述相关方法,用于告诉spring此方法的返回值要交给spring管理,bean的名字默认为方法名,假如需要指定名字可以@Bean(“bean的名字”),最多的应用场景是整合第三方的资源-对象
2.@Autowired注解的作用?
此注解用于描述属性,构造方法,set方法等,用于告诉spring框架,按找一定的规则为属性进行DI操作,默认按属性,方法参数类型查找对应的对象,假如只找到一个,则直接注入,类型多个时还会按照属性名或方法参数名进行值的注入,假如名字也不同,就出报错.
3.Nacos中的负责均衡底层是如何实现的?
通过Ribbon实现,Ribbon中定义了一些负载均衡算法,然后基于这些算法从服务实例中获取一个实例为消费方法提供服务
4.Ribbon 是什么?
Netflix公司提供的负载均衡客户端,一般应用于服务的消费方法
5.Ribbon 可以解决什么问题?
基于负载均衡策略进行服务调用, 所有策略都会实现IRule接口
6.Ribbon 内置的负载策略都有哪些?
8种,可以通过查看IRule接口的实现类进行分析
7.@LoadBalanced的作用是什么?
描述RestTemplate对象,用于告诉Spring框架,在使用RestTempalte进行服务调用时,这个调用过程会被一个拦截器进行拦截,然后在拦截器内部,启动负载均衡策略。
8.我们可以自己定义负载均衡策略吗?
可以,基于IRule接口进行策略定义,也可以参考NacosRule进行实现
三、基于Feign的远程服务调用
背景分析
服务消费方基于rest方式请求服务提供方的服务时,一种直接的方式就是自己拼接url,拼接参数然后实现服务调用,但每次服务调用都需要这样拼接,代码量复杂且不易维护,此时Feign诞生。
1. Feign是什么
Feign 是一种声明式Web服务客户端,底层封装了对Rest技术的应用,通过Feign可以简化服务消费方对远程服务提供方法的调用实现。如图所示:
Feign 最早是由 Netflix 公司进行维护的,后来 Netflix 不再对其进行维护,最终 Feign 由一些社区进行维护,更名为 OpenFeign。
2. Feign应用实践
第一步: 在服务消费方,添加项目依赖(SpringCloud团队基于OpenFeign研发了starter),代码如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
第二步: 在启动类上添加@EnableFeignClients
注解,代码如下:
@EnableFeignClients
@SpringBootApplication
public class ConsumerApplication {…}
第三步: 定义Http请求API,基于此API借助OpenFeign访问远端服务,代码如下:
package com.jt.consumer.service;
@FeignClient(name="sca-provider")//sca-provider为服务提供者名称
public interface RemoteProviderService{
@GetMapping("/provider/echo/{string}")//前提是远端需要有这个服务
public String echoMessage(@PathVariable("string") String string);
}
其中,@FeignClient描述的接口底层会为其创建实现类。
第四步: 创建FeignConsumerController
中并添加feign访问,代码如下:
package com.jt.consumer.controller;
@RestController
@RequestMapping("/consumer/ ")
public class FeignConsumerController {
@Autowired
private RemoteProviderService remoteProviderService;
/**基于feign方式的服务调用*/
@GetMapping("/echo/{msg}")
public String doFeignEcho(@PathVariable String msg){
//基于feign方式进行远端服务调用(前提是服务必须存在)
return remoteProviderService.echoMessage(msg);
}
}
第五步: 启动消费者服务,在浏览器中直接通过feign客户端进行访问,如图所示(反复刷新检测其响应结果):
说明,feign方式的远程服务调用,底层会自动基于ribbon组件实现负载均衡。
3. Feign配置进阶实践
一个服务提供方通常会提供很多资源服务,服务消费方基于同一个服务提供方写了很多服务调用接口,此时假如没有指定contextId,服务启动就会失败,例如,假如在服务消费方再添加一个如下接口,消费方启动时就会启动失败,例如:
@FeignClient(name="sca-provider")
public interface RemoteOtherService {
@GetMapping("/doSomeThing")
public String doSomeThing();
}
其启动异常如下:
The bean 'optimization-user.FeignClientSpecification', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled.
此时我们需要为远程调用服务接口指定一个contextId,作为远程调用服务的唯一标识(这个标识是Bean对象的名字)即可,例如:
@FeignClient(name="sca-provider",contextId="remoteProviderService")//sca-provider为服务提供者名称
interface RemoteProviderService{
@GetMapping("/provider/echo/{string}")//前提是远端需要有这个服务
public String echoMessage(@PathVariable("string") String string);
}
还有,当我们在进行远程服务调用时,假如调用的服务突然不可用了或者调用过程超时了,怎么办呢?一般服务消费端会给出具体的容错方案,例如,在Feign应用中通过FallbackFactory接口的实现类进行默认的相关处理,例如:
第一步: 定义FallbackFactory接口的实现,代码如下:
package com.jt.service.factory;
/**
* 基于此对象处理RemoteProviderService接口调用时出现的服务中断,超时等问题
*/
@Component
public class ProviderFallbackFactory implements FallbackFactory<RemoteProviderService> {
/**
* 此方法会在RemoteProviderService接口服务调用时,出现了异常后执行.
* @param throwable 用于接收异常
*/
@Override
public RemoteProviderService create(Throwable throwable) {
return (msg)->{
return "服务维护中,稍等片刻再访问";
};
}
}
第二步: 在Feign访问接口中应用FallbackFactory对象,例如:
@FeignClient(name = "sca-provider", contextId = "remoteProviderService",
fallbackFactory = ProviderFallbackFactory.class)//sca-provider为nacos中的服务名
public interface RemoteProviderService {
@GetMapping("/provider/echo/{msg}")
public String echoMsg(@PathVariable String msg);
}
第三步: 在配置文件application.yml中添加如下配置,启动feign方式调用时的服务中断处理机制.
feign:
hystrix:
enabled: true #默认值为false
第四步: 在服务提供方对应的调用方法中添加Thread.sleep(5000)模拟耗时操作,然后启动服务进行访问测试.
4. Feign 调用过程分析
Feign应用过程分析(底层逻辑先了解):
- 通过 @EnableFeignCleints 注解告诉springcloud,启动 Feign Starter 组件。
- Feign Starter 会在项目启动过程中注册全局配置,扫描包下所由@FeignClient注解描述的接口,然后由系统底层创建接口实现类(JDK代理类),并构建类的对象,然后交给spring管理(注册 IOC 容器)。
- Feign接口被调用时,底层代理对象会将接口中的请求信息通过编码器创建 Request对象,基于此对象进行远程过程调用。
- Feign客户端请求对象会经Ribbon进行负载均衡,挑选出一个健康的 Server 实例(instance)。
- Feign客户端会携带 Request 调用远端服务并返回一个响应。
- Feign客户端对象对Response信息进行解析然后返回客户端。
四、Nacos配置快速入门
业务描述
在sca-provider项目中添加一个Controller对象,例如ProviderLogController,基于此Controller中的方法演示日志级别的配置。
1. 配置准备工作
第一步: 创建ProviderLogController
对象,例如:
package com.jt.provider.controller;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 基于此controller演示配置中心的作用.
* 在这个controller中我们会基于日志对象
* 进行日志输出测试.
*/
//@Slf4j
@RestController
public class ProviderLogController {
//创建一个日志对象
//org.slf4j.Logger (Java中的日志API规范,基于这个规范有Log4J,Logback等日志库)
//org.slf4j.LoggerFactory
//log对象在哪个类中创建,getLogger方法中的就传入哪个类的字节码对象
//记住:以后只要Java中使用日志对象,你就采用下面之中方式创建即可.
//假如在log对象所在的类上使用了@Slf4j注解,log不再需要我们手动创建,lombok会帮我们创建
private static Logger log=
LoggerFactory.getLogger(ProviderLogController.class);
@GetMapping("/provider/log/doLog01")
public String doLog01(){//trace<debug<info<warn<error
System.out.println("==doLog01==");
log.trace("===trace===");
log.debug("===debug===");
log.info("===info====");
log.warn("===warn===");
log.error("===error===");
return "log config test";
}
}
第二步: 在已有的sca-provider项目中添加如配置依赖,例如:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
第三步: 将项目sca-provider的application.yml的名字修改为bootstrap.yml(启动优先级最高),并添加配置中心配置,代码如下:
spring:
application:
name: sca-provider
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: yml # Configure the data format of the content, default to properties
2. 新建Nacos配置
打开nacos配置中心,新建配置,如图所示:
其中,Data ID的值要与bootstrap.yml中定义的spring.application.name的值相同(服务名-假如有多个服务一般会创建多个配置实例,不同服务对应不同的配置实例)。配置发布以后,会在配置列表中,显示我们的配置,例如:
3. 测试Nacos数据读取
配置创建好以后,启动sca-provider服务,然后打开浏览器,输入http://localhost:8081/provider/log/doLog01,检测idea控制台日志输出。然后再打开nacos控制台动态更新日志级别,再访问资源并检测后台日志输出.
然后,修改nacos配置中心的日志级别,再刷新浏览器,检测日志的输出,是否会发生变化.
4. @RefreshScope注解的应用
对于nacos配置中心而言,有系统内部对配置变化的感知,还有外部系统对配置的感知,假如我们系统在浏览器中能看到日志级别的变化,该如何实现呢?我们现在来实现一个案例.
第一步: 在ProviderLogController类的上面添加一个@RefreshScope注解,例如:
@RefreshScope
@RestController
public class ProviderLogController{
//.....
}
其中,@RefreshScope的作用是在配置中心的相关配置发生变化以后,能够及时看到类中属性值的更新(底层是通过重新创建Controller对象的方式,对属性进行了重新初始化)。
第二步: 添加ProviderLogController
中添加一个获取日志级别(debug<info<warn<error
)的的属性和方法,代码如下:
@Value("${logging.level.com.jt:error}")
private String logLevel;
@GetMapping("/provider/log/doLog02")
public String doLog02(){
log.info("log level is {}",logLevel);
return "log level is "+logLevel;
}
5. 小节面试分析
1.配置中心一般都会配置什么内容?
可能会经常变化的配置信息,例如连接池,日志、线程池、限流熔断规则
2.什么信息一般不会写到配置中心?
服务端口,服务名,服务的注册地址,配置中心
3.项目中为什么要定义bootstrap.yml文件?
此文件被读取的优先级比较高,可以在服务启动时读取配置中心的数据
4.Nacos配置中心宕机了,我们的服务还可以读取到配置信息吗?
可以从内存,客户端获取了配置中心的配置信息以后,会将配置信息在本地内存中存储一份.
5.微服务应用中我们的客户端如何获取配置中心的信息?
我们的服务一般首先会从内存读取配置信息,同时我们的微服务还可以定时向nacos配置中心发请求拉取(pull)更新的配置信息
6.微服务应用中客户端如何感知配置中心数据变化?
1.4.x版本的nacos客户端会基于长轮询机制从nacos获取配置信息,所谓的长轮询就是没有配置更新时,会在nacos服务端的队列进行等待.
7.服务启动后没有从配置中心获取我们的配置数据是什么原因?
依赖,配置文件名字bootstrap.yml,配置中心的dataId名字是否正确,分组是否正确,配置的名字是否正确,缩进关系是否正确,假如是动态发布,类上是否有@RefreshScope注解
8.你项目中使用的日志规范是什么?
SLF4J
9.你了解项目中的日志级别吗?
debug,info,error,…,可以基于日志级别控制日志的输出
五、Nacos配置管理模型
1. 概述
Nacos 配置管理模型由三部分构成,如图所示:
其中:
- Namespace:命名空间,对不同的环境进⾏隔离,⽐如隔离开发环境和⽣产环境。
- Group:分组,将若⼲个服务或者若⼲个配置集归为⼀组。
- Service/DataId:某⼀个服务或配置集,一般对应一个配置文件。
2. 命名空间设计
Nacos中的命名空间一般用于配置隔离,这种命名空间的定义一般会按照环境(开发,生产等环境)进行设计和实现.我们默认创建的配置都存储到了public命名空间,如图所示:
创建新的开发环境并定义其配置,然后从开发环境的配置中读取配置信息,该如何实现呢?
第一步: 创建新命名空间,如图所示:
命名空间成功创建以后,会在如下列表进行呈现。
在指定命名空间下添加配置,也可以直接取配置列表中克隆,例如:
克隆成功以后,我们会发现在指定的命名空间中有了我们克隆的配置,如图所示:
此时我们修改dev命名空间中Data Id的sca-provider配置,如图所示:
修改项目module中的配置文件bootstrap.yml,添加如下配置,关键代码如下:
spring:
cloud:
nacos:
config:
namespace: 6058fd3f-0d4d-44f2-85d6-5fc7d2348046
……
其中,namespace后面的字符串为命名空间的id,可直接从命名空间列表中进行拷贝.然后重启服务,继续刷新http://localhost:8081/provider/log/doLog02地址。检测输出,看看输出的内容是什么,是否为dev命名空间下配置的内容。
3. 分组设计及实现
当我们在指定命名空间下,按环境或服务做好了配置以后,有时还需要基于服务做分组配置,例如,一个服务在不同时间节点(节假日,活动等)切换不同的配置,可以在新建配置时指定分组名称,如图所示:
其中,这里的useLocalCache为自己定义的配置值,表示是否使用本地缓存.
配置发布以后,修改boostrap.yml配置类,在其内部指定我们刚刚创建的分组,代码如下:
server:
port: 8081
spring:
application:
name: sca-provider
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
group: DEFAULT_GROUP_51 # Group, default is DEFAULT_GROUP
file-extension: yml # Configure the data format of the content, default to properties
namespace: 7da4aa75-f64c-43c6-b101-9d77ad96f1c0
在指定的Controller类中添加属性和方法用于获取和输出DEFAULT_GROUP_51中的useLocalCache的值,代码如下:
package com.jt.provider.controller;
@RefreshScope
@RestController
public class ProviderCacheController {
@Value("${useLocalCache:false}")
private boolean useLocalCache;
@RequestMapping("/provider/cache01")
public String doUseLocalCache01(){
return "useLocalCache'value is "+useLocalCache;
}
}
然后重启服务,进行访问测试,检测内容输出。
4. 共享配置设计及读取
当同一个namespace的多个配置文件中都有相同配置时,可以对这些配置进行提取,然后存储到nacos配置中心的一个或多个指定配置文件,哪个微服务需要,就在服务的配置中设置读取即可。例如:
第一步: 在nacos中创建一个共享配置文件,例如:
其中,这里的secret可以理解为一个密钥。
第二步: 在指定的微服务配置文件(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.yml
refresh: true #默认false,共享配置更新,引用此配置的地方是否要更新
第三步: 在指定的Controller类中读取和应用共享配置即可,例如:
package com.jt.provider.controller;
@RefreshScope
@RestController
public class ProviderSecretController {
@Value("${app.secret:123456}")
private String secret;
@GetMapping("/provider/secret")
public String doGetSecret(){
//return String.format()
return "The Secret is "+secret;
}
}
**第四步:**启动服务,然后打开浏览器进行访问测试。
5. 小节面试分析
1.Nacos配置管理模型的背景?
环境不同配置不同
2.Nacos配置中的管理模型是怎样的?
namespace,group,service/data-id
3.Nacos客户端(微服务)是否可以读取共享配置?
可以
6. 总结(Summary)
重难点分析
- 配置中心的选型。(市场活跃度、稳定性、性能、易用)
- Nacos配置中心基本应用。(新建,修改、删除配置以后,在Nacos客户端应用配置)
- 配置管理模型应用。(namespace,group,service/dataId)
- Nacos配置变更的动态感知。(底层原理分析)
FAQ分析
- 为什么需要配置中心?(动态管理发布配置,无需重启服务,更好保证服务的可用)
- 配置中一般要配置什么内容?(经常变化的配置数据-日志级别,线程池、连接池、…)
- 市面上有哪些主流配置中心?(Nacos,….)
- 配置中心选型时要重点考虑哪些因素?(市场活跃度、稳定性、性能、易用)
- Nacos客户端(微服务业务)如何动态感知配置中心数据变化的?(nacos2.0之前nacos客户端采用长轮询机制每隔30秒拉取nacos配置信息.)
- Nacos配置管理模型是怎样的?(命名空间-namespace,分组-group,服务实例-dataId)