# cloud-demo **Repository Path**: sunwdong/cloud-demo ## Basic Information - **Project Name**: cloud-demo - **Description**: 学习记录 【尚硅谷2025最新SpringCloud教程,springcloud从入门到大牛 | 7小时速通】 https://www.bilibili.com/video/BV1UJc2ezEFU/ 文档:https://www.yuque.com/leifengyang/sutong/oz4gbyh5maa0rmxu - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 6 - **Forks**: 1 - **Created**: 2025-01-23 - **Last Updated**: 2025-11-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README [尚硅谷SpringCloud 7小时快速通关.视频](https://www.bilibili.com/video/BV1UJc2ezEFU/?vd_source=90e069d391ef8d8b231878ddf5931333) [SpringCloud-快速通关.文档](https://www.yuque.com/leifengyang/sutong/oz4gbyh5maa0rmxu) ## nacos ### 注册中心 ### 配置中心 1. 配置文件: SpringBoot V2.4后增加配置项, nacos为统一口径, 须显示写出需要引入的配置dataId和group( 可选,不填为DEFAULT_GROUP) ```yaml spring: application: name: @artifactId@ cloud: nacos: server-addr: 127.0.0.1:8848 config: server-addr: ${spring.cloud.nacos.server-addr} #group: DEFAULT_GROUP namespace: ${spring.profiles.active} file-extension: yaml import-check: enabled: false # 禁用启动时导入检查 config: import: - nacos:${spring.application.name}-${spring.profiles.active}?group=DEFAULT_GROUP - nacos:${spring.application.name}-hello?group=DEFAULT_GROUP ``` 2. 使用 1. @Value + @RefreshScope 注解 2. @ConfigurationProperties(prefix=xxxx) 注解: 集中配置项,减少代码量,同时也具备自动刷新配置 3. 监听变化 从容器中获取NacosConfigManager, 使用getConfigService()#addListener()添加Listener ### 拓展 1. 引入其他yaml配置文件 ```yaml spring: profiles: include: - feign # 引入同级目录下名称为application-feign.yml的配置文件 - sentinel # 引入同级目录下名称为application-sentinel.yml的配置文件 ``` 2. SpringCloud中支持多环境配置 * 数据隔离-动态切换环境: 在一个配置文件中为不同环境自定义配置项,只有`spring.config.activate.on-profile`与`spring.profiles.active`相同对应部分才会生效 ```yaml --- spring: config: activate: on-profile: dev import: - nacos:${spring.application.name}-${spring.profiles.active}?group=DEFAULT_GROUP - nacos:dev?group=order --- spring: config: activate: on-profile: test import: - nacos:${spring.application.name}-${spring.profiles.active}?group=DEFAULT_GROUP --- spring: config: activate: on-profile: prod import: - nacos:${spring.application.name}-${spring.profiles.active}?group=DEFAULT_GROUP - nacos:prod?group=order ``` ## openFeign 1. 超时时间 ```properties spring.cloud.openfeign.client.config.default.connect-timeout=1000 spring.cloud.openfeign.client.config.default.read-timeout=2000 spring.cloud.openfeign.client.config.feign的名称.connect-timeout=1000 spring.cloud.openfeign.client.config.feign的名称.read-timeout=2 ``` 2. 日志 1. 配置文件 ```properties spring.cloud.openfeign.client.config.default.logger-level=full spring.cloud.openfeign.client.config.feign的名称.logger-level=full ``` 2. 容器注入bean, 会自动生效 ```java @Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } ``` 3. 重试:仅在服务调用失败 或 响应头中包含Retry-After时才会重试 1. 配置文件 ```properties spring.cloud.openfeign.client.config.default.retryer=feign.Retryer.Default spring.cloud.openfeign.client.config.feign的名称.retryer=feign.Retryer.Default ``` 2. 容器注入bean ```java @Bean public Retryer retryer() { return new Retryer.Default(); } ``` 4. interceptor 1. 编码 1. request: 实现feign.RequestInterceptor 2. response: 实现feign.ResponseInterceptor 2. 生效 1. 容器注入bean, 会自动生效 2. 配置文件 ```properties spring.cloud.openfeign.client.config.default.requestInterceptors=tech.cyhk.cloud.demo.order.interceptor.FeignRequestTokenInterceptor,xxxxx,yyyyy ``` 5. fallback: 当服务不可用或抛出异常时,会调用fallback方法 需要结合其他熔断组件 ## sentinel ### 如何使用 > 使用wiki:https://github.com/alibaba/Sentinel/wiki 1. web层实现了intercept,如果容器中没有BlockExceptionHandler实例,就用DefaultBlockExceptionHandler来处理BlockException 2. 对于@SentinelResource注解的资源,会依次检查blockHandler、fallback、defaultFallback属性找到对应的处理方法,都没有则抛出异常 3. 对于OpenFeign调用,会用SentinelFeign.Builder代替Feign.Builder作为FeignClient的代理构建器,也就创建SentinelInvocationHandler的代理对象,使用fallback来处理 4. Spuh就是编码式的方式对进行流控 ### [规则管理及推送](https://github.com/alibaba/Sentinel/wiki/%E5%9C%A8%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Sentinel) 一般来说,规则的推送有下面三种模式: 1. 原始模式:
如果不做任何修改,Dashboard 的推送规则方式是通过 API 将规则推送至客户端并直接更新到内存中:
这种做法的好处是简单,无依赖;坏处是应用重启规则就会消失,仅用于简单测试,不能用于生产环境。 2. pull模式:
扩展写数据源(WritableDataSource), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件等。 数据源(如本地文件、RDBMS 等)一般是可写入的。使用时需要在客户端注册数据源:将对应的读数据源注册至对应的 RuleManager,将写数据源注册至 transport 的 WritableDataSourceRegistry 中。以本地文件数据源为例: ```java public class FileDataSourceInit implements InitFunc { @Override public void init() throws Exception { String flowRulePath = "xxx"; ReadableDataSource> ds = new FileRefreshableDataSource<>( flowRulePath, source -> JSON.parseObject(source, new TypeReference>() {}) ); // 将可读数据源注册至 FlowRuleManager. FlowRuleManager.register2Property(ds.getProperty()); WritableDataSource> wds = new FileWritableDataSource<>(flowRulePath, this::encodeJson); // 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中. // 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中. WritableDataSourceRegistry.registerFlowDataSource(wds); } private String encodeJson(T t) { return JSON.toJSONString(t); } } ``` 本地文件数据源会定时轮询文件的变更,读取规则。这样我们既可以在应用本地直接修改文件来更新规则,也可以通过 Sentinel 控制台推送规则。以本地文件数据源为例,推送过程如下图所示:
3. push模式:
生产环境下一般更常用的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中心(ZooKeeper, Nacos, Apollo等等),推送的操作不应由 Sentinel 客户端进行,而应该经控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。因此推送规则正确做法应该是 配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel,而不是经 Sentinel 数据源推送至配置中心。这样的流程就非常清晰了: [动态规则扩展](https://sentinelguard.io/zh-cn/docs/dynamic-rule-configuration.html)
### 规则 #### [流量控制](https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6) 1. 阈值类型 * QPS: 统计每秒请求数 * 并发线程数: 统计并发线程数 并发数控制用于保护业务线程池不被慢调用耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。为应对太多线程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程池隔离)。这种隔离方案虽然隔离性比较好,但是代价就是线程数目太多,线程上下文切换的 overhead 比较大,特别是对低延时的调用有比较大的影响。Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目),如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。并发数控制通常在调用端进行配置。 例子参见:[ThreadDemo](https://github.com/alibaba/Sentinel/blob/master/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/flow/FlowThreadDemo.java) 2. 是否集群-集群阈值模式 * 单机均摊: * 总体阈值: 3. 流控模式 1. 直接:
2. 关联:
当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。 比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身 带来的开销会降低整体的吞吐量。 可使用关联限流来避免具有关联关系的资源之间过度的争抢,举例来说,read_db 和 write_db 这两个资源分别代表数据 库读写,我们可以给 read_db 设置限流规则来达到写优先的目的:设置 流控模式`FlowRule.strategy` 为 关联 `RuleConstant.RELATE` 同时设置 关联资源 `FlowRule.ref_identity` 为 write_db。这样当写库操作过于频繁时,读数据的请求会被限流。 3. 链路:
底层`NodeSelectorSlot`中记录了资源之间的调用链路,这些资源通过调用关系,相互之间构成一棵调用树。这棵树的根节点是一个名字为 machine-root 的虚拟节点,调用链的入口都是这个虚节点的子节点。 一棵典型的调用树如下图所示: ``` ``` machine-root / \ / \ Entrance1 Entrance2 \ / \ / DefaultNode(nodeA) ``` 上图中来自入口 `Entrance1` 和 `Entrance2` 的请求都调用到了资源 `NodeA`,Sentinel 允许只根据某个入口的统计信息对资源限流。 比如我们可以设置 流控模式`FlowRule.strategy` 为 链路`RuleConstant.CHAIN`,同时设置 入口资源`FlowRule.ref_identity` 为 `Entrance1` 来表示只有从入口 `Entrance1` 的调用才会记录到 NodeA 的限流统计当中,而对来自 `Entrance2` 的调用漠不关心。 调用链的入口是通过 API 方法 `ContextUtil.enter(name)` 定义的。 ``` 4. 流控效果 > 注意:**_若使用除了直接拒绝之外的流量控制效果,则调用关系限流策略(strategy)会被忽略。_** 1. 快速失败:
`RuleConstant.CONTROL_BEHAVIOR_DEFAULT` 默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。具体的例子参见 [FlowQpsDemo](https://github.com/alibaba/Sentinel/blob/master/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/flow/FlowQpsDemo.java)。 2. warm up:
`RuleConstant.CONTROL_BEHAVIOR_WARM_UP` 即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过" 冷启动" ,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。详细文档可以参考[流量控制 - Warm Up 文档](https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81---%E5%86%B7%E5%90%AF%E5%8A%A8) ,具体的例子可以参见 [WarmUpFlowDemo](https://github.com/alibaba/Sentinel/blob/master/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/flow/WarmUpFlowDemo.java)。 通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:
3. 排队等待:
`RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER` 方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。 详细文档可以参考 [流量控制 - 匀速器模式](https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6-%E5%8C%80%E9%80%9F%E6%8E%92%E9%98%9F%E6%A8%A1%E5%BC%8F) ,具体的例子可以参见 [PaceFlowDemo](https://github.com/alibaba/Sentinel/blob/master/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/flow/PaceFlowDemo.java)。 该方式的作用如下图所示:
这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。 > 注意:匀速排队模式暂时不支持 QPS > 1000 的场景。 #### [熔断降级 degrade](https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7) 现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的 某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定 因素导致整体的雪崩。熔断降级作为保护自身的手段,**通常在客户端(调用端)进行配置**。 1. 目的 * 切断不稳定调用 * 快速返回不积压 * 避免雪崩 2. 状态 1. 半开状态:
2. 闭合状态:
3. 断开状态:
2. 策略 1. 慢调用比例 (`SLOW_REQUEST_RATIO`): 选择以慢调用比例作为阈值,需要设置允许的慢调用 `RT`(即最大的响应时间),请求的响应时间大于该值则统 计为慢调用。当单位统计时长( `statIntervalMs`) 内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态 (`HALF-OPEN` 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 `RT` 则会再次被熔断。 2. 异常比例 (ERROR_RATIO): 当单位统计时长(`statIntervalMs`)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔 断时长后熔断器会进入探测恢复状态(`HALF-OPEN` 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值 范围是 [0.0, 1.0],代表 0% - 100%。 3. 异常数 (`ERROR_COUNT`): 当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(`HALF-OPEN` 状态),若接下来的一个请求成功 完成(没有错误)则结束熔断,否则会再次被熔断。 #### [热点参数限流](https://github.com/alibaba/Sentinel/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81) 热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。* *热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。**
如果要添加参数外项,需得在新增热点规则后点击编辑-高级选型中填写,已实现对特殊参数值控制。 ```java @GetMapping("/hotData") @SentinelResource(value = "hotData" // , blockHandler = "hotDataBlockHandler" ,fallback = "hotDataFallback" ) public String hotData(@RequestParam Long id, @RequestParam String name){ return "hotData:"+ id + ":" + name; } public String hotDataBlockHandler(Long id, String name, BlockException e){ return "hotData blockHandler:"+ id + ":" + name+e.getClass(); } /** * {@link com.alibaba.csp.sentinel.annotation.aspectj.AbstractSentinelAspectSupport#resolveFallbackInternal} 解析时先用原方法参数查找,未找到再加Throwable */ public String hotDataFallback( Long id, String name, Throwable e){ return "hotData fallback:"+ id + ":" + name+e.getClass(); } ``` #### 授权规则 在调用方发起前,往header塞入origin信息 ```java @Slf4j @Component public class FeignRequestHeaderInterceptor implements RequestInterceptor { @Value(value = "${spring.application.name}") private String applicationName; @Override public void apply(RequestTemplate template) { template.header("x-application-name", applicationName); } } ``` 被调用方,增加`RequestOriginParser`实现类从header解析 ```java @Slf4j @Component public class CustomRequestOriginParser implements RequestOriginParser { @Override public String parseOrigin(HttpServletRequest request) { return request.getHeader("x-application-name"); } } ``` ## gateway 官网:https://spring.io/projects/spring-cloud-gateway 快速使用: ```yaml spring: cloud: gateway: routes: - id: product_route uri: lb://service-product predicates: - Path=/api/product/** filters: - StripPrefix=1 order: 1 # 优先级,越小优先级越高 - id: order_route uri: lb://service-order predicates: # 短断言写法 # - Path=/api/order/** # 长断言写法 - name: Path args: patterns: /api/order/** matchTrailingSlash: true filters: - StripPrefix=1 order: 1 ``` ### [断言 predicate](https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway/request-predicates-factories.html) **`RouteDefinitionRouteLocator` 会用`NameUtils#normalizeRoutePredicateName`将`RoutePredicateFactory`部分截断,完成前缀映射 ** 1. 官方提供的断言 | 类 | 名 | 参数(个数/类型) | 作用 | | ----------------------------------------- | -------------------- | ------------------- | ------------------------------------------------------------ | | AfterRoutePredicateFactory | After | 1/datetime | 在指定时间之后 | | BeforeRoutePredicateFactory | Before | 1/datetime | 在指定时间之前 | | BetweenRoutePredicateFactory | Between | 2/datetime | 在指定时间区间内 | | CookieRoutePredicateFactory | Cookie | 2/string,regexp | 包含cookie名且必须匹配指定值 | | HeaderRoutePredicateFactory | Header | 2/string,regexp | 包含请求头且必须匹配指定值 | | HostRoutePredicateFactory | Host | N/string | 请求host必须是指定枚举值 | | MethodRoutePredicateFactory | Method | N/string | 请求方式必须是指定枚举值 | | PathRoutePredicateFactory | Path | 2/List,bool | 请求路径满足规则,是否匹配最后的/ | | QueryRoutePredicateFactory | Query | 2/string,regexp | 包含指定请求参数 | | RemoteAddrRoutePredicateFactory | RemoteAddr | 1/List | 请求来源于指定网络域( CIDR写法) | | WeightRoutePredicateFactory | Weight | 2/string,int | 按指定权重负载均衡 | | XForwardedRemoteAddrRoutePredicateFactory | XForwardedRemoteAddr | 1/List | 从X-Forwarded-For请求头中解析请求来源,并判断是否来源于指定网络域 | 2. 自定义断言 1. 继承`AbstractRoutePredicateFactory` ```java @Component public class VipRoutePredicateFactory extends AbstractRoutePredicateFactory { public VipRoutePredicateFactory() { super(Config.class); } /* * 短写法中参数字段顺序 */ @Override public List shortcutFieldOrder() { return List.of("key","value"); } @Override public Predicate apply(Config config) { return (GatewayPredicate) serverWebExchange -> { String first = serverWebExchange.getRequest().getQueryParams().getFirst(config.getKey()); return StringUtils.equals(first, config.getValue()); }; } @Validated @Data static class Config{ private String key; private String value; } } ``` 2. 配置文件 ```yaml spring: cloud: gateway: routes: - id: order_route uri: lb://service-order predicates: # 短断言写法 # - Vip=user,admin # 长断言写法 - name: Vip args: key: user value: admin filters: - StripPrefix=1 order: 1 ``` ### [过滤器 filter](https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway/gatewayfilter-factories.html) **`RouteDefinitionRouteLocator` 会用`NameUtils#normalizeFilterFactoryName`将`GatewayFilterFactory`部分截断,完成前缀映射 ** 作用:允许以某种方式修改传入的HTTP请求或传出的HTTP响应。 1. 官方提供的filter | 名 | 参数(个数/类型) | 作用 | | -------------------------------- | ----------------- | ------------------------------------------------- | | AddRequestHeader | 2/string | 添加请求头 | | AddRequestHeadersIfNotPresent | 1/List | 如果没有则添加请求头,key:value方式 | | AddRequestParameter | 2/string、string | 添加请求参数 | | AddResponseHeader | 2/string、string | 添加响应头 | | CircuitBreaker | 1/string | 仅支持forward:/inCaseOfFailureUseThis方式进行熔断 | | CacheRequestBody | 1/string | 缓存请求体 | | DedupeResponseHeader | 1/string | 移除重复响应头,多个用空格分割 | | FallbackHeaders | 1/string | 设置Fallback头 | | JsonToGrpc | | 请求体Json转为gRPC | | LocalResponseCache | 2/string | 响应数据本地缓存 | | MapRequestHeader | 2/string | 把某个请求头名字变为另一个名字 | | ModifyRequestBody | 仅 Java 代码方式 | 修改请求体 | | ModifyResponseBody | 仅 Java 代码方式 | 修改响应体 | | PrefixPath | 1/string | 自动添加请求前缀路径 | | PreserveHostHeader | 0 | 保护Host头 | | RedirectTo | 3/string | 重定向到指定位置 | | RemoveJsonAttributesResponseBody | 1/string | 移除响应体中的某些Json字段,多个用,分割 | | RemoveRequestHeader | 1/string | 移除请求头 | | RemoveRequestParameter | 1/string | 移除请求参数 | | RemoveResponseHeader | 1/string | 移除响应头 | | RequestHeaderSize | 2/string | 设置请求大小,超出则响应431状态码 | | RequestRateLimiter | 1/string | 请求限流 | | RewriteLocationResponseHeader | 4/string | 重写Location响应头 | | RewritePath | 2/string | 路径重写 | | RewriteRequestParameter | 2/string | 请求参数重写 | | RewriteResponseHeader | 3/string | 响应头重写 | | SaveSession | 0 | session保存,配合spring-session框架 | | SecureHeaders | 0 | 安全头设置 | | SetPath | 1/string | 路径修改 | | SetRequestHeader | 2/string | 请求头修改 | | SetResponseHeader | 2/string | 响应头修改 | | SetStatus | 1/int | 设置响应状态码 | | StripPrefix | 1/int | 路径层级拆除 | | Retry | 7/string | 请求重试设置 | | RequestSize | 1/string | 请求大小限定 | | SetRequestHostHeader | 1/string | 设置Host请求头 | | TokenRelay | 1/string | OAuth2的token转发 | ```yaml spring: cloud: gateway: routes: - id: order_route uri: lb://service-order predicates: # 短断言写法 # - Path=/api/order/** # 长断言写法 - name: Path args: patterns: /api/order/** matchTrailingSlash: true filters: # - StripPrefix=1 # 例如:请求路径 /api/v1/resource 将被重写为 /v1/resource; # 请注意,由于YAML规范,$应该替换为$\。 - RewritePath=/api/?(?.*), /$\{segment} order: 1 ``` 2. [默认filter](https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway/gatewayfilter-factories/default-filters.html) ```yaml spring: cloud: gateway: # 向所有的route添加filter default-filters: - AddResponseHeader=X-Response-Red, Blue ``` 3. [GlobalFilter](https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway/global-filters.html) ```java @Bean public GlobalFilter customFilter() { return new CustomGlobalFilter(); } public class CustomGlobalFilter implements GlobalFilter, Ordered { @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { log.info("custom global filter"); return chain.filter(exchange) .doFinally(signalType->{ //异步编程,后置操作 }); } @Override public int getOrder() { return -1; } } ``` 4. 自定义过滤器 1. 继承`AbstractNameValueGatewayFilterFactory` 或者 `AbstractGatewayFilterFactory` ```java @Slf4j @Component public class OnceTokenGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory { @Override public GatewayFilter apply(NameValueConfig config) { return (exchange, chain) -> chain.filter(exchange).then(Mono.fromRunnable(() -> { exchange.getResponse().getHeaders().add(config.getName(), config.getValue()); })); } } ``` 2. 配置文件 ```yaml spring: cloud: gateway: routes: - id: order_route uri: lb://service-order predicates: # 短断言写法 - Path=/api/order/** filters: # - OnceToken=jwt,1234567890 - name: OnceToken args: name: jwt value: 1234567890 order: 1 ``` ### [跨域](https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway/cors-configuration.html) 1. 全局跨域配置 ```yaml spring: cloud: gateway: globalcors: cors-configurations: '[/**]': allowedOrigins: "https://docs.spring.io" allowedMethods: - GET ``` 2. 单个route跨域配置 ```yaml spring: cloud: gateway: routes: - id: cors_route uri: https://example.org predicates: - Path=/service/** metadata: cors: allowedOrigins: '*' allowedMethods: - GET - POST allowedHeaders: '*' maxAge: 30 ``` ## seata 官网:https://seata.apache.org/zh-cn/
### 模式 1. [AT](https://seata.apache.org/zh-cn/docs/user/mode/at)——配合数据库事务、非侵入式分布式事务解决方案 > Seata 在内部做了对数据库操作的代理层,我们使用 Seata AT 模式时,实际上用的是 Seata 自带的数据源代理 DataSourceProxy,Seata 在这层代理中加入了很多逻辑,比如插入回滚 undo_log 日志,检查全局锁等。 机制: * 一阶段:业务数据和回滚日志记录(分别为执行前镜像和执行后镜像)在同一个本地事务中提交,释放本地锁和连接资源。 * 二阶段: * 提交异步化,非常快速地完成。 * 回滚通过一阶段的回滚日志进行反向补偿。 2. [XA](https://seata.apache.org/zh-cn/docs/user/mode/xa)——配合数据库事务、非侵入式分布式事务解决方案 > 从 1.2 版本支持的事务模式。XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准。Seata XA 模式是利用事务资源(数据库、消息服务等)对 XA 协议的支持,以 XA 协议的机制来管理分支事务的一种事务模式。 1. 机制: * 执行阶段: - 可回滚:业务 SQL 操作放在 XA 分支中进行,由资源对 XA 协议的支持来保证 *可回滚* - 持久化:XA 分支完成后,执行 XA prepare,同样,由资源对 XA 协议的支持来保证 *持久化*(即,之后任何意外都不会造成无法回滚的情况) * 完成阶段: - 分支提交:执行 XA 分支的 commit - 分支回滚:执行 XA 分支的 rollback 2. 优缺点 1. 优势: 要求事务资源本身提供对规范和协议的支持,所以事务资源(如数据库)可以保障从任意视角对数据的访问有效隔离,满足全局数据一致性。此外的一些优势还包括: 1. 业务无侵入:和 AT 一样,XA 模式将是业务无侵入的,不给应用设计和开发带来额外负担。 2. 数据库的支持广泛:XA 协议被主流关系型数据库广泛支持,不需要额外的适配即可使用。 2. 缺点: **prepare 后,分支事务进入阻塞阶段,收到 XA commit 或 XA rollback 前必须阻塞等待。** 事务资源长时间得不到释放,锁定周期长,而且在应用层上面无法干预,性能差。
3. [TCC](https://seata.apache.org/zh-cn/docs/user/mode/tcc)——不依赖数据库、侵入式分布式事务解决方案、**适用于核心系统等对性能有很高要求的场景 ** 机制: ​ 在两阶段提交协议中,资源管理器(RM, Resource Manager)需要提供“准备”、“提交”和“回滚” 3 个操作;而事务管理器(TM, Transaction Manager)分 2 阶段协调所有资源管理器,在第一阶段询问所有资源管理器“准备”是否成功,如果所有资源均“准备”成功则在第二阶段执行所有资源的“提交”操作,否则在第二阶段执行所有资源的“回滚”操作,保证所有资源的最终状态是一致的,要么全部提交要么全部回滚。 ​ 资源管理器有很多实现方式,其中 TCC(Try-Confirm-Cancel)是资源管理器的一种服务化的实现;TCC 是一种比较成熟的分布式事务解决方案,可用于解决跨数据库、跨服务业务操作的数据一致性问题;TCC 其 Try、Confirm、Cancel 3 个方法均由业务编码实现,故 TCC 可以被称为是服务化的资源管理器。 ​ TCC 的 Try 操作作为一阶段,负责资源的检查和预留;Confirm 操作作为二阶段提交操作,执行真正的业务;Cancel 是二阶段回滚操作,执行预留资源的取消,使资源回到初始状态。
4. [Saga](https://seata.apache.org/zh-cn/docs/user/mode/saga)——长事务解决方案 业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。 ### 使用 1. 启动seata server 2. 整合seata依赖 ```pom com.alibaba.cloud spring-cloud-starter-alibaba-seata ``` resource/file.conf ```txt service { #transaction service group mapping vgroupMapping.my_test_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 } ``` 3. 全局事务入口方法上添加` @GlobalTransactional`, 事务各参与方启动类开启事务`@EnableTransactionManagement`都方法仍旧使用 ` @Transactional`