SpringCloud
GateWay:
网关是微服务架构的统一入口,承担流量入口、路由转发、通用防护三大核心职责,简化前端调用,统一管控后端服务
两种依赖:
spring-cloud-starter-gateway:原生响应式网关,基于 Reactive Server,无 Web 容器,适合高并发场景
spring-cloud-starter-gateway-mvc:基于 MVC 模式的网关适配,兼容传统 Web 技术栈,降低迁移成本
创建网关:
引入依赖:
<dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency> </dependencies>配置文件:
spring: application: name: gateway cloud: nacos: server-addr: 127.0.0.1:8848 server: port: 80路由:
规则配置:
需求:客户端发送 /api/order/** 转到 service-order,发送 /api/product/** 转到 service-product
spring: cloud: gateway: # 网关路由规则配置 routes: # 订单服务路由(唯一标识,自定义) - id: order-route # 转发目标:通过Nacos负载均衡到service-order服务 uri: lb://service-order # 路径匹配规则:匹配所有/api/order开头的请求 predicates: - Path=/api/order/** # 商品服务路由(唯一标识,自定义) - id: product-route # 转发目标:通过Nacos负载均衡到service-product服务 uri: lb://service-product # 路径匹配规则:匹配所有/api/product开头的请求 predicates: - Path=/api/product/**route 配置中可写的参数:
负载均衡:
工作原理:
请求进入:客户端请求首先到达 Gateway 网关
路由匹配:
GatewayHandlerMapping 读取所有配置的 Route 路由规则,通过 Predicate(断言)判断当前请求符合哪条路由;匹配成功后,确定该路由对应的目标 URI(如 lb://service-order)和关联的 Filter 过滤器链
过滤链处理:
GatewayWebHandler 拿到匹配的路由后,将请求交给过滤器链(Filter Chain)处理
过滤器按顺序执行:
后置处理:目标服务返回响应后,过滤器链反向执行,对响应做修改(如添加响应头、脱敏等)
转发请求:经过所有前置过滤器后,请求被转发到目标服务(目的地)
前置处理:在请求转发到目的地前,对请求做修改(如添加请求头、去掉路径前缀、鉴权等)
响应返回:处理后的响应最终原路返回给客户端
规则有序:
此时访问订单会访问不通且跳转:
数字越小优先级越高:
断言:
断言是路由匹配规则,用于判断客户端请求是否满足当前路由的条件
长短写法:
断言定义:
长写法:
内置断言:
各种断言规则:
演示 Query:
可以写的参数:
必须匹配所有断言规则:
自定义断言工厂:
VipRoutePredicateFactory:
@Component public class VipRoutePredicateFactory extends AbstractRoutePredicateFactory<VipRoutePredicateFactory.Config> { //构造器指定 Config 类型 public VipRoutePredicateFactory(){ super(Config.class); } //简写配置字段顺序 @Override public List<String> shortcutFieldOrder(){ return Arrays.asList("param","value"); } //断言逻辑 @Override public Predicate<ServerWebExchange> apply(Config config) { return new GatewayPredicate() { @Override public boolean test(ServerWebExchange serverWebExchange) { ServerHttpRequest request = serverWebExchange.getRequest(); String first = request.getQueryParams().getFirst(config.param); return StringUtils.hasText(first) && first.equals(config.value); } }; } /** * 可配置的参数 */ @Validated public static class Config{ @NotEmpty private String param; @NotEmpty private String value; public @NotEmpty String getParam(){ return param; } public void setParam(@NotEmpty String param){ this.param = param; } public @NotEmpty String getValue(){ return value; } public void setValue(@NotEmpty String value){ this.value = value; } } }测试:
过滤器:
过滤器是请求处理逻辑,用于在请求转发前、响应返回后对数据进行修改与增强
内置过滤器:
路径重写(RewritePath):
添加响应头(AddResponseHeader):
添加过滤器前:
添加过滤器后:
默认(Default ):
添加过滤器前:
添加过滤器后:
全局(Global):
添加依赖:
@Slf4j @Component public class RtGlobalFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); String uri = request.getURI().toString(); long start = System.currentTimeMillis(); log.info("请求 {} 开始:时间:{}", uri, start); //以上是前置逻辑 Mono<Void> filter = chain.filter(exchange) .doFinally((result) -> { //以下是后置逻辑 long end = System.currentTimeMillis(); log.info("请求 {} 结束:时间:{},耗时:{}ms", uri, end, end - start); });//放行 return filter; } @Override public int getOrder() { return 0; } }自定义过滤器工厂:
@Component public class OnceTokenGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory { @Override public GatewayFilter apply(NameValueConfig config) { return new GatewayFilter() { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //每次响应之前,添加一个一次性令牌,支持 uuid,jwt等各种格式 return chain.filter(exchange).then(Mono.fromRunnable(() -> { ServerHttpResponse response = exchange.getResponse(); HttpHeaders headers = response.getHeaders(); String value = config.getValue(); if ("uuid".equalsIgnoreCase(value)) { value = UUID.randomUUID().toString(); } if ("jwt".equalsIgnoreCase(value)) { value = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiw"; } headers.add(config.getName(), value); })); } }; } }全局跨域:
在前后端分离的开发模式中,前端给后端发送请求经常会出现跨域问题
(如果是在单体项目情况下,可以给 Controller 上标注跨域注解 @CrossOrigin)
在网关上设置统一跨域:
面试题:微服务之间的调用过网关吗
微服务之间的调用通常不会经过网关,而是直接通过服务发现组件(如 Nacos/Eureka)实现点对点通信以提升性能,网关只给外部客户端(浏览器 / APP) 用;
实现过网关:让服务调用网关的地址,由网关转发到目标服务,就能让微服务之间的调用经过网关,但多一层转发会增加延迟、降低性能
Seata:
Seata 是微服务分布式事务框架,核心 TC / TM / RM 三角色,主流用 AT 模式实现无侵入最终一致性,保证跨服务事务要么全部成功,要么全部回滚
环境搭建:
Sql 文件:
CREATE DATABASE IF NOT EXISTS `storage_db`; USE `storage_db`; DROP TABLE IF EXISTS `storage_tbl`; CREATE TABLE `storage_tbl` ( `id` int(11) NOT NULL AUTO_INCREMENT, `commodity_code` varchar(255) DEFAULT NULL, `count` int(11) DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY (`commodity_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO storage_tbl (commodity_code, count) VALUES ('P0001', 100); INSERT INTO storage_tbl (commodity_code, count) VALUES ('B1234', 10); DROP TABLE IF EXISTS `undo_log`; CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `branch_id` bigint(20) NOT NULL, `xid` varchar(100) NOT NULL, `context` varchar(128) NOT NULL, `rollback_info` longblob NOT NULL, `log_status` int(11) NOT NULL, `log_created` datetime NOT NULL, `log_modified` datetime NOT NULL, `ext` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; CREATE DATABASE IF NOT EXISTS `order_db`; USE `order_db`; DROP TABLE IF EXISTS `order_tbl`; CREATE TABLE `order_tbl` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` varchar(255) DEFAULT NULL, `commodity_code` varchar(255) DEFAULT NULL, `count` int(11) DEFAULT 0, `money` int(11) DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `undo_log`; CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `branch_id` bigint(20) NOT NULL, `xid` varchar(100) NOT NULL, `context` varchar(128) NOT NULL, `rollback_info` longblob NOT NULL, `log_status` int(11) NOT NULL, `log_created` datetime NOT NULL, `log_modified` datetime NOT NULL, `ext` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; CREATE DATABASE IF NOT EXISTS `account_db`; USE `account_db`; DROP TABLE IF EXISTS `account_tbl`; CREATE TABLE `account_tbl` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` varchar(255) DEFAULT NULL, `money` int(11) DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO account_tbl (user_id, money) VALUES ('1', 10000); DROP TABLE IF EXISTS `undo_log`; CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `branch_id` bigint(20) NOT NULL, `xid` varchar(100) NOT NULL, `context` varchar(128) NOT NULL, `rollback_info` longblob NOT NULL, `log_status` int(11) NOT NULL, `log_created` datetime NOT NULL, `log_modified` datetime NOT NULL, `ext` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;架构原理:
完整执行流程:
开启全局事务:
Business 服务(TM)向 TC 发送 Begin Global Transaction,开启一个全局事务
分支事务注册:
Business 调用 Storage 扣减库存,Storage(RM)向 TC 注册分支事务并执行本地 SQL
Business 调用 Order 创建订单,Order(RM)向 TC 注册分支事务并执行本地 SQL
Order 调用 Account 扣减余额,Account(RM)向 TC 注册分支事务并执行本地 SQL
(每个 RM 都会通过 Register Branch/Report Status 向 TC 同步状态)
全局事务决议:
所有分支执行完成后,Business(TM)向 TC 发送 Global Commit/Rollback:
全部成功 → TC 向所有 RM 发送 Branch Commit,提交本地事务;
任意失败 → TC 向所有 RM 发送 Branch Rollback,通过 undo_log 回滚本地事务
分支事务完成:
各 RM 执行本地提交 / 回滚后,向 TC 报告完成状态,全局事务结束
下载:https://seata.apache.org/zh-cn/download/seata-server/
引入依赖:
<!-- seata --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <version>2023.0.3.2</version> </dependency>在每个微服务下添加 file.conf 配置文件,用于在文件注册中心模式下,告诉微服务如何连接 Seata Server(TC 事务协调者):
service { #transaction service group mapping vgroupMapping.default_tx_group = "default" #only support when registry.type=file, please don't set multiple addresses default.grouplist = "127.0.0.1:8091" #degrade, current not support enableDegrade = false #disable seata disableGlobalTransaction = false }@GlobalTransactional 是 Seata 提供的分布式事务注解,用于开启全局事务,让跨多个微服务的多个数据库操作,要么全部成功要么全部失败
测试:
二阶提交协议:
二阶提交:把分布式事务拆成准备阶段和提交 / 回滚阶段
undo_log:回滚日志表,记录操作前镜像(beforeImage)和操作后镜像(afterImage),用于故障时恢复数据
全局锁:数据级别的锁,防止并发修改导致数据不一致
第一阶段本地事务提交:
每个微服务执行本地业务 SQL 时,Seata 会自动生成 undo_log 记录(beforeImage:修改前的数据快照,afterImage:修改后的数据快照);
把业务数据和 undo_log 放在同一个本地事务中提交;
此时数据对其他事务可见,但全局事务还未最终确认
第二阶段全局提交 / 回滚:
成功场景:所有分支事务都执行成功,TC(事务协调者)通知所有 RM(资源管理器),各服务异步删除自己的 undo_log,事务结束
失败场景:任意分支事务失败;TC 通知所有 RM 执行回滚,各服务根据 beforeImage 把数据恢复到修改前状态,回滚完成后删除 undo_log
四种事务模式:
AT 模式:
核心思想:基于本地事务和 undo_log 实现最终一致性
特点:
对业务代码零侵入,只需加 @GlobalTransactional;自动生成回滚日志 undo_log,故障时自动恢复;性能较好,适合绝大多数微服务场景
适用场景:普通业务系统、电商订单、库存扣减等
TCC 模式:
核心思想:Try-Confirm-Cancel 三段式编程
Try:预留资源(冻结库存 / 金额),Confirm:确认提交(真正扣减),Cancel:取消回滚(释放预留资源)
特点:
强一致性,性能比 AT 模式更好;业务侵入大,需要手动编写三个接口;要处理空回滚、幂等、悬挂等问题
适用场景:对一致性要求极高的金融、支付场景
Saga 模式:
核心思想:把长事务拆成多个子事务,顺序执行,失败时反向补偿
特点:
适合长时间运行的业务流程(如跨多系统审批);无锁,并发性能好;数据中间状态可见,不保证强一致
适用场景:长流程业务、跨多部门协作、异步化场景
XA 模式:
核心思想:基于数据库原生 XA 协议实现分布式事务
特点:
强一致性,由数据库保证 ACID;性能较差,因为需要锁定资源直到全局提交;依赖数据库对 XA 的支持(MySQL、Oracle 等)
适用场景:对一致性要求极高、且数据库支持 XA 的场景
