Spring Cloud 是一套基于 Spring Boot 的框架聚集,用于构建分布式微服务架构。它提供了一系列工具和库,资助开辟者更轻松地管理分布式系统中的关键问题,比如服务注册与发现、负载均衡、分布式配置管理、熔断与降级、链路追踪等。
下图展示了微服务架构中每个主要功能模块的常用办理方案。
一、相关功能的介绍
1. 服务注册与发现
服务注册:服务注册与发现用于让各个服务在启动时自动注册到一个中心注册中心(如 Nacos、Eureka),并且能让其他服务通过注册中心找到并调用它们的地址。
发现:每个服务启动后会将自身的地址和端口信息注册到注册中心;其他服务要调用它时,通过注册中心获取服务实例的地址,而不需要固定的地址。
2. 服务调用和负载均衡
服务调用:服务之间的通信方式,可以通过 HTTP(如 RESTful API)或 RPC(远程过程调用)举行服务之间的哀求。
负载均衡:在微服务架构中,通常会有多个雷同的服务实例分布在不同的服务器上。负载均衡用于在多个实例间分配哀求,常见的计谋有轮询、随机、最小连接数等,从而提升系统的处理能力和容错性。
3. 分布式事务
分布式事务用于保证多个服务在处理同一个业务利用时的一致性。例如,用户下单时,需要支付服务和库存服务同时完成,如果某一方失败,整个利用需要回滚。
4. 服务熔断和降级
服务熔断:用于防止一个服务的故障传导到其他服务。如果某个服务在短时间内出现大量的错误或相应缓慢,熔断机制会自动切断对该服务的调用,避免对系统造成更大影响。
服务降级:在服务出现问题时,提供降级计谋,比如返回默认值或简化相应内容,使系统能够在部门服务不可用的情况下继续运行。
5. 服务链路追踪
服务链路追踪用于跟踪分布式系统中一次哀求的完整路径,分析其跨多个服务的执行情况,方便发现延长或错误。
6. 服务网关
服务网关作为服务的同一入口,处理所有外部哀求,提供认证授权、负载均衡、路由分发、监控等功能。它还能对哀求举行限流、熔断、降级等保护。
7. 分布式配置管理
分布式配置管理用于集中管理各服务的配置文件,支持动态更新,不需要重启服务。 可以在配置更新后自动推送至各服务节点,使它们能实时更新配置信息,提升了系统的机动性和一致性。
二、前置内容和预备工作
1、 不同服务之间的调用
下面两个相识其中一个就行
- RestTemplate
RestTemplate 是 Spring 提供的一个同步 HTTP 客户端,用于与 RESTful Web 服务举行交互。它支持多种 HTTP 方法,包括 GET、POST、PUT、DELETE 等,简化了调用 REST API 的过程。
在微服务架构中,服务之间需要举行通信,RestTemplate 通过简单的 HTTP 调用实现这一点。通过服务注册和发现机制,可以动态获取服务的地址和端口。
配置RestTemplate
- 确保 pom.xml 文件中包罗了 Spring Web 相关的依赖:
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
复制代码 - 创建 RestTemplate Bean
在 Spring Boot 应用程序中,通常在配置类中创建一个 RestTemplate 的 Bean:- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.web.client.RestTemplate;
- @Configuration
- public class AppConfig {
- @Bean
- public RestTemplate restTemplate() {
- return new RestTemplate();
- }
- }
复制代码 RestTemplate常用方法
- GET 哀求
- restTemplate.getForObject(url, String.class);
- // getForObject(String url, Class<T> responseType):发起 GET 请求,并返回响应体。
复制代码 - POST 哀求
- restTemplate.postForObject(url, request, MyResponseObject.class);
- // postForObject(String url, Object request, Class<T> responseType):发起 POST 请求,将请求体发送给指定 URL,并返回响应体。
复制代码 - PUT 哀求
- restTemplate.put(url, request);
- // put(String url, Object request):发起 PUT 请求,将请求体发送到指定 URL。
复制代码 - DELETE 哀求
- restTemplate.delete(url);
- // delete(String url):发起 DELETE 请求。
复制代码
- OpenFeign
Feign 是一个声明式的 Web 服务客户端,它简化了与 HTTP 服务交互的方式。使得开辟职员能够通过简单的注解方式调用 RESTful Web 服务,而不需要手动编写繁琐的 HTTP 哀求代码。(RestTemplate和这个相识一种即可)
引入依赖
- 如果在使用 Spring Cloud,可以在你的 pom.xml 中加入 Feign 相关的依赖:
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-openfeign</artifactId>
- </dependency>
复制代码 - 启用 Feign:
在 Spring Boot 应用的主类或者配置类中添加 @EnableFeignClients 注解,启用 Feign 客户端:- import org.springframework.cloud.openfeign.EnableFeignClients;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- @SpringBootApplication
- @EnableFeignClients // 启用 Feign 客户端
- public class MyApplication {
- public static void main(String[] args) {
- SpringApplication.run(MyApplication.class, args);
- }
- }
复制代码 - 定义 Feign 接口:
Feign 使用接口定义 HTTP 哀求。通过注解指定哀求的类型和路径:- import org.springframework.cloud.openfeign.FeignClient;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RequestParam;
- @FeignClient(name = "account-service") // 指定服务名称, 这里是指注册到naocs的服务名
- public interface AccountClient {
- @GetMapping("/account/balance")
- String getBalance(@RequestParam("userId") String userId);
- }
复制代码 在上面的例子中:
- @FeignClient(name = "account-service") 表现 Feign 客户端将向名为 account-service 的服务发起哀求。
- @GetMapping("/account/balance") 表现该方法会向 /account/balance 路径发送 GET 哀求,并返回相应。
- 调用 Feign 客户端:
在其他服务中调用 Feign 客户端接口,就像调用本地方法一样:- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
- @RestController
- public class OrderController {
- @Autowired
- private AccountClient accountClient;
- @RequestMapping("/order/test")
- public String createOrder(String userId) {
- // 调用 Feign 客户端方法
- String balance = accountClient.getBalance(userId);
- return "Account balance: " + balance;
- }
- }
复制代码
2、 预备工作(引入spring cloud依赖)
1. dependencyManagement
dependencyManagement 是 Maven 构建工具中的一个元素,用于定义项目中依赖的管理方式。它允许我们在父 POM 文件或依赖管理部门中集中声明依赖的版本号和作用范围,所有子模块(子项目)可以自动继承这些声明,而不需要在每个子模块的 pom.xml 中重复定义。
- 同一依赖版本管理:
dependencyManagement 能够资助你同一管理多个模块中某个依赖的版本。例如,你可以在一个中心位置(通常是父 POM 文件)声明 Spring Boot 的版本号,如许所有子项目都会使用这个版本,而不需要每个项目中都定义。
- 简化子项目中的依赖声明:
子项目无需在每个 pom.xml 文件中声明依赖的版本号,只需要定义依赖的 groupId 和 artifactId,版本号将从 dependencyManagement 中继承。
根项目 pom 文件- <packaging>pom</packaging>
- <dependencyManagement>
- <dependencies>
-
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-dependencies</artifactId>
- <version>2023.0.3</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
-
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-alibaba-dependencies</artifactId>
- <version>2023.0.1.3</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- </dependencies>
- </dependencyManagement>
复制代码 子项目 pom 文件- <parent>
- <groupId>com.cloud</groupId>
- <artifactId>learnCloudAlibaba</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
复制代码 2. 引入依赖 spring Cloud
maven官网
- Spring Cloud Dependencies
2. 引入依赖 spring Cloud alibaba
- spring-cloud-alibaba-dependencies
3. 版本兼容性问题
- 参考文章 地址
这里我引入的是 springcloud 2023 , springcloudalibaba 2023, springboot 3.2, jdk 17
下面解说一下每个主要功能模块的部署和配置
三、服务注册与发现 nacos
1. 预备工作
- 下载 nacos 本地服务 (地址)
- 添加依赖到子模块
在需要被nacos注册的模块中加入下面配置,启动项目即可在 localhost:8848/nacos 中查看到已经被注册到中心注册中心- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
- </dependency>
复制代码 - 简单配置
- spring.cloud.nacos.discovery.server-addr=localhost:8848
复制代码 - 在启动类上加上@EnableDiscoveryClient注解
@EnableDiscoveryClient 是一个注解,用于启用服务注册与发现功能,通常在使用 Spring Cloud 和 Nacos 的服务中添加。它告诉 Spring Boot 应用要注册到服务注册中心,以便其他服务能够发现它。
2. 以单例启动
解压进入nacos的 bin目录,以单例模式启动- .\startup.cmd -m standalone
- # localhost:8848/nacos 进行访问, 默认账号密码都是 nacos
复制代码 3. 常见配置
- spring.cloud.nacos.discovery.namespace=命名空间id # 指定命令空间, 不同空间下的实例不可互相访问
- spring.cloud.nacos.discovery.group=DEFAULT_GROUP # 默认是DEFAULT_GROUP,指定group,不同group下的实例不可互相访问
- spring.cloud.nacos.discovery.cluster-name=BeiJing # 指定当前实例是哪个集群,一般按照地区划分,讲请求发送距离近的实例
- spring.cloud.loadbalancer.nacos.enabled=true # 开启 优先向跟当前发送请求的实例 在同一集群的实例发送请求
- spring.cloud.nacos.discovery.weight=1 # 当前实例的权值,权值在1-100,默认是1,权值越大越容易接收请求,一般给配置高的服务器权值高一些
复制代码 4. Nacos 集群架构
对于 Nacos 集群,主要的作用是 实现高可用和数据一致性,保证服务注册和配置管理的可靠性。
- 集群架构
Nacos 集群通常包罗多个节点,部署在不同的机器或虚拟机上,以提供服务注册、配置管理的冗余和高可用性。当一个节点发生故障时,其他节点可以继续提供服务,从而保证系统的稳固运行。
- 数据一致性(RAFT 协议)
Nacos 集群内部使用 RAFT 协议 来实现服务数据的强一致性。这种一致性保证了同一服务的多个实例在集群中都能被正确地注册和发现。(在集群中的任意以nacos中注册,即可被整个集群的nacos访问)
- Leader 选举:在 Nacos 集群中,一个节点会被选举为 Leader,其它节点作为 Follower。Leader 负责处理写哀求并同步数据到 Follower 节点。
- 数据同步:当服务实例的注册、注销或配置变动等写哀求发生时,Leader 会将数据同步到所有 Follower,确保数据在集群中的一致性。
- 高可用性和故障恢复
- 当 Leader 节点故障时,集群中的其他节点会重新选举一个新的 Leader,继续处理写哀求和同步数据。
- Follower 节点接收 Leader 的数据更新哀求并定期与 Leader 举行心跳检测,以保证集群稳固运行。
可以通过nginx反向代理,实现只暴漏一个nacos服务地址,nginx内容实现负载均衡
也可以通过loadbalancer或是在 application 中添加集群的所有地址实现简单的负载均衡- # 会选其中一个地址注册服务
- spring.cloud.nacos.discovery.server-addr: 172.20.10.2:8870,172.20.10.2:8860,172.20.10.2:8848
复制代码 5. 集群模式部署
- 配置数据库
Nacos 集群需要一个共享的数据库来存储配置信息。可以使用 MySQL 作为存储引擎。
- 在 MySQL 中创建一个数据库:
- CREATE DATABASE nacos_config;
复制代码 - 进入mysql,执行 Nacos 提供的 SQL 脚本:
- mysql> use nacos_config;
- Database changed
- mysql> source D:\kafka\nacos\conf\mysql-schema.sql
复制代码
- 配置 Nacos 集群
打开每个节点的 conf/application.properties 文件,举行以下配置:- server.port=8848
- spring.datasource.platform=mysql
- spring.sql.init.platform=mysql
- ### Count of DB:
- db.num=1
- db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_cofig?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
- db.user.0=root
- db.password.0=password
复制代码 打开每个节点的 conf/cluster.conf 文件,举行以下配置:- 172.20.10.2:8848 # 前面是ip地址,内网的或是公网的
- 172.20.10.2:8860
- 172.20.10.2:8870
复制代码 一些坑
这里我要在本地启动三个nacos,那就需要复制 nacos 文件夹,然后分别修改内里的 application.properties 和 cluster.conf 的配置文件,其中 server.port=8848 端口之间不要离太近。(离太近nacos 2.0 版本会出问题)这里弄成了 8848, 8860, 8870 端口
- 启动 Nacos 实例
在每台服务器上启动 Nacos 服务。执行以下下令:注意:每个节点启动时,cluster.conf 文件中需要列出所有节点的 IP 和端口。
四、服务调用和负载均衡 LoadBalancer
Spring Cloud LoadBalancer 是 Spring Cloud 中的一个负载均衡模块,用于在服务调用时实现客户端负载均衡。
1. 基本概念
Spring Cloud LoadBalancer 通过客户端负载均衡,在服务调用者和多个实例之间分配流量。它通过服务发现(比如使用 Nacos)获取可用服务实例的列表,并根据不同的负载均衡计谋(如轮询、随机等)选择一个实例举行哀求分发。
2. 配置情况
在项目中使用 Spring Cloud LoadBalancer,在每个需要使用客户端负载均衡功能的子模块中添加:- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-loadbalancer</artifactId>
- </dependency>
复制代码 配置默认计谋(轮询)- spring.cloud.loadbalancer.configurations=default
复制代码 3. 负载均衡的使用方式
Spring Cloud LoadBalancer 支持使用 RestTemplate 、WebClient 、OpenFeign 举行负载均衡。
使用 RestTemplate
- 定义 RestTemplate Bean 并标注 @LoadBalanced 注解:
- @Configuration
- public class AppConfig {
-
- @Bean
- @LoadBalanced // 启用 RestTemplate 的负载均衡
- public RestTemplate restTemplate() {
- return new RestTemplate();
- }
- }
复制代码 - 发起哀求
使用 @LoadBalanced 的 RestTemplate 时,可以直接通过服务名称调用服务,而不需要手动指定服务的 IP 地址和端口,避免了ip和端口写死, 只向一个实例发送哀求的情况。
(我们同一个服务名称一般会有多个实例(分布式), 通过带有 @LoadBalanced注解的 RestTemplate 可以实现负载均衡, 让哀求根据我们的配置分别发送到不同的实例)- @Autowired
- private RestTemplate restTemplate;
- public String callService() {
- // 使用服务名代替实际地址
- return restTemplate.getForObject("http://module2/api/v1/data", String.class);
- }
复制代码 4. 测试 负载均衡
假设我们有一个根模块, 根模块下面有三个模块 module1,module2,module3。module2 和 module3 我们在本地的不同端口各启动两个(模拟分布式)。当我们通过module1调用 module2 和 module3 的方法,观察哀求的分布
- 将多个实例注册到nacos
这里演示的nacos用的单例模式,终极结果如下图
下面是如何启动让module2和module3分别启动两个实例
- 查看哀求分布
三个module中的demo代码
module1 中,通过get方法访问 /test 时,会向 module2 和 module3 的实例发送哀求。(spring cloud loadbalancer 会根据配置实现负载均衡)- @Autowired
- RestTemplate restTemplate;
- @GetMapping("/test")
- public String test() {
- String module2 = restTemplate.getForObject("http://module2/api/test", String.class);
- String module3 = restTemplate.getForObject("http://module3/api/test", String.class);
- return module2 + "\n" + module3;
- }
复制代码 module2 中- @GetMapping("/api/test")
- public String test() {
- System.out.println("module2 test");
- return "module2 test";
- }
复制代码 module3 中- @GetMapping("/api/test")
- public String test() {
- System.out.println("module3 test");
- return "module3 test";
- }
复制代码 当我们在欣赏器访问 16 次 /test 的时候, restTemplate.getForObject 会向对应的module发送16次哀求。观察module2的两个实例和module3的两个实例可以看到 分别接收到了 8 次哀求(默认是轮询)。如下图
5. 使用不同的负载均衡器
上面的配置是所有服务都是用默认的负载均衡器,即轮询的负载均衡器。下面讲一下怎么让不同服务使用不同的负载均衡器
- 创建两个配置文件,把轮询负载均衡器和随机负载均衡器注册为bean
- @Configuration
- public class DefaultLoadBalancerConfig {
- @Bean
- ReactorLoadBalancer<ServiceInstance> roundRobinLoadBalancer(Environment environment,
- LoadBalancerClientFactory loadBalancerClientFactory) {
- String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); // 获取负载均衡器名称
- return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
- }
- }
复制代码- @Configuration
- public class RandomLoadBalancerConfig {
- @Bean
- // 定义一个Bean
- ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, // 注入环境变量
- LoadBalancerClientFactory loadBalancerClientFactory) { // 注入负载均衡器客户端工厂
- String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); // 获取负载均衡器的名称
- // 创建并返回一个随机负载均衡器实例
- return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
- }
- }
复制代码 - 创建restTempalte
- // module2 使用默认,module3 使用随机
- @Configuration
- @LoadBalancerClients({
- @LoadBalancerClient(value = "module2", configuration = RandomLoadBalancerConfig.class),
- @LoadBalancerClient(value = "module3", configuration = DefaultLoadBalancerConfig.class)
- })
- public class AppConfig {
- @Bean
- @LoadBalanced // 启用 RestTemplate 的负载均衡
- public RestTemplate restTemplate() {
- return new RestTemplate();
- }
- }
复制代码 五、分布式事务 seata
Seata 是一款开源的分布式事务办理方案,旨在办理微服务架构中跨服务的事务一致性问题。它提供了易于使用、性能高效的分布式事务管理功能,资助开辟者在分布式系统中保持数据一致性。
1. Seata 的概要
Seata 由阿里巴巴发起,最初的目标是为了办理微服务场景下的数据一致性问题,尤其是在分布式数据库事务中。Seata 提供了全局事务、分支事务以及资源管理等功能。
- 全局事务:在分布式事务中,通常一个事务大概会涉及多个服务或数据库,Seata 通过全局事务 ID 来保证跨服务事务的原子性。
- 分支事务:每个服务或数据库在执行全局事务时会变成一个分支事务,Seata 负责和谐各个分支事务的提交与回滚。
- AT模式:Seata 提供了基于数据库的 AT (Automatic Transaction) 模式,它通过对数据库的预存利用和恢复利用来实现事务的一致性。
- TCC模式:TCC (Try, Confirm, Cancel) 是 Seata 支持的另一种事务模式,适用于需要显式利用的场景。
- SAGA模式:SAGA 是另一种事务模子,适合于长事务和复杂业务场景。
- RM(ResourceManager):用于直接执行本地事务的提交和回滚。
- TM(TransactionManager):TM是分布式事务的核心管理者。比如现在我们需要在借阅服务中开启全局事务,来让其自身、图书服务、用户服务都参与进来,也就是说一般全局事务发起者就是TM。
- TC(TransactionManager)这个就是我们的Seata服务器,用于全局控制,一个分布式事务的启动就是由TM向TC发起哀求,TC再来与其他的RM举行和谐利用
TM哀求TC开启一个全局事务,TC会天生一个XID作为该全局事务的编号,XID会在微服务的调用链路中传播,保证将多个微服务的子事务关联在一起;RM哀求TC将本地事务注册为全局事务的分支事务,通过全局事务的XID举行关联;TM哀求TC告诉XID对应的全局事务是举行提交还是回滚;TC驱动RM将XID对应的本身的本地事务举行提交还是回滚;
下面以官网的案例来演示整个使用过程
2. 预备工作
- 下载seata服务(tc)
下载seata-service压缩包,解压进入/bin (下载地址), 执行 ./seata-server.bat
- 配置 Seata:
在 conf 目录下,你会找到一个配置文件 application.yml。编辑该文件以配置 Seata Server 的各种参数,比如数据库连接、事务日志等。- server:
- port: 7091
-
- spring:
- application:
- name: seata-server
-
- logging:
- config: classpath:logback-spring.xml
- file:
- path: ${log.home:${user.home}/logs/seata}
- extend:
- logstash-appender:
- destination: 127.0.0.1:4560
- kafka-appender:
- bootstrap-servers: 127.0.0.1:9092
- topic: logback_to_logstash
-
- console:
- user:
- username: seata
- password: seata
- seata:
- config:
- # support: nacos, consul, apollo, zk, etcd3
- type: nacos
- nacos:
- server-addr: 127.0.0.1:8848
- namespace:
- group: SEATA_GROUP
- username: nacos
- password: nacos
- registry:
- # support: nacos, eureka, redis, zk, consul, etcd3, sofa
- type: nacos
- nacos:
- application: seata-server
- server-addr: 127.0.0.1:8848
- group: SEATA_GROUP
- namespace:
- cluster: default
- username: nacos
- password: nacos
- store:
- # support: file 、 db 、 redis 、 raft
- mode: db
- db:
- datasource: druid
- db-type: mysql
- driver-class-name: com.mysql.jdbc.Driver
- url: jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true
- user: mysql
- password: mysql
- min-conn: 10
- max-conn: 100
- global-table: global_table
- branch-table: branch_table
- lock-table: lock_table
- distributed-lock-table: distributed_lock
- query-limit: 1000
- max-wait: 5000
- # server:
- # service-port: 8091 #If not configured, the default is '${server.port} + 1000'
- security:
- secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
- tokenValidityInMilliseconds: 1800000
- ignore:
- urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login,/metadata/v1/**
复制代码 - 数据库配置
创建 seata 数据库,并访问 该地址,在seata数据库中执行内里所有的sql脚本
创建 seata_account, seata_order, seata_storage数据库,访问 该地址, 在上面三个数据库中都创建 UNDO_LOG 表,在对应的数据库中创建对应的 业务表。(地址中有sql脚本)
- 启动 Seata Server:
使用以下下令启动 Seata Server: 终极结果如下

创建三个模块(account, order, storage),加入相关依赖(数据库驱动,mybatis...),然后按照下面加入 nacos 和 seata 依赖和配置
3. Seata 与 Spring Boot 集成
3.1 添加 Seata 依赖
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
- <exclusions>
- <exclusion>
- <groupId>io.seata</groupId>
- <artifactId>seata-spring-boot-starter</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
复制代码 3.2 配置 Seata
在 Spring Boot 项目标 application.yml 中配置 Seata。- seata:
- registry:
- type: nacos
- nacos:
- server-addr: localhost:8848
- namespace: ""
- group: SEATA_GROUP # 组名 跟我们之前配置的seata的配置文件的是对应的
- application: seata-server # 服务名 跟我们之前配置的seata的配置文件的是对应的
- tx-service-group: default_tx_group
- service:
- vgroup-mapping:
- default_tx_group: default
- data-source-proxy-mode: AT
复制代码 3.3 开启事务管理
@GlobalTransactional 是 Seata 提供的注解,用于实现分布式事务的管理。它是 Seata 的全局事务控制器,通过这个注解,你可以在一个跨多个微服务的利用中,确保数据的一致性和事务的回滚。
作用:
- 开启全局事务:使用 @GlobalTransactional 注解可以标记方法为全局事务,Seata 会在这个方法执行时开启一个全局事务。
- 事务的提交与回滚:在方法执行过程中,如果发生异常,Seata 会自动回滚所有与该全局事务相关的子事务。相反,如果方法执行成功,Seata 会提交所有子事务。
基本语法:
- @GlobalTransactional(name = "your-global-tx-name", rollbackFor = Exception.class)
- public void yourMethod() {
- // Your business logic
- }
复制代码 参数:
- name:指定全局事务的名称,通常为了区分不同的事务,可以给它一个有意义的名字。
- rollbackFor:指定哪些异常类型会导致事务回滚,默认是 RuntimeException 和 Error,如果需要捕捉其他异常,可以通过此参数指定。
4. 演示流程
下面是通过访问 order 订单服务,然后执行 创建新的订单-> 扣除用户支付的钱 -> 减去用户购买商品的数量更新商品库存- @RestController
- public class CreateOrderController {
- @Autowired
- OrderMapper orderMapper;
- @Autowired
- RestTemplate restTemplate;
- @RequestMapping("/order/test")
- @GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
- public String createOrder(@RequestParam String userId,
- @RequestParam String commodityCode,
- @RequestParam Integer count,
- @RequestParam Integer money
- ) {
- // 创建订单
- Order order = new Order(null, userId, commodityCode, count, money);
- orderMapper.insert(order);
- // 扣除账户余额
- Map<String, String> mp1 = new HashMap<>();
- mp1.put("userId", userId);
- mp1.put("money", money.toString());
- String resp1 = restTemplate.postForObject("http://localhost:8001/account/test", mp1, String.class);
- // 减去用户购买商品数量,更新库存
- Map<String, String> mp2 = new HashMap<>();
- mp2.put("commodityCode", commodityCode);
- mp2.put("count", count.toString());
- String resp2 = restTemplate.postForObject("http://localhost:8003/storage/test", mp2, String.class);
- if ("ok".equals(resp1) && "ok".equals(resp2)) {
- return "ok";
- }
- return "error";
- }
- }
复制代码 如果在调用其他服务时(扣除账户余额,更新库存时),如果抛出异常的话,整个事务就会回滚。比如减去用户购买商品数量时发现库存不足,抛出异常,整个事务回滚,之前的创建订单和扣除账户余额都会回滚
六、服务熔断和降级 sentinel
Sentinel 是阿里巴巴开源的分布式系统流量控制组件,主要用于保护微服务在高并发、突发流量等场景下的稳固性和可靠性。Sentinel 提供了 流量控制、熔断降级、系统自适应保护等机制
1. Sentinel 的核心功能
Sentinel 提供了以下核心功能:
- 流量控制:根据预定义的规则限定访问频率或数量,避免系统过载。
- 熔断降级:在哀求失败率或相应时间过高时自动降级,防止雪崩效应。
- 系统自适应保护:基于系统的负载情况自动举行保护,如 CPU 使用率、内存占用等。
- 热点参数限流:对哀求中的参数举行限流,比如按不同用户 ID 或商品 ID 限定哀求。
2. Sentinel 的基本概念
在 Sentinel 中,核心是“资源”,可以是服务、接口或方法。每一个资源都会绑定一套限流规则或熔断规则。以下是 Sentinel 的几个基本概念:
- 资源 (Resource):受保护的对象,如 API 接口或数据库利用。
- 规则 (Rule):用于定义流量控制、熔断降级的条件。
- Slot Chain:Sentinel 使用 Slot Chain 机制来应用限流、降级、系统保护等规则。每一个 Slot Chain 包罗多个 Slot,不同 Slot 处理不同的规则。
3. Sentinel 的监控和控制台
Sentinel 提供了 Dashboard 管理控制台,可以用来监控各个资源的访问情况,并动态配置流量控制和熔断规则。(下载地址, 下载jar包)
- 启动 jar 包。
访问 http://localhost:8888/ (这里我设置的端口是 8888) 进入控制台。
账号密码都是 sentinel
- 引入 sentinel
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
- </dependency>
复制代码- spring.cloud.sentinel.transport.dashboard=localhost:8888
复制代码 - 访问module任意controller后就可以看到 module1 加入到控制台
4. @SentinelResource注解
@SentinelResource 是 Sentinel 中用于标记 方法 的注解,它允许你在方法调用时应用 Sentinel 的规则和控制计谋。通过使用这个注解,你可以将 Sentinel 的流量控制、熔断降级、热点参数限流等功能集成到业务逻辑中。
1. value 参数:资源名称
- 作用:指定该方法的 资源名称,这个名称会作为 Sentinel 资源的标识,用来应用流控、熔断、降级等计谋。通常可以是方法名或其他具代表性的字符串。
- 默认值:方法名(如果未指定)。
2. blockHandler 参数:限流或熔断处理方法
- 作用:当资源受到 流量控制(如限流)或 熔断降级(如调用失败)时,会调用指定的 blockHandler 方法。(handelBlock 方法肯定要写上 BlockException ex )
- 类型:blockHandler 的方法可以是当前类中的一个静态方法,或是其他类的静态方法。
示例:- @SentinelResource(value = "myResource", blockHandler = "handleBlock")
- public String someMethod() {
- // 你的业务逻辑
- return "Hello, Sentinel!";
- }
- // 流控或熔断时的处理方法
- public static String handleBlock(BlockException ex) {
- return "Service is currently unavailable due to high traffic. Please try again later.";
- }
复制代码 在这个例子中,当 myResource 资源被流控或熔断时,handleBlock 方法将被调用,返回一条友爱的错误信息。
3. fallback 参数:降级处理方法
- 作用:当资源发生 异常 或 超时 时,触发降级逻辑,会调用指定的 fallback 方法。这个方法需要与原始方法的署名一致。(流量超限或熔断触发时也会调用 fallback,如果同时有 fallback 和 blockHandler, 会优先调用 blockHandler)
- 类型:fallback 的方法可以是当前类中的一个静态方法,或是其他类的静态方法。
示例:- @SentinelResource(value = "myResource", fallback = "fallbackMethod")
- public String someMethod() {
- // 可能会抛出异常的业务逻辑
- throw new RuntimeException("Something went wrong");
- }
- // 降级方法
- public static String fallbackMethod(Throwable ex) {
- return "Service is temporarily unavailable due to internal error. Please try again later.";
- }
复制代码 在这个例子中,当 someMethod 方法抛出异常时,fallbackMethod 会被调用,返回一个默认的降级相应。
4. exceptionsToIgnore 参数:忽略的异常类型
- 作用:指定不触发 降级 或 熔断 的异常类型。即使这些异常发生,也不会进入 fallback 或 blockHandler。
- 默认值:没有忽略的异常,所有的异常都会触发降级逻辑。
5. blockHandlerClass 参数:自定义流控处理类
- 作用:指定一个类,其中包罗 blockHandler 处理方法。如许可以将流控处理方法与业务逻辑分离,便于管理。
- 默认值:如果未指定,blockHandler 方法会在当前类中查找。
示例:- @SentinelResource(value = "myResource", blockHandler = "handleBlock", blockHandlerClass = BlockHandler.class)
- public String someMethod() {
- // 业务逻辑
- return "Hello, Sentinel!";
- }
- // 自定义流控处理类
- public class BlockHandler {
- public static String handleBlock(BlockException ex) {
- return "Service is temporarily unavailable due to traffic control.";
- }
- }
复制代码 6. fallbackClass 参数:自定义降级处理类
- 作用:指定一个类,其中包罗 fallback 处理方法。如许可以将降级处理方法与业务逻辑分离,便于管理。
- 默认值:如果未指定,fallback 方法会在当前类中查找。
示例:- @SentinelResource(value = "myResource", fallback = "fallbackMethod", fallbackClass = FallbackHandler.class)
- public String someMethod() {
- // 业务逻辑
- throw new RuntimeException("Something went wrong");
- }
- // 自定义降级处理类
- public class FallbackHandler {
- public static String fallbackMethod(Throwable ex) {
- return "Service is temporarily unavailable due to an error.";
- }
- }
复制代码 5. 流量控制
5.1 流控模式
- 直接:只针对于当前接口。
- 关联:如果指定的关联接口凌驾qps(每秒的哀求数),会导致当前接口被限流。
- 链路:更细粒度的限流,能精确到具体的方法, 直接和关联只能对接口举行限流
链路流控模式指的是,当从指定接口过来的资源哀求到达限流条件时,开启限流。- # 关闭Context收敛,这样被监控方法可以进行不同链路的单独控制
- spring.cloud.sentinel.web-context-unify: false
- # 比如我 /test1 和 /test2 接口下都调用了 test 方法,那么我可以在sentinel 控制台中分别对 /test1 和 /test2 的 test 进行设置
复制代码
5.2 流控结果
快速拒绝: 既然不再担当新的哀求,那么我们可以直接返回一个拒绝信息,告诉用户访问频率过高。
预热: 依然基于方案一,但是由于某些情况下高并发哀求是在某一时刻突然到来,我们可以缓慢地将阈值进步到指定阈值,形成一个缓冲保护
排队等待: 不担当新的哀求,但是也不直接拒绝,而是进队列先等一下,如果规定时间内能够执行,那么就执行,要是超时就算了。
5.3 实现对方法的限流控制
我们可以使用到@SentinelResource对某一个方法举行限流控制,无论是谁在那边调用了它,,一旦方法被标注,那么就会举行监控。- @RestController
- public class TestController {
- @Autowired
- RestTemplate restTemplate;
- @Autowired
- MyService myService;
- @GetMapping("/test1")
- public String test1() {
- return myService.test();
- }
- @GetMapping("/test2")
- public String test2() {
- return myService.test();
- }
- }
- // ------------------------
- @Service
- public class MyService {
- @SentinelResource("mytest") // 标记方法
- public String test() {
- return "test";
- }
- }
复制代码 添加 spring.cloud.sentinel.web-context-unify=false, 可以对 /test1 和 /test2 的 mytest单独控制
不添加的, 无法单独控制
一些坑
如果在方法上添加了 @SentinelResource 注解,但是不在控制台中显示的话(不显示mytest), 大概是由于添加的方法没有加入到spring容器中举行管理。比如我当时下面如许写就出现了不在控制台显示的情况。- @RestController
- public class TestController {
-
- @GetMapping("/test1")
- public String test1() {
- return test();
- }
-
- @SentinelResource("mytest")
- public String test() {
- return "test";
- }
- }
复制代码 5.4 处理限流情况
当访问某个 接口 出现限流时,会抛出限流异常,重定向到我们添加的路径- @RequestMapping("/blockPage")
- public String blockPage() {
- return "blockPage";
- }
复制代码- # 用于设置当 流控 或 熔断 触发时,返回给用户的 阻塞页面
- spring.cloud.sentinel.block-page=/blockPage
复制代码 当访问的某个 方法 出现了限流,为 blockHander 指定方法,当限流时会访问 test2, 并且可以担当 test 的参数- @Service
- public class MyService {
- @SentinelResource(value = "mytest", blockHandler = "test2")
- public String test(int id) {
- return "test";
- }
- public String test2(int id, BlockException ex) {
- return "test 限流了 " + id;
- }
- }
复制代码 5.5 处理异常的处理
当调用 test 时抛出了异常的话,那么就会执行 fallback 指定的方法, exceptionsToIgnore 指定的是忽略掉的异常。(限流的话会抛出限流的异常,也会被捕捉,执行 fallback 指定的方法)
如果 fallback 和 blockHandler 都指定了方法,出现限流异常会优先执行 blockHandler 的方法- @Service
- public class MyService {
- @SentinelResource(value = "mytest", fallback = "except", exceptionsToIgnore = IOException.class)
- public String test(int id) {
- throw new RuntimeException("hello world");
- }
- // 注意参数, 必须是跟 test 的参数列表相同, 然后可以多加一个获取异常的参数
- public String except(int id, Throwable ex) {
- System.out.println("hello = " + ex.getMessage());
- return ex.getMessage();
- }
- }
复制代码 5.6 热点参数限流
对接口或方法的某个参数举行限流- @GetMapping("/test1")
- @SentinelResource(value = "test1")
- public String test1(@RequestParam(required = false) String name, @RequestParam(required = false) String age) {
- return name + " " + age;
- }
复制代码
6. 熔断降级
1. 服务熔断
当某个服务的错误率或失败次数(如异常或超时)凌驾预设的阈值时,熔断器就会“断开”,该服务的调用将不会继续执行。熔断器“断开”后,所有哀求都会被 拒绝,直到熔断器进入 恢复阶段,然后根据预设规则恢复正常工作。
2. 熔断规则
熔断规则定义了什么情况下触发熔断。常见的触发条件有:
- 异常比例(异常比例熔断):当某个资源的调用异常(如超时、返回错误等)比例凌驾设定的阈值时,触发熔断。
- 错误数(错误数熔断):当某个资源的调用错误次数凌驾设定的阈值时,触发熔断。
- RT(相应时间熔断):当某个资源的调用相应时间凌驾设定的阈值时,触发熔断。
这些规则是可以配置的,通常会在应用初始化时举行设置。
- 2.2 熔断恢复机制
熔断后,Sentinel 会进入 自恢复机制,通过设定的时间窗口,逐渐恢复正常的服务调用。这一恢复过程通常包括以下两个阶段:
- 半开状态:熔断器进入半开状态,允许肯定命量的哀求尝试访问该资源。此时系统会验证该资源是否已恢复,若恢复则正常调用,若失败则继续熔断。
- 正常状态:如果半开状态中的哀求均成功,则熔断器恢复为正常状态,恢复正常的哀求处理。
3. 服务降级 (Service Degradation)
服务降级是指在某些情况下,通过返回一个默认值、简单处理或快速失败来淘汰服务的压力,避免因资源超载或异常哀求导致服务崩溃。Sentinel 通过配置不同的降级计谋,使得系统能够在流量激增或服务不稳固时自动切换到降级模式。
最后简单介绍一下限流,熔断,降级之间的接洽。 根据降级的概念,当出现限流或熔断的时候都会触发降级的方法,只不过熔断会根据本身的配置,来跟熔断的服务断开接洽,不再担当哀求。而限流的话不会断开服务,而是继续担当哀求,如果哀求不满意 限流规则的话,还是会进入到降级的方法
七、服务链路追踪
在zipkin官网下载 zipkin.jar 包。下载地址
Micrometer Tracing 是 Spring 官方在现代可观测性(Observability)体系中的新工具,用于实现分布式链路追踪。
1. 核心概念
- Span
- 每个 Span 表现一个利用的执行过程(如调用某个方法、处理一个 HTTP 哀求)。
- 包罗利用的开始时间、连续时间、名称等信息。
- Trace 是由多个 Span 组成的分布式调用链。
- Trace
- 表现分布式系统中多个服务协作完成的团体调用链。
- 包括一个根 Span 和多个子 Span。
- Context Propagation
- 在分布式系统中,需要在服务之间传递上下文(如 traceId 和 spanId),以实现跨服务的追踪。
- Micrometer Tracing 通过支持 W3C Trace Context 标准(OpenTelemetry 默认标准)完成上下文传递。
2. 工作原理
- 天生和传播追踪信息
- 服务 A 天生 traceId 和 spanId,并通过 HTTP header 或消息队列传递给服务 B。
- 服务 B 接收这些信息,继续天生新的 span 并关联到 trace。
- 采样计谋
- 决定是否记录 trace 数据(例如只采样部门哀求以淘汰性能开销)。
- Micrometer Tracing 提供多种采样计谋,可以通过配置控制。
- 导出追踪数据
- Micrometer Tracing 支持将追踪数据导出到多个后端(如 Jaeger、Zipkin 或 Prometheus)。这里我们用 zipkin
- 通过 OpenTelemetry Bridge 提供对多种观察后端的支持。
3. 简单配置
在 Spring Boot 3+ 项目中,Micrometer Tracing 的依赖配置如下:
- 引入依赖
在 pom.xml 中添加以下依赖:- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-actuator</artifactId>
- </dependency>
- <dependency>
- <groupId>io.micrometer</groupId>
- <artifactId>micrometer-tracing</artifactId>
- </dependency>
- <dependency>
- <groupId>io.micrometer</groupId>
- <artifactId>micrometer-tracing-bridge-brave</artifactId>
- </dependency>
- <dependency>
- <groupId>io.micrometer</groupId>
- <artifactId>micrometer-observation</artifactId>
- </dependency>
- <dependency>
- <groupId>io.zipkin.reporter2</groupId>
- <artifactId>zipkin-reporter-brave</artifactId>
- </dependency>
复制代码 - 配置
- management:
- zipkin:
- tracing:
- endpoint: http://localhost:9411/api/v2/spans
- tracing:
- sampling:
- probability: 1.0 #采样率默认0.1(10次只能有一次被记录),值越大手机越及时
复制代码 之后就可以访问 http://127.0.0.1:9411
八、服务网关 gateway
Gateway(网关)是微服务架构中的一个重要组件,它通常用作客户端和多个微服务之间的中介,负责哀求的路由、负载均衡、认证、限流、安全控制等功能。它通常部署在前端,起到了“入口”作用,是微服务的前端同一访问点。
1. 网关的核心功能
网关的核心职责是将外部哀求路由到相应的微服务,同时提供一些重要的功能:
- 哀求路由: 网关根据哀求的路径、哀求头、参数等信息,将哀求转发到对应的微服务。
- 负载均衡: 网关能够实现哀求的负载均衡,将哀求分发到多个后端服务实例,提升服务的可用性和性能。
- 安全性: 网关通常是整个系统的第一道防线,可以举行哀求的身份验证、授权控制、加密等。
- 服务发现: 通过与服务注册中心集成,网关可以动态地获取微服务的实例信息,实现动态路由。
- 过滤器: 允许在哀求处理过程中添加自定义逻辑。过滤器分为“全局过滤器”和“局部过滤器”。
- 动态路由: 可以动态添加路由规则,无需重新启动网关。
2. 预备工作
- 引入依赖
- org.springframework.cloud spring-cloud-starter-gateway com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery<dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-loadbalancer</artifactId>
- </dependency>
复制代码 - 配置路由规则:
- spring:
- application:
- name: gateway
- cloud:
- nacos:
- discovery:
- server-addr: localhost:8848
- gateway:
- # 配置路由,这里是一个列表,每一项都包含很多信息
- routes:
- - id: module1 # 路由名称(nacos中的服务名)
- uri: lb://module1 # 路由地址,lb表示使用负载均衡到微服务,也可以使用http正常转发
- predicates: # 路由规则,决定哪些请求会被路由
- - Path=/test1/** # 请求的路径匹配规则
- filters: # 向请求头中添加 test=hello world
- - AddRequestHeader=test, hello world
- server:
- port: 8002
复制代码 在 Spring Cloud Gateway 中,predicates 和 filters 是配置路由规则和处理哀求
常见的 predicates(路由匹配条件):
- Path:根据哀求路径举行匹配
- Path=/users/**:匹配以 /users/ 开头的路径。
- 示例:/users/123、/users/details。
- Method:根据 HTTP 方法举行匹配
- Method=GET:只匹配 GET 哀求。
- Method=POST:只匹配 POST 哀求。
- 示例:GET /users/123、POST /users。
- Host:根据哀求的 Host 举行匹配
- Host=*.example.com:匹配所有哀求的 Host 名称为 example.com 的哀求。
- 示例:GET /users 哀求的 Host 为 api.example.com。
- Query:根据查询参数举行匹配
- Query=username={value}:匹配查询参数 username 的值。
- 示例:GET /users?username=john。
- Header:根据哀求头举行匹配
- Header=Authorization=Bearer {token}:匹配带有特定 Authorization 头的哀求。
- 示例:GET /users/123,并且 Authorization=Bearer 。
常见的 filters(过滤器):
filters 用于在哀求和相应之间举行处理,通常用于修改哀求头、相应体、重定向等。这里的过滤器是局部的过滤器
- AddRequestHeader:添加哀求头
- AddRequestHeader=X-Request-Foo, Bar:向哀求中添加 X-Request-Foo 头,值为 Bar。
- 示例:哀求中会包罗 X-Request-Foo: Bar。
- AddResponseHeader:添加相应头
- AddResponseHeader=X-Response-Foo, Baz:向相应中添加 X-Response-Foo 头,值为 Baz。
- SetPath:修改哀求路径
- SetPath=/newpath/{segment}:将哀求的路径设置为新的路径。
- 示例:哀求 /users/123 会被设置为 /newpath/123。
- RedirectTo:重定向哀求到其他地址
- RedirectTo=301, /new-location:将哀求重定向到 /new-location。
- 示例:会发出 301 重定向到 /new-location。
上面的predicates和filters只写了一部门,具体可以参考spring官网 地址
Spring Cloud Gateway 与其他网关对比
- 与 Nginx 对比: Nginx 是一个高性能的 Web 服务器,可以作为反向代理和负载均衡器。虽然 Nginx 可以用作网关,但它不提供像 Spring Cloud Gateway 那样丰富的业务逻辑处理能力(如动态路由、API 聚合、过滤器等)。Spring Cloud Gateway 更多地专注于微服务架构中的业务需求。
3. 自定义全局和局部过滤器
过滤器是网关的一个重要特性,可以在哀求和相应的生命周期中做一些额外的处理。
- 全局过滤器(Global Filter):全局过滤器可以处理所有哀求和相应。你可以在全局过滤器中添加日志记录、认证、限流等利用。
- 局部过滤器(Gateway Filter):局部过滤器是针对某个特定路由的过滤器。你可以在路由配置中使用这些过滤器举行特定利用。
3.1 自定义全局过滤器
在 Spring Cloud Gateway 中,全局过滤器(Global Filters)用于在哀求和相应过程中对所有路由举行处理。
- 过滤器的作用:
- 哀求过滤: 在哀求到达后端微服务之前对哀求做一些处理,比如增加哀求头、日志记录、权限校验等。
- 相应过滤: 在相应从后端微服务返回到客户端之前对相应做一些修改,比如修改相应内容、加密、日志记录等。
1. 创建一个全局过滤器
首先,需要创建一个实现 GlobalFilter 接口的类。在这个类中,你可以定义过滤器的逻辑。- import org.springframework.cloud.gateway.filter.GatewayFilterChain;
- import org.springframework.cloud.gateway.filter.GlobalFilter;
- import org.springframework.core.Ordered;
- import org.springframework.http.HttpHeaders;
- import org.springframework.stereotype.Component;
- import org.springframework.web.server.ServerWebExchange;
- import reactor.core.publisher.Mono;
- @Component
- public class AddHeaderGlobalFilter implements GlobalFilter, Ordered {
- @Override
- public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
- return chain.filter(exchange);
- }
- @Override
- public int getOrder() {
- return 1;
- }
- }
复制代码 2. 全局过滤器的工作原理
- filter():在这里你可以获取到 ServerWebExchange 对象,它包罗了哀求和相应的所有信息。你可以在这里利用哀求和相应的内容,举行一些预处理或后处理。最后,调用 chain.filter(exchange) 以传递哀求继续向下执行其他过滤器或路由。
- getOrder():返回一个整数值,用来决定过滤器的执行次序。值越小的过滤器会优先执行。一般来说,数字越小的过滤器会在哀求刚开始时执行,而数字大的过滤器会在哀求的最后阶段执行。
- 如果你有多个全局过滤器,它们会按照 getOrder() 返回值的次序执行。
3. 示例:添加哀求头
假设我们需要为每个哀求添加一个特定的哀求头。- package com.cloud.gateway.config;
- import org.springframework.cloud.gateway.filter.GatewayFilterChain;
- import org.springframework.cloud.gateway.filter.GlobalFilter;
- import org.springframework.core.Ordered;
- import org.springframework.http.HttpHeaders;
- import org.springframework.stereotype.Component;
- import org.springframework.web.server.ServerWebExchange;
- import reactor.core.publisher.Mono;
- @Component
- public class AddHeaderGlobalFilter implements GlobalFilter, Ordered {
- @Override
- public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
- // 获取请求的 headers
- HttpHeaders headers = exchange.getRequest().getHeaders();
- // 打印原始请求头
- System.out.println("Request Headers: " + headers);
- // 为请求添加一个新的头部
- exchange.getRequest().mutate()
- .header("test", "hello world") // 添加请求头
- .build();
- // 继续传递到下一个过滤器
- return chain.filter(exchange);
- }
- @Override
- public int getOrder() {
- return 1;
- }
- }
复制代码 3.1 自定义局部过滤器
自定义局部过滤器(GatewayFilter):
- import org.springframework.cloud.gateway.filter.GatewayFilter;
- import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
- import org.springframework.http.HttpStatus;
- import org.springframework.stereotype.Component;
- @Component
- public class AuthFilter extends AbstractGatewayFilterFactory<AuthFilter.Config> {
- public AuthFilter () {
- super(Config.class);
- }
- @Override
- public GatewayFilter apply(Config config) {
- return (exchange, chain) -> {
- System.out.println("AuthFilter");
- // 下面实现自己的逻辑
- String token = exchange.getRequest().getHeaders().getFirst("token");
- if (token == null || !token.equals(config.getToken())) {
- exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
- return exchange.getResponse().setComplete();
- }
- return chain.filter(exchange); // 继续处理链中的其他过滤器
- };
- }
- public static class Config {
- private String token;
- public String getToken() {
- return token;
- }
- public void setToken(String token) {
- this.token = token;
- }
- }
- }
复制代码 使用局部过滤器
局部过滤器通常在路由配置中使用,你可以将它应用于特定的路由,例如:- spring:
- cloud:
- gateway:
- routes:
- - id: user-service
- uri: lb://USER-SERVICE
- predicates:
- - Path=/users/**
- filters:
- - AuthFilter # 这里引用自定义的局部过滤器
复制代码 九、分布式配置管理 nacos
分布式配置管理功能的主要作用是在不同的服务之间集中管理和同一分发配置。这使得系统在配置变动时无需重启服务,可以实时更新配置,从而到达快速相应的结果。
基本概念
- Data ID(数据 ID):表现每个配置的唯一标识。在 Nacos 中,一个配置项通常用 Data ID 表现,通常为字符串情势,代表唯一的配置文件名。
- Group(组):用于将不同的配置项举行分组管理,方便区分开辟、测试、生产情况等场景。
- Namespace(命名空间):用于逻辑隔离配置数据。不同命名空间内的配置是互相隔离的,这在多租户场景中非常有用。
- 配置项:每个具体的配置信息称为配置项,可以是一个或多个键值对。
Nacos 配置管理的使用步骤
引入 Nacos 配置管理
- 引入依赖
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-bootstrap</artifactId>
- </dependency>
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
- </dependency>
复制代码 - 配置 Nacos 服务器地址
在 bootstrap.yml 文件中,设置 Nacos 配置中心地址和必要的配置信息:- spring:
- application:
- name: module1
- profiles:
- active: dev
-
- cloud:
- nacos:
- config:
- server-addr: localhost:8848 # 服务地址
- file-extension: yaml
复制代码 data_id 一般命名接纳 application.name-profiles.active.filex-extension,根据上面的配置,我的dataid就是 module1-dev.yaml
- 获取配置
可以使用 Spring Boot 的 @Value 注解来获取 Nacos 中的配置项。例如:
- @RestController
- @RefreshScope
- public class TestNacosConfigController {
- @Value("${test}") // 获取到 test 的值
- private String test;
- @GetMapping("/nacos/config")
- public String nacosConfig() {
- return test;
- }
- }
复制代码 - 动态刷新配置
使用 @RefreshScope 注解,自动刷新配置:(当我们配置中心修改时,不需要重启项目,test 就会自动更新)- @RestController
- @RefreshScope
- public class TestController {
- @Value("${test}")
- private String test;
- @GetMapping("/nacos/config")
- public String getConfig() {
- return test;
- }
- }
复制代码 我踩的一些坑:
- 最好是新建一个 bootstrap.yaml 文件写nacos config的配置,不要直接在 application 中写,不然会遇到奇怪的bug。
- bootstrap.yaml 中肯定还要写上 spring: application: name: module1 profiles: active: dev 这些东西,即使你在 application 中已经写了
- 如果都按照上面的要求做了,但是就是无法通过 @Value 获取到配置属性的话,可以尝试低落 spring-cloud-starter-alibaba-nacos-config 的版本。
这里我学习的时候就遇到了,通过第一个配置(2023.0.1.3)死活获取不到 test,但是用第二个配置(低落版本的),其他的都没改,就可以获取到。- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
- </dependency>
复制代码- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
- </dependency> 2023.0.1.2
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |