当前位置: 首页 > news >正文

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 的场景

http://www.cnnetsun.cn/news/1494490.html

相关文章:

  • Win11Debloat:Windows系统优化的模块化解决方案与技术实践
  • 动画设置、自定义放映、幻灯片放映设置、排练计时
  • 别再手动标点了!用Python解析无人机JPG照片,自动获取图上任意点的GPS坐标
  • 工业数据智能:从混沌到秩序的底层重构
  • 程序人生之知幻即离,离幻即觉:《圆觉经》智慧给职场工作的深层启发
  • 从零开始:mitmproxy跨平台证书配置与实战抓包指南
  • 如何快速掌握免费UML绘图工具:UMLet新手完全指南
  • 你也想转行网安吗?作为过来人的我希望你想清楚这几个问题再做决定
  • KindEditor富文本编辑器:轻量级网页内容创作解决方案
  • 保姆级教程:用LTspice仿真带你搞懂USB3.2信号上的AC耦合电容,容量与封装到底怎么选
  • 基于RetinaFace的实时视频会议系统:人脸追踪与虚拟背景
  • 别再手动建模了!用SolidWorks导出的STL模型,在MATLAB里5分钟搞定静力学仿真
  • 5个颠覆级技巧:用Fastboot Enhance实现Android设备安全高效管理
  • 5步玩转OpenDroneMap:从图像到三维模型的全流程指南
  • 深度学习入门神器:PyTorch 2.6 镜像快速部署教程,开箱即用免配置
  • 基于LaravelS与Swoole构建高并发WebSocket实时通信服务
  • Win11Debloat:Windows 11终极优化工具完整指南
  • 3个步骤打造你的智能笔记助手:obsidian-copilot从安装到精通
  • 飞书文档智能备份终极指南:3步实现企业知识库全自动同步
  • Qwen1.5-1.8B GPTQ生成技术博客大纲与初稿:以“操作系统内存管理”为例
  • 数字工具如何让手写艺术重获新生?
  • 别再手动算坐标了!UE5蓝图UI的Menu Anchor,5分钟搞定浮动详情弹窗
  • 税务季网络钓鱼攻击的演进特征、社会工程学机制与多维防御体系构建
  • DamoFD人脸检测模型实战教程:5步完成关键点检测部署
  • Halcon圆弧检测避坑指南:为什么你的拟合圆总是不准?
  • 如何安全获取安卓应用?APKMirror带来的革新性解决方案
  • Windows搭建WebDAV服务:从内网穿透到本地盘符映射的完整指南
  • 基于Transformer架构解析:Flux Sea Studio的图像生成优势
  • 告别FPS采样!用RandLA-Net的随机采样,在普通GPU上也能玩转百万级点云分割
  • 深入理解Linux内核模块初始化:module_init宏背后的机制与优化技巧