本文是基于【雷丰阳老师:尚硅谷2025最新SpringCloud - 快速通关】进行实践操作,并对雷神的笔记做一个更详细的补充,供大家学习参考,一起加油!
视频地址:SpringCloud快速通关_教程简介_哔哩哔哩_bilibili
SpringCloud-快速通关(一)
一、分布式基础
1.1、微服务
微服务架构风格,就像是把一个单独的应用程序开发为一套小服务,每个小服务运行在自己的进程中,并使用轻量级机制通信,通常是 HTTP API。这些服务围绕业务能力来构建, 并通过完全自动化部署机制来独立部署。这些服务使用不同的编程语言书写,以及不同数据存储技术,并保持最低限度的集中式管理。
简而言之:拒绝大型单体应用,基于业务边界进行服务微化拆分,各个服务独立部署运行。
1.2、集群&分布式&节点
集群是个物理形态,分布式是个工作方式。
只要是一堆机器,就可以叫集群,他们是不是一起协作着干活,这个谁也不知道;
《分布式系统原理与范型》定义:
- “分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统”
- 分布式系统(distributed system)是建立在网络之上的软件系统。
分布式是指将不同的业务分布在不同的地方。
集群指的是将几台服务器集中在一起,实现同一业务。
例如:京东是一个分布式系统,众多业务运行在不同的机器,所有业务构成一个大型的业务集群。每一个小的业务,比如用户系统,访问压力大的时候一台服务器是不够的。我们就应该将用户系统部署到多个服务器,也就是每一个业务系统也可以做集群化;
分布式中的每一个节点,都可以做集群。 而集群并不一定就是分布式的。
节点:集群中的一个服务器
1.3、远程调用
在分布式系统中,各个服务可能处于不同主机,但是服务之间不可避免的需要互相调用,我们称为远程调用。 SpringCloud 中使用 HTTP+JSON
的方式完成远程调用
1.4、负载均衡
分布式系统中,A 服务需要调用 B 服务,B 服务在多台机器中都存在,A 调用任意一个服务器均可完成功能。 为了使每一个服务器都不要太忙或者太闲,我们可以负载均衡的调用每一个服务器,提升网站的健壮性。
常见的负载均衡算法:
- 轮询:为第一个请求选择健康池中的第一个后端服务器,然后按顺序往后依次选择,直到最后一个,然后循环。
- 最小连接:优先选择连接数最少,也就是压力最小的后端服务器,在会话较长的情况下可以考虑采取这种方式。
- 散列:根据请求源的 IP 的散列(hash)来选择要转发的服务器。这种方式可以一定程度上保证特定用户能连接到相同的服务器。如果你的应用需要处理状态而要求用户能连接到和之前相同的服务器,可以考虑采取这种方式。
1.5、服务注册/发现&注册中心
A 服务调用 B 服务,A 服务并不知道 B 服务当前在哪几台服务器有,哪些正常的,哪些服务已经下线。解决这个问题可以引入注册中心;
如果某些服务下线,我们其他人可以实时的感知到其他服务的状态,从而避免调用不可用的服务。
1.6、配置中心
每一个服务最终都有大量的配置,并且每个服务都可能部署在多台机器上。我们经常需要变更配置,我们可以让每个服务在配置中心获取自己的配置。
配置中心用来集中管理微服务的配置信息
1.7、服务熔断&服务降级
在微服务架构中,微服务之间通过网络进行通信,存在相互依赖,当其中一个服务不可用时,有可能会造成雪崩效应。要防止这样的情况,必须要有容错机制来保护服务。
1)、服务熔断
设置服务的超时,当被调用的服务经常失败到达某个阈值,我们可以开启断路保护机制,后来的请求不再去调用这个服务。本地直接返回默认的数据
2)、服务降级
在运维期间,当系统处于高峰期,系统资源紧张,我们可以让非核心业务降级运行。降级:某些服务不处理,或者简单处理【抛异常、返回 NULL、调用 Mock 数据、调用 Fallback 处理逻辑】
1.8、API 网关
在微服务架构中,API Gateway 作为整体架构的重要组件,它抽象了微服务中都需要的公共功能,同时提供了客户端负载均衡,服务自动熔断,灰度发布,统一认证,限流流控,日志统计等丰富的功能,帮助我们解决很多 API 管理难题。
二、Spring Cloud
2.1、简介
Spring Cloud是分布式系统一站式解决方案。
什么是分布式系统?
架构分:单体和分布式。集群只是一种物理形态,分布式是工作方式。
架构演进 | 单体架构 | 集群架构 | 分布式架构 |
---|---|---|---|
定义 | 所有功能模块都在一个项目里 | 单体的多服务器版本 | 一个大型应用被拆分成很多小应用分布部署在各个机器; |
优点 | 开发部署简单 | 解决大并发 | 解决了单体+集群的问题 |
缺点 | 无法应对高并发 | 问题1:模块块升级 麻烦 问题2:多语言团队 交互不通 |
基于自己的理解:分布式架构(模拟用户访问)
- 通过网关来发送各个微服务的请求(请求路由)。用
gateway
。网关需要对请求进行分发,所以要注册到注册中心。 - 将各微服务布置到各服务器,即微服务(自治) 独立部署、数据隔离、语言无关,将不同模块部署到多个服务器,每个模块都要有副本服务器。不能让每个模块只部署到一个服务器,会出现单点故障问题:如果这个服务器崩了,那应用就不能提供完整服务了
- 如果模块跨服务器之间调用会遇到什么问题?远程调用RPC。如果远程调用怎么让应用知道调用哪个服务器的微服务。此时就需要用到nacos注册中心和配置中心,注册中心有两个功能:服务注册(监控服务上下线)和服务发现(远程调用之前要发现对方在哪)。配置中心:统一管理配置文件+推送配置的变更。
Nacos+OpenFeign
- 如果模块之间调用失败导致服务调用链整体阻塞甚至雪崩,怎么办?服务熔断(快速失败机制),及时释放资源,防止资源耗尽。
Sentinal
- 如果有一个操作需要多个数据库合作,而不同数据库部署在不同服务器,这就需要用到分布式事务。
Seata
2.2、技术配置
Spring Cloud 系列:
- 官网:https://spring.io/projects/spring-cloud
- 远程调用:OpenFeign
- 网关:Gateway
Spring Cloud Alibaba 系列:
- 官网:https://sca.aliyun.com/
- 注册中心/配置中心:Nacos
- 服务保护:Sentinel
- 分布式事务:Seata
2.3、版本
2.4、实践
2.4.1、建springcloud-demo项目
先用手脚架快速搭建框架
2.4.2、导依赖
pom父模块
注意:springboot, springcloud, springcloud-alibaba的版本
<?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>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.atguigu</groupId>
<artifactId>spring-cloud-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-cloud.version>2023.0.3</spring-cloud.version>
<spring-cloud-alibaba.version>2023.0.3.2</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2.4.3、建services模块
services模块作为管理所有service-xxx 模块的父模块
导入依赖,在service中导入nacos-discovery
依赖
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
2.4.4、建service-order/product模块
注意父模块是services
三、Nacos - 注册/配置中心
3.1、基础入门
官网:https://nacos.io/zh-cn/docs/v2/quickstart/quick-start.html
Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
下载nacos的服务端:nacos server 账号密码都是nacos
安装:
Docker 安装
docker run -d -p 8848:8848 -p 9848:9848 -e MODE=standalone --name nacos nacos/nacos-server:v2.4.3
下载软件包:nacos-server-2.4.3.zip
启动:
startup.cmd -m standalone
启动成功
3.2、注册中心
3.2.1、依赖引入
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
3.2.2、整合配置
1、在 application.properties
中配置如下
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
#暂未用到配置中心功能,需要关闭配置检查
#spring.cloud.nacos.config.import-check.enabled=false
2、开启服务注册/发现功能
@EnableDiscoveryClient //核心注解
@SpringBootApplication
public class OrderMainApplication {
public static void main(String[] args) {
SpringApplication.run(OrderMainApplication.class, args);
}
}
3.2.3、服务注册
作用:将微服务注册到nacos中进行统一管理
service-order, service-product都加依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
service-order模块
application.properties
server.port=8000
spring.application.name=service-order
spring.cloud.nacos.server-addr=127.0.0.1:8848
- 启动类
@SpringBootApplication
public class OrderMainApplication {
public static void main(String[] args) {
SpringApplication.run(OrderMainApplication.class, args);
}
}
service-product模块
application.properties
server.port=9000
spring.application.name=service-product
spring.cloud.nacos.server-addr=127.0.0.1:8848
- 启动类
@SpringBootApplication
public class ProductMainApplication {
public static void main(String[] args) {
SpringApplication.run(ProductMainApplication.class, args);
}
}
效果
查看效果
访问:http://localhost:8848/nacos
可以看到服务已经注册上来;
启动集群
例如:service-order启动两个,service-product启动3个
order端口:8000/8001
product端口:9000/9001/9002
3.2.4、服务发现
服务发现的作用是:服务间的远程调用通过nacos发现对方的服务,然后进行调用,后续不用手动调,这里只要加上注解,两个API作为了解。
- 启动类加注解
@EnableDiscoveryClient
@EnableDiscoveryClient
@SpringBootApplication
public class ProductMainApplication {
public static void main(String[] args) {
SpringApplication.run(ProductMainApplication.class, args);
}
}
- 测试类:测试类的包和启动类的包保持一致
@SpringBootTest
public class ProductApplicationTest {
@Autowired
DiscoveryClient discoveryClient;
@Autowired
NacosDiscoveryClient nacosDiscoveryClient;//二者效果一样,这个依赖nacos
@Test
public void discoveryClientTest(){
List<String> services = discoveryClient.getServices();
for (String service : services) {
System.out.println("service = " + service);
List<ServiceInstance> instances = discoveryClient.getInstances(service);
for (ServiceInstance instance : instances) {
System.out.println("instance.getHost() = " + instance.getHost());
System.out.println("instance.getPort() = " + instance.getPort());
}
}
}
@Test
public void nacosDiscoveryClientTest(){
List<String> services = nacosDiscoveryClient.getServices();
for (String service : services) {
System.out.println("service = " + service);
List<ServiceInstance> instances = nacosDiscoveryClient.getInstances(service);
for (ServiceInstance instance : instances) {
System.out.println("instance.getHost() = " + instance.getHost());
System.out.println("instance.getPort() = " + instance.getPort());
}
}
}
}
3.2.5、远程调用
新建model模块和services模块平级,实体类的统一管理
导入依赖
<dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>annotationProcessor</scope> </dependency> </dependencies>
实体类:com.atguigu.bean
@Data public class Order { private Long id; private BigDecimal totalAmount; private Long userId; private String nickName; private String address; private List<Product> productList; }
@Data public class Product { private Long id; private BigDecimal price; private String productName; private int num; }
在services的pom文件中导入model,就可以用了
<dependency> <groupId>com.atguigu</groupId> <artifactId>model</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
回到service业务类
- service-product
controller
@RestController public class ProductController { @Autowired ProductService productService; @GetMapping(value = "/productId/{id}") public Product getProductById(@PathVariable("id") Long productId) { Product product = productService.getProductById(productId); return product; } }
service
public interface ProductService { Product getProductById(Long productId); }
@Service public class ProductServiceImpl implements ProductService { @Override public Product getProductById(Long productId) { Product product = new Product(); product.setId(productId); product.setPrice(new BigDecimal("99")); product.setProductName("苹果-" + productId); product.setNum(11); return product; } }
测试:http://localhost:9000/product/2
- service-order
- controller
@RestController public class OrderController { @Autowired OrderService orderService; @GetMapping(value = "/create") public Order createOrder(@RequestParam("userId") Long userId, @RequestParam("productId") Long productId) { Order order = orderService.createOrder(userId, productId); return order; } }
- service
public interface OrderService { Order createOrder(Long userId, Long productId); }
- 此处需要对service-product服务进行远程调用,稍后处理,先测试
@Service public class OrderServiceImpl implements OrderService { @Override public Order createOrder(Long userId, Long productId) { Order order = new Order(); order.setId(1L); //TODO 总金额 order.setTotalAmount(new BigDecimal("0")); order.setUserId(userId); order.setNickName("张三"); order.setAddress("火星"); //TODO 远程查询商品列表 order.setProductList(null); return order; } }
- 可以自动生成getter/setter方法的IDEA插件
- 测试:http://localhost:8000/create?userId=2&productId=23
- controller
完善业务类中远程调用
- service-order
- config:将RestTemplate加入到spring容器
@Configuration public class OrderConfig { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
- service
@Service @Slf4j public class OrderServiceImpl implements OrderService { @Autowired DiscoveryClient discoveryClient; @Autowired RestTemplate restTemplate; @Override public Order createOrder(Long userId, Long productId) { Product product = getProductFromRemote(productId); Order order = new Order(); order.setId(1L); // 总金额=价格*数量 BigDecimal price = product.getPrice();//价格 int num = product.getNum();//数量 order.setTotalAmount(price.multiply(new BigDecimal(num)));//总价 order.setUserId(userId); order.setNickName("张三"); order.setAddress("火星"); // 远程查询商品列表 order.setProductList(Arrays.asList(product)); return order; } //远程调用获取商品信息 public Product getProductFromRemote(Long productId) { //1、获取到商品服务所在的所有机器IP+port List<ServiceInstance> instances = discoveryClient.getInstances("service-product"); ServiceInstance instance = instances.get(0); //远程URL String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/productId/" + productId; log.info("远程请求:{}", url); //2、给远程发送请求 Product product = restTemplate.getForObject(url, Product.class); return product; } }
- 测试:http://localhost:8000/create?userId=2&productId=23
- config:将RestTemplate加入到spring容器
3.2.6、负载均衡
1、使用 LoadBalancerClient
在services模块加入依赖
<!--负载均衡--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency>
测试
前提:只能负载均衡注册到nacos的服务@SpringBootTest public class OrderApplicationTest { @Autowired LoadBalancerClient loadBalancerClient; @Test public void test() { ServiceInstance choose = loadBalancerClient.choose("service-product"); System.out.println("choose.getHost()+choose.getPort() = " + choose.getHost() + ":" + choose.getPort()); choose = loadBalancerClient.choose("service-product"); System.out.println("choose.getHost()+choose.getPort() = " + choose.getHost() + ":" + choose.getPort()); choose = loadBalancerClient.choose("service-product"); System.out.println("choose.getHost()+choose.getPort() = " + choose.getHost() + ":" + choose.getPort()); choose = loadBalancerClient.choose("service-product"); System.out.println("choose.getHost()+choose.getPort() = " + choose.getHost() + ":" + choose.getPort()); choose = loadBalancerClient.choose("service-product"); System.out.println("choose.getHost()+choose.getPort() = " + choose.getHost() + ":" + choose.getPort()); } }
效果
改造service-order 的
OrderServiceImpl
的远程调用product服务的方法@Service @Slf4j public class OrderServiceImpl implements OrderService { @Autowired DiscoveryClient discoveryClient; @Autowired RestTemplate restTemplate; @Autowired LoadBalancerClient loadBalancerClient; @Override public Order createOrder(Long userId, Long productId) { // Product product = this.getProductFromRemote(productId); Product product = this.getProductFromRemoteWithLoadBalance(productId); Order order = new Order(); order.setId(1L); // 总金额=价格*数量 BigDecimal price = product.getPrice();//价格 int num = product.getNum();//数量 order.setTotalAmount(price.multiply(new BigDecimal(num)));//总价 order.setUserId(userId); order.setNickName("张三"); order.setAddress("火星"); // 远程查询商品列表 order.setProductList(Arrays.asList(product)); return order; } //阶段二:加入负载均衡 public Product getProductFromRemoteWithLoadBalance(Long productId) { //1、获取到商品服务所在的所有机器IP+port ServiceInstance choose = loadBalancerClient.choose("service-product"); //远程URL String url = "http://" + choose.getHost() + ":" + choose.getPort() + "/productId/" + productId; log.info("远程请求:{}", url); //2、给远程发送请求 Product product = restTemplate.getForObject(url, Product.class); return product; } //远程调用获取商品信息 public Product getProductFromRemote(Long productId) { //1、获取到商品服务所在的所有机器IP+port List<ServiceInstance> instances = discoveryClient.getInstances("service-product"); ServiceInstance instance = instances.get(0); //远程URL String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/productId/" + productId; log.info("远程请求:{}", url); //2、给远程发送请求 Product product = restTemplate.getForObject(url, Product.class); return product; } }
效果
2、使用@LoadBalanced
注解
config
@Configuration public class OrderConfig { @LoadBalanced //基于注解式的负载均衡 @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
ProductController
@RestController public class ProductController { @Autowired ProductService productService; @GetMapping(value = "/productId/{id}") public Product getProductById(@PathVariable("id") Long productId) { System.out.println("正在远程调用service-product..."); Product product = productService.getProductById(productId); return product; } }
OrderServiceImpl
@Service @Slf4j public class OrderServiceImpl implements OrderService { @Autowired DiscoveryClient discoveryClient; @Autowired RestTemplate restTemplate; @Autowired LoadBalancerClient loadBalancerClient; @Override public Order createOrder(Long userId, Long productId) { // Product product = this.getProductFromRemote(productId); // Product product = this.getProductFromRemoteWithLoadBalance(productId); Product product = this.getProductFromRemoteWithLoadBalanceAnnotation(productId); Order order = new Order(); order.setId(1L); // 总金额=价格*数量 BigDecimal price = product.getPrice();//价格 int num = product.getNum();//数量 order.setTotalAmount(price.multiply(new BigDecimal(num)));//总价 order.setUserId(userId); order.setNickName("张三"); order.setAddress("火星"); // 远程查询商品列表 order.setProductList(Arrays.asList(product)); return order; } //阶段三:于注解的负载均衡 public Product getProductFromRemoteWithLoadBalanceAnnotation(Long productId) { //给远程发送请求;;service-product会被动态替换 String url = "http://service-product/productId/" + productId; Product product = restTemplate.getForObject(url, Product.class); return product; } //阶段二:加入负载均衡 public Product getProductFromRemoteWithLoadBalance(Long productId) { //1、获取到商品服务所在的所有机器IP+port ServiceInstance choose = loadBalancerClient.choose("service-product"); //远程URL String url = "http://" + choose.getHost() + ":" + choose.getPort() + "/productId/" + productId; log.info("远程请求:{}", url); //2、给远程发送请求 Product product = restTemplate.getForObject(url, Product.class); return product; } //远程调用获取商品信息 public Product getProductFromRemote(Long productId) { //1、获取到商品服务所在的所有机器IP+port List<ServiceInstance> instances = discoveryClient.getInstances("service-product"); ServiceInstance instance = instances.get(0); //远程URL String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/productId/" + productId; log.info("远程请求:{}", url); //2、给远程发送请求 Product product = restTemplate.getForObject(url, Product.class); return product; } }
效果:http://localhost:8000/create?userId=2&productId=23
分别在各个service-product打印
思考:注册中心宕机,远程调用还能成功吗?
可以进行实验
实验一:五个服务启动,在nacos中有注册,但是没有执行http://localhost:8000/create?userId=2&productId=23测试,也就是没有经过远程调用,然后将nacos关闭,再调用请求测试
结论:不能调用,因为远程调用由于nacos宕机找不到地址
实验一:五个服务启动,在nacos中有注册,但是执行http://localhost:8000/create?userId=2&productId=23测试,已经经过远程调用,然后将nacos关闭,再调用请求测试
结论:可以调用,因为缓存中有地址。但如果对方服务宕机则也调不通。
原理
第一次远程调用要经过两个步骤:1.拿到nacos服务地址列表 2.给对方服务的某个地址发送请求。
第二次及后续:就会将步骤1省略,已经将地址列表放到缓存中了,即使nacos宕机也能远程调用,并且能负载均衡。
小结
- 使用 RestTemplate 可以获取到远程数据
- 必须精确指定地址和端口
- 如果远程宕机将不可用
3.3、配置中心
3.3.1、整合配置
services导入依赖
<!--配置中心--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
service-order的配置文件application.properties
导入了配置中心的依赖但是没设置(用到)配置中心就会报错,解决办法,关闭自动检查
server.port=8000 spring.application.name=service-order spring.cloud.nacos.server-addr=127.0.0.1:8848 spring.config.import=nacos:service-order.properties #暂未用到配置中心功能,需要关闭配置检查 spring.cloud.nacos.config.import-check.enabled=false
service-product
spring.application.name=service-product spring.cloud.nacos.server-addr=127.0.0.1:8848 #暂未用到配置中心功能,需要关闭配置检查 spring.cloud.nacos.config.import-check.enabled=false
在nacos服务端进行配置
代码测试 order: controller@RestController public class OrderController { @Autowired OrderService orderService; @Value("${order.timeout}") String orderTimeout; @Value("${order.auto-confirm}") String orderAutoConfirm; @GetMapping("/config") public String getConfig() { return "OrderTimeout+OrderAutoConfirm = " + orderTimeout+" : " + orderAutoConfirm; } @GetMapping(value = "/create") public Order createOrder(@RequestParam("userId") Long userId, @RequestParam("productId") Long productId) { Order order = orderService.createOrder(userId, productId); return order; } }
3.3.2、动态刷新
@Value(“${xx}”)
获取配置 +@RefreshScope
实现自动刷新但是会产生一个问题:nacos配置中修改后,不重启服务,发请求不能自动更新修改后的数据
实时更新配置显示的办法:在@RestController
上加@RefreshScope
即可@RefreshScope//自动刷新 @RestController public class OrderController { @Autowired OrderService orderService; @Value("${order.timeout}") String orderTimeout; @Value("${order.auto-confirm}") String orderAutoConfirm; @Autowired OrderProperties orderProperties; @GetMapping("/config") public String config(){ return "order.timeout="+orderProperties.getTimeout()+"; " + "order.auto-confirm="+orderProperties.getAutoConfirm() +";"+ "order.db-url="+orderProperties.getDbUrl(); } }
@ConfigurationProperties
无感自动刷新无需 @RefreshScope,自动绑定配置,动态更新
加com.atguigu.order.properties.OrderProperties
@Component @ConfigurationProperties(value = "order")//配置批量绑定在nacos下,可以无需@RefreshScope就能实现自动刷新 @Data public class OrderProperties { String timeout; String autoConfirm; }
service-order: controller
//@RefreshScope @RestController public class OrderController { @Autowired OrderService orderService; // @Value("${order.timeout}") // String orderTimeout; // @Value("${order.auto-confirm}") // String orderAutoConfirm; @Autowired OrderProperties orderProperties; @GetMapping("/config") public String getConfig() { return "OrderTimeout+OrderAutoConfirm = " + orderProperties.getTimeout() + " : " + orderProperties.getAutoConfirm(); } @GetMapping(value = "/create") public Order createOrder(@RequestParam("userId") Long userId, @RequestParam("productId") Long productId) { Order order = orderService.createOrder(userId, productId); return order; } }
3.3.3、NacosConfigManager 监听配置变化
在启动类中添加ApplicationRunner
实例,是一个一次性任务,项目启动他就会执行
@SpringBootApplication
public class OrderMainApplication {
public static void main(String[] args) {
SpringApplication.run(OrderMainApplication.class, args);
}
//1、项目启动就监听配置文件变化
//2、发生变化后拿到变化值
//3、发送邮件
@Bean
ApplicationRunner applicationRunner(NacosConfigManager nacosConfigManager) {
// return new ApplicationRunner() {
// @Override
// public void run(ApplicationArguments args) throws Exception {
//
// }
// }
return args -> {
//这个监听的服务和application.yml中naocs的配置中心有关
ConfigService configService = nacosConfigManager.getConfigService();
configService.addListener("service-order.properties", "DEFAULT_GROUP", new Listener() {
@Override
public Executor getExecutor() {
return Executors.newFixedThreadPool(4);
}
@Override
public void receiveConfigInfo(String s) {
System.out.println("变化的配置信息:" + s);
System.out.println("邮件通知....");
}
});
System.out.println("=========");
};
}
}
测试效果
思考: Nacos中的数据集 和 application.properties 有相同的 配置项,哪个生效?
以配置中心为准,不然要配置中心干什么
3.3.4、数据隔离
作用:配置中心基于项目激活哪个环境标识,动态指定名称空间,动态加载指定文件和配置
推荐用法
创建几个namespace和Group等方便测试
按需加载,设置application.yml
配置文件,将application.properties
注释掉
server:
port: 8000
spring:
profiles:
active: prod
application:
name: service-order
cloud:
nacos:
server-addr: 127.0.0.1:8848
config:
import-check:
enabled: false
namespace: ${spring.profiles.active:public}
---
spring:
config:
import:
- nacos:common.properties?group=order
- nacos:database.properties?group=order
activate:
on-profile: dev
---
spring:
config:
import:
- nacos:common.properties?group=order
- nacos:database.properties?group=order
- nacos:haha.properties?group=order
activate:
on-profile: test
---
spring:
config:
import:
- nacos:common.properties?group=order
- nacos:database.properties?group=order
- nacos:hehe.properties?group=order
activate:
on-profile: prod
代码层面
OrderProperties
@Component
@ConfigurationProperties(value = "order")//配置批量绑定在nacos下,可以无需@RefreshScope就能实现自动刷新
@Data
public class OrderProperties {
String timeout;
String autoConfirm;
String dbUrl;
}
OrderController
//@RefreshScope
@RestController
public class OrderController {
@Autowired
OrderService orderService;
// @Value("${order.timeout}")
// String orderTimeout;
// @Value("${order.auto-confirm}")
// String orderAutoConfirm;
@Autowired
OrderProperties orderProperties;
@GetMapping("/config")
public String getConfig() {
return orderProperties.getTimeout() + " : " + orderProperties.getAutoConfirm() + " : " + orderProperties.getDbUrl();
}
@GetMapping(value = "/create")
public Order createOrder(@RequestParam("userId") Long userId, @RequestParam("productId") Long productId) {
Order order = orderService.createOrder(userId, productId);
return order;
}
}
测试效果
3.4、小结
- 多环境(public, dev, test, prod)=》用namespace管理
- 多服务(order, product)=》group
- 多配置(xxx.properties)=》具体xxx.properties
- 多配置项
参考链接:
https://blog.csdn.net/weixin_56884174/article/details/145573890
https://www.yuque.com/leifengyang/sutong/oz4gbyh5maa0rmxu#tHTwd