# springcloud2024
**Repository Path**: xfanonymous/springcloud2024
## Basic Information
- **Project Name**: springcloud2024
- **Description**: SpringCloud项目笔记,包括Consul、Feign、CircuitBreaker、MicrometerTracing、Gateway。Alibaba Nacos、Sentinel、Seata。
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2024-03-04
- **Last Updated**: 2025-08-07
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
[toc]
# SpringCloud
- mybatis_generator01:MyBatis自动工具Mapper4使用
- cloud-provider-payment8001:服务端,基本SpringBoot项目实现、Swagger3使用、时间格式、统一返回值格式、全局异常处理
- cloud-consumer-order81:消费端,服务间调用、RestTemplate使用
- cloud-api-commons:公共代码抽取
- cloud-provider-payment8002: 服务端,LB实现
- cloud-provider-payment8003: 服务端,LB实现
- cloud-consumer-feign-order82:消费端,Feign服务统一接口访问
- cloud-gateway9527: Gateway网关:Route路由、Predicate断言、Filter过滤器
- cloudalibaba-provider-payment9001: Nacos 服务端,服务注册、OpenFeign和Sentinel集成
- cloudalibaba-consumer-nacos-order83:Nacos 消费端,服务调用、OpenFeign和Sentinel集成
- cloudalibaba-config-nacos-client3377:Nacos 分布式配置管理
- cloudalibaba-sentinel-service8401:Sentinel 流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护
- cloudalibaba-sentinel-gateway9528:Gateway和Sentinel集成
- seata-order-service2001: Seata分布式事务管理、@GlobalTransactional
- seata-storage-service2002: Seata分布式事务管理
- seata-account-service2003: Seata分布式事务管理
```
框架版本信息
Java:17
SpringCloud: 2023.0.0
SpringBoot: 3.2
SpringCloudAlibaba: 2022.0.0
Maven: 3.9+
MySQL: 8.0+
```
## mybatis_generator01
- 使用MyBatis的Mapper4,根据数据库表自动生成Entity、Mapper接口、XML映射文件。
- MySQL8.0 JDBC四件套
```properties
jdbc.driverClass = com.mysql.cj.jdbc.Driver
jdbc.url= jdbc:mysql://localhost:3306/db2024?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
jdbc.user = root
jdbc.password =123456
```
## cloud-provider-payment8001
- 微服务口诀:建Module、改POM、写Yaml、主启动、业务类
- 主启动类,@MapperScan("") 定义Mapper接口路径扫描 //import tk.mybatis.spring.annotation.MapperScan;
- 实体类,一般而言,前端调用者不应该获悉服务端提供者的entity资源并知道表结构关系,所以服务提供方给出的接口文档都应该是DTO
### Swagger3
1. 依赖 springdoc-openapi-starter-webmvc-ui
2. 注解
1. @Tag(name = "支付微服务模块",description = "支付CRUD") 修饰Controller类
2. @Operation(summary = "删除", description = "删除支付流水方法") 修饰Handler方法
3. @Schema(title = "支付交易表") 修饰Entity类及属性
3. 配置类
1. 通过组件GroupedOpenApi对API进行分组说明
2. 添加API说明配置
4. 调用 http://localhost:port/swagger-ui/index.html
### 时间格式
1. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
2. 修饰Entity时间属性,指定返回前端的时间数据格式
3. 不推荐 application.yml 配置,防止臃肿
```yml
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
```
### 统一返回值
- 定义ResultData类
1. 返回格式标配:code状态码、message描述、data数据,扩展字段:timestamp接口调用时间
2. 定义枚举状态码类ReturnCodeEnum,枚举值、构造方法、遍历
3. 定义返回类型类ResultData
1. @Accessors(chain = true) lombok链式编程,可以使用链式set方法,且setter方法返回当前对象,替代void。
2. 构造方法对时间戳赋值,每次调用接口方法获取返回值时,都获取当前时间 System.currentTimeMillis();
3. 泛型:声明方法时,在返回值前添加``表示当前函数使用了泛型变量。
4. 修改Controller,将所有Handler返回类型统一为ResultData
### 全局异常处理
1. 定义类GlobalExceptionHandler
2. @Slf4j lombok日志工具,log.error()
3. @RestControllerAdvice = @ResponseBody + @ControllerAdvice
4. @ResponseStatus 用于异常类或Handler方法,返回异常状态码和描述信息
## cloud-consumer-order81
- 服务间调用,81服务请求8001服务
### RestTemplate
- 提供了多种便捷访问远程Http服务的方法,是Spring访问restful服务模板工具类
- RestTemplate方法及使用:https://blog.csdn.net/nanhuaibeian/article/details/117331748
- 问题:
1. 调用的目标API地址和端口号硬编码
2. 调用的目标服务可能存在多个微服务,无法实现负载均衡
- 解决:需要引入服务治理功能,实现微服务之间的动态注册与发现
## cloud-api-commons
- 新建服务,抽取公共代码:entities、resp、exp
- 将其他服务中公共代码删除
- 将自定义公共服务引入其他服务pom
## Consul
- 功能
1. 服务发现:提供HTTP和DNS两种发现方式。
2. 健康监测:支持多种方式,HTTP、TCP、Docker、Shell脚本定制化监控。
3. KV存储:Key、Value的存储方式。
4. 支持多数据中心
5. 可视化Wob界面
- 使用
1. 下载consul
2. 运行consul: consul agent-dev
3. 访问consul: http://localhost:8500
4. consul --version
5. 编写consul持久化运行脚本,以管理员方式运行,停止运行:sc delete consul
6. 脚本内容
```bat
@echo.服务启动......
@echo off
@sc create Consul binpath= "D:\Install\Tools\consul_1.17.3\consul.exe agent -server -ui -bind=127.0.0.1 -client=0.0.0.0 -bootstrap-expect 1 -data-dir D:\Install\Tools\consul_1.17.3\mydata "
@net start Consul
@sc config Consul start= AUTO
@echo.Consul start is OK......success
@pause
```
7. 删除Consul注册的服务`PUT http://localhost:8500/v1/agent/service/deregister/cloud-payment-service-8003`
### 服务注册与发现
1. 修改服务:cloud-provider-payment8001、cloud-consumer-order81
2. 改pom:spring-cloud-starter-consul-discovery
3. 写yml:spring:cloud:consul
4. 主启动类:@EnableDiscoveryClient
5. 除告警:commons-logging
6. Controller 微服务间调用时,直接调用服务注册中心上的微服务名称,避免硬编码
7. RestTemplate 微服务间调用时,需要有负载均衡能力,通过名称调用的服务可能是集群模式
#### @EnableDiscoveryClient
- `@EnableDiscoveryClient` 开启服务发现,向Consul注册服务
#### @LoadBalanced
- `@LoadBalanced` 赋予RestTemplate负载均衡的能力,当通过服务注册中心Consul的服务名调用其他服务时,需要开启负载均衡能力
### 分布式配置管理
- 修改服务cloud-provider-payment8001
- bootstrap.yml
1. `application.yml`是用户级资源配置,`bootstrap.yml`是系统级资源配置,优先级更高。
2. SpringCloud会创建`Bootstrap Context`,作为Spring应用`Application Context`的父上下文。初始化时,BootstrapContext从外部源加载配置属性并解析。
3. `Bootstrap`属性有高优先级,默认不会被本地配置覆盖。新增`bootstrap.yml`文件,保证`Bootstrap Context`和`Application Context`配置分离。
- 新建配置文件bootstrap.yml
1. 配置微服务名、consul设置
- 启动本地consul
- 启动微服务
- 在http://localhost:8500/配置key-value
1. 创建文件夹 config/
2. 在config下创建文件夹cloud-payment-service/、cloud-payment-service-dev/、cloud-payment-service-prod/
3. 在三个文件夹下创建data配置文件
4. 配置文件改为yaml格式,填写配置信息
- 在PayController编写测试代码,测试consul配置的信息,对比application.yml中的server.port
- 访问 GET http://localhost:8001/pay/get/info
#### @RefreshScope
- @RefreshScope 支持Consul控制台配置动态刷新,程序可以取到最新配置值
## LoadBalancer
- LB负载均衡(Load Balance)就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)
- Spring Cloud LoadBalancer是由SpringCloud官方提供的一个开源的、简单易用的客户端负载均衡器,它包含在SpringCloud-commons中用替换以前的Ribbon组件。支持RestTemplate、WebClient(WeClient是Spring Web Flux中提供的功能,可以实现响应式异步请求)
- Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求,即负载均衡是由服务端实现的。
- loadbalancer本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,在本地实现RPC远程服务调用技术。
1. 使用RestTemplate作为客户端LB
2. 新建服务8002、8003
3. 将各服务注册到consul
4. 在客户端cloud-consumer-order81添加loadbalancer依赖`spring-cloud-starter-loadbalancer`
5. 启动客户端,注册到consul
6. 通过localhost:port访问客户端服务、后端3个微服务验证服务访问正常
7. 通过localhost:81访问客户端,验证轮询调用后端3个微服务
- 编码使用`DiscoveryClient`动态获取所有Consul上线的服务
- LB算法
1. 默认算法:轮询 RoundRobinLoadBalancer、随机 RandomLoadBalancer
2. 所有算法接口:ReactiveLoadBalancer
3. @LoadBalancerClient 指定LB配置,指定服务名,使用的配置类,可以切换算法,修饰RestTemplateConfig配置类
### @LoadBalancerClient
- @LoadBalancerClient 指定LB配置,指定服务名,使用的配置类,可以切换算法
## OpenFeign
- OpenFeign是声明式web服务客户端,是当前微服务之间调用的事实标准。
- OpenFeign只需创建接口并添加`@FeignClient`注解,即可完成服务提供方的接口绑定,统一对外提供调用方法,避免了每个微服务自行封装服务调用方法RestTemplate。
- OpenFeign集成了SpringCloud LoadBalancer,可以继承Alibaba Sentinel提供熔断降级功能。
- 实现效果:cloud-consumer-feign-order82 通过 cloud-api-commons 的Feign方法接口 调用到 cloud-payment-service 的8001/8002/8003微服务
### 服务调用与负载均衡
#### cloud-api-commons
- 修改cloud-api-commons通用模块
1. 依赖导入OpenFeign,spring-cloud-starter-openfeign
2. 新建API接口 PayFeignApi
#### @FeignClient
- 接口添加注解 @FeignClient
- `@FeignClient("value = service-name")` 为服务端提供统一对外的端口方法,value为注册中心的服务名
- 在Feign接口中声明方法,和服务端Controller方法保持一致,访问路径为 服务名+URL
- 声明方法 add、get、getInfo
3. Maven重新install
#### cloud-consumer-feign-order82
- 新建消费端order82
1. 依赖导入 spring-cloud-starter-openfeign
2. 写yml,指定服务端口号
3. 主启动
#### @EnableDiscoveryClient
#### @EnableFeignClients
- @EnableFeignClients 启用Feign客户端
- `@EnableFeignClients(basePackages = "com.xfanonymous.cloud")`
- 需要指定openFeign接口所在包路径,否则会报错 A component required a bean of type that could not be found.
4. 业务类
- 修改Controller调用,直接调用Feign接口方法
### 超时控制
1. 服务端8001的getbyid方法中故意sleep,制造请求超时
2. 消费端82的getbyid方法中通过feign调用服务端8001,打印请求超时错误的时间
3. 修改消费端Feign超时时间application.yml
- default 指定全局配置信息。也可以通过服务名指定具体服务端的配置信息,会覆盖全局default配置数据
- openfeign:client:config:default:connectTimeout/readTimeout
- openfeign:client:config:cloud-payment-service:connectTimeout/readTimeout
#### 重试机制
1. 消费端新建OpenFeign配置文件类FeignConfig
2. Feign默认配置不重试
3. 通过`Retryer`配置重试机制 new Retryer.Default(100,1,3);
4. 不会展示重试过程和次数,只展示超时结果,总超时时长=重试次数*请求超时时间
#### HttpClient替换
1. OpenFeign默认使用JDK自带的HttpURLConnection发送HTTP请求,没有连接池,性能和效率比较低
2. `Apache HttpClient 5`替换OpenFeign默认的HttpURLConnection
3. 消费端改pom:httpclient5、feign-hc5
4. 消费端写yml:httpclient:hc5:enabled: true 开启HC5
#### 请求响应压缩
- 节约传输带宽,降低性能损耗,只需在yml开启配置即可
1. 对请求和响应进行GZIP压缩,减少通信过程中的性能损耗
- spring.cloud.openfeign.compression.request.enabled=true
- spring.cloud.openfeign.compression.response.enabled=true
2. 细粒度设置,比如指定压缩的请求数据类型、设置请求压缩的大小下限,只有超过这个大小的请求才会进行压缩
- spring.cloud.openfeign.compression.request.enabled=true
- spring.cloud.openfeign.compression.request.mime-types=text/xml,application/xml,application/json #触发压缩数据类型
- spring.cloud.openfeign.compression.request.min-request-size=2048 #最小触发压缩的大小
#### 日志打印功能
- 对Feign接口的调用情况进行监控和输出,了解Feign中Http请求的细节
1. 在FeignConfig配置类中配置日志Bean组件Logger.Level
2. 通过`feign.Logger.Level`调整日志级别
- NONE:默认,不显示任何日志;
- BASIC:仅记录请求方法、URL、响应状态码及执行时间;
- HEADERS:BASIC信息,还有请求和响应的头信息;
- FULL:HEADERS信息,还有请求和响应的正文及元数据。
3. yml配置日志
- logging.level + 含有@FeignClient注解的完整带包名的接口名 + debug模式
- logging.level.com.xfanonymous.cloud.api.PayFeignApi:debug
## Resilience4J
1. 分布式系统问题
- 分布式系统的应用有复杂的依赖关系,存在服务间访问失败的情况。
- 服务雪崩:多个微服务之间调用,如果扇出链路上某个微服务的响应时间过长或者不可用,会导致系统资源占用,引发级联故障。
- 解决:通过CircuitBreaker断路器避免服务雪崩,快速返回FallBack失败处理、或返回默认兜底数据(服务降级),而不是长时间等待占用。
2. 服务熔断:当服务端达到最大访问后,直接拒绝访问,跳闸限电(OPEN),消费端会接受服务降级的处理并返回友好兜底提示
3. 服务降级:当服务端故障时,消费端可主动断开与生产者的连接,立即返回友好提示FallBack。
4. 服务限流:在流量高峰时,可根据消费端优先级适当调整流量限制,保护服务端不被流量击垮。
### CircuitBreaker熔断
1. Hystrix 是处理分布式系统延迟和容错的开源库,已停更维护,替换方案 Resilience4J,用于消费端。
2. CircuitBreaker 目的是避免分布式系统因故障和异常崩溃,提高系统的可用性和健壮性。
- 当服务故障时,CircuitBreaker迅速切换到OPEN状态,阻止请求发送到该服务,减少负载,避免崩溃。
- CircuitBreaker 包含3个状态:CLOSE、OPEN、HALF-OPEN
- 服务端状态判定:调用次数的滑动窗口(最近N次的返回结果)、基于时间的滑动窗口(最近N秒的调用结果)
- CircuitBreaker 可以在分布式系统的各服务间自动切换,避免单点故障的问题。
- CircuitBreaker 只是一套规范和接口,落地实现是 Resilience4J
3. Resilience4J配置参数,可以查看配置类源码CircuitBreakerConfiguration
| Resilience4J配置 | 说明 |
|----------------------------------------------|--------------------------------------------|
| failure-rate-threshold | 以百分比配置失败率 |
| sliding-window-type | 滑动窗口类型,次数 COUNT_BASED(默认),时间 TIME_BASED |
| sliding-window-size | 滑动窗口大小:COUNT_BASED表示请求结果百分比;TIME_BASED表示N秒 |
| slowCallDurationThreshold | 调用时间的峰值,高于该峰值的视为慢调用。 |
| slowCallRateThreshold | 慢调用比例次数的百分比,超过时断路器开启,进入服务降级。 |
| permitted-number-of-calls-in-half-open-state | 半开启状态的调用次数,N次调用仍然慢速的,重新进入打开状态 |
| minimum-number-of-calls | 最小调用次数,只有达到一定次数后才开始计算故障率,次数小于此数值就算全失败也不会进入OPEN |
| wait-duration-in-open-state | 从OPEN到HALF_OPEN状态需要等待的时间 |
4. 步骤
1. 服务端8001,新建PayCircuitController,方法myCircuit验证异常、超时、正常场景
2. 公共模块,PayFeignApi新增接口,直接拷贝方法myCircuit
3. 消费端cloud-consumer-feign-order82
1. 改pom:断路保护等需要AOP实现,spring-cloud-starter-circuitbreaker-resilience4j、spring-boot-starter-aop
2. 写yml:开启CircuitBreaker,spring.cloud.openfeign.circuitbreaker.enabled
3. 业务类:新建OrderCircuitController,客户端通过FeignAPI调用服务端
4. @CircuitBreaker(name="service-name", fallbackMethod="method-name")
4. 访问客户端 GET http://localhost:82/feign/pay/circuit/{{id}}
5. 客户端配置了Resilience4J CircuitBreaker和兜底方法
6. Handler通过Feign调用到服务端 GET http://localhost:8001/pay/circuit/{{id}}
7. 当服务端PayCircuitController抛出异常时,客户端触发兜底方法
8. 当多次请求异常,触达异常次数百分比后,服务熔断,正常请求也会阻塞
9. 一段时间后,服务自动处于半开状态,多次请求正常后,熔断器转为关闭,服务转为正常
10. 半开状态下,如果有一次请求异常,断路器会重新回到open状态
#### @CircuitBreaker
- `@CircuitBreaker(name="service-name", fallbackMethod="method-name")`
- service-name 和yml配置中指定的服务名一致
- fallbackMethod 指定服务降级的兜底方法
### BulkHead舱壁隔离
- 舱壁隔离:限制服务访问并发数
- SemaphoreBulkhead:通过信号量实现
- FixedThreadPoolBulkhead:通过有界队列和固定大小线程池实现,先调用线程池,线程池满后,请求进入队列,队列满后,阻塞请求。
- ThreadPoolBulkhead只对CompletableFuture方法有效
1. 信号量舱壁(Semaphore BulkHead)
1. 当信号量有空闲时,进入系统的请求会直接获取信号量并开始业务处理。
2. 当信号量全被占用时,接下来的请求将会进入阻塞状态,如果阻塞状态的请求在阻塞计时内无法获取到信号量则系统会拒绝这些请求。
3. 若请求在阻塞计时内获取到了信号量,那将直接获取信号量并执行相应的业务处理。
2. 服务端8001,PayCircuitController添加Handler方法myBulkhead
3. 公共模块Feign,PayFeignApi添加BulkHead方法接口
4. 消费端cloud-consumer-feign-order82
1. 改pom:resilience4j-bulkhead
2. 写yml:resilience4j:bulkhead:
3. 业务类:OrderCircuitController添加Handler方法,通过FeignApi调用服务端
1. @BulkHead(name="service-name",fallbackMethod="method-name",type="bulkhead-type")
2. name:服务名,与配置文件保持一致
3. fallbackMethod:访问异常后的兜底方法
4. 设置并发请求,超过并发限制数值后,新的请求会阻塞,直到取到信号量
#### @BulkHead
- @BulkHead(name="service-name",fallbackMethod="method-name",type="bulkhead-type")
### RateLimiter限流
- 限流,就是限制并发访问,或者对一个时间窗口内的请求限速,以保护系统。
- 限流算法
1. 漏桶算法:源头高流量,漏铜固定容量,匀速流出。容量满后,拒绝新请求。
2. 令牌桶算法:匀速向桶中添加令牌,从桶中取出令牌执行请求。令牌空后,拒绝新请求。
3. 滚动时间窗:允许固定数量的请求进入,叠加超过阈值就拒绝新请求,等下一个时间段进入。问题:临界时间点可能出现双倍请求负载。
4. 滑动时间窗:时间片划分成固定窗口,开始时间点和结束时间点随时间移动,避开计数器的临界点的问题。
1. 服务端8001:PayCircuitController添加限流Handler方法
2. OpenFeign:添加API接口
3. 客户端82:
1. 改pom:resilience4j-ratelimiter
2. 写yml:resilience4j:ratelimiter:
3. OrderCircuitController添加方法,@RateLimiter指定降级方法
4. 疯狂刷新请求,验证效果
#### @RateLimiter
- @RateLimiter(name = "cloud-payment-service", fallbackMethod = "myRateLimitFallback")
## 分布式链路追踪
- 微服务框架中,多个服务节点间的互相调用会形成一条复杂的分布式服务调用链路,任何一环出现高延时或错误都会引起整个请求最后的失败。
- 分布式链路追踪(Distributed Tracing)就是对分布式请求进行日志记录、性能监控,还原成调用链路进行展示。
- Sleuth 停更维护,替换方案 Micrometer Tracing。
- MicrometerTracing 负责数据收集。
- ZipKin 负责数据展现。
### ZipKin
- 下载,运行:java -jar zipkin.jar
- 登录控制台:http://localhost:9411/zipkin/
### MicrometerTracing
1. 改pom,父工程、客户端82、服务端8001 均需导入链路追踪依赖
- MicrometerTracing是一个门面工具,自身并没有实现完整的链路追踪系统,需要引入第三方链路追踪系统的依赖。
2. 写yal,服务端8001
3. 业务类,服务端8001新建PayMicrometerController
4. Feign,common模块PayFeignApi新加Handler
5. 客户端82,新建OrderMicrometerController
6. 启动ZipKin,启动微服务,访问 http://localhost:9411/zipkin/
## Gateway
- Spring Cloud Gateway本身也是一个微服务,需要注册进服务注册中心。
- Route路由(路由转发),Predicate断言(断言判断),Filter过滤器(执行过滤器链)。
1. API网关
- 网关,是系统对外的的唯一入口,服务端不对外暴露。提供路由请求、鉴权、监控、缓存、限流等功能。用户只需和网关交互,由网关根据业务情况访问不同的微服务。
- API网关,处于REST API服务之前,用来管理授权、访问控制和流量限制等,保护、增强和控制API服务的访问。
- 请求路由:所有客户端的请求首先会先转到API网关,网关将再将请求路由到相应的服务。使用API组合模式处理请求、聚合结果。
- 过滤器:分为GatewayFilIer、GlobalFilter。可以对请求和响应进行处理。
2. Spring Cloud Gateway
- SpringCloud Gateway 是微服务网关,为微服务应用程序提供路由、负载均衡、统一鉴权、限流等功能。目标是替代 Zuul。
- SpringCloud Gateway 基于WebFlux框架实现,底层使用了高性能的Reactor模式通信框架Netty。
- Spring Cloud Gateway组件的核心是一系列的过滤器,通过这些过滤器可以将客户端发送的请求转发(路由)到对应的微服务。
- Spring Cloud Gateway是加在整个微服务最前沿的防火墙和代理器,隐藏微服务结点IP端口信息,从而加强安全保护。
3. Nginx
- Nginx 是高性能HTTP和反向代理Web服务器,同时提供了IMAP/POP3/SMTP服务。占有内存少,并发能力强。
- 高性能:Nginx采用异步、非阻塞的事件驱动模型,可以支持高并发请求,并且消耗较少的系统资源。
- 轻量级:Nginx的二进制文件很小,可以快速启动并占用较少的内存。
- 可靠性高:Nginx的代码被设计为高可靠性的,可以在长时间运行中保持稳定性。
- 可扩展性好:Nginx可以通过添加第三方模块或编写自定义模块来扩展其功能。
- 支持热部署:Nginx可以在不停止服务的情况下重新加载配置文件和动态模块。
1. 反向代理:客户端不知道要访问的目标,只需要将请求发送至反向代理服务器,由反向代理服务器决定需要访问哪个资源
2. 正向代理:客户端知道要访问的目标,由代理完成请求并将结果返回。
3. 负载均衡策略
- 轮询(默认)每个请求会按时间顺序逐一分配到不同的后端服务器。
- weight(权重)在轮询策略的基础上指定轮询的几率。
- ip_hash(相同的客户端的请求一直发送到相同的服务器,解决session不能跨服务器问题)
- url_hash(使每个url定向到同一个后端服务器,要配合缓存命中来使用。)
- least_conn(把请求转发给连接数较少的后端服务器。)
4. 区别
- Nginx,置于API网关前,负责请求转发,做API网关的负载均衡,是流量网关。定义全局性的、与业务应用和服务完全无关的策略网关。比如全局流量监控、日志记录、全局限流、黑白名单控制、接入请求到业务系统的负载均衡等。
- API网关,以集群的方式做高可用,聚合服务,是业务网关。对业务应用和服务提供特定的流控策略、缓存策略、鉴权认证策略等。
- 过滤器用于拦截单个服务,网关用于拦截整个微服务。
- Nginx是C语言编写的服务器端负载均衡器,Gateway是Java本地负载均衡。
5. [一文速通Nginx网关与gateway网关区分](https://cloud.tencent.com/developer/article/2350984)
### cloud-gateway-9527
1. 建Module:网关服务 cloud-gateway-9527
2. 改pom:spring-cloud-starter-gateway
3. 写yml:
- Gateway服务也要注册到Consul,服务名 cloud-gateway
- spring:cloud:gateway,可以配置多个路由,指定后端服务的服务名、指定后端服务可以匹配的uri。
### Route路由
- Route(路由)是构建网关的基本模块,由ID、目标URI、一系列的断言和过滤器组成,如果断言为true则匹配该路由。
1. 服务端8001,新建PayGateWayController,提供get功能
2. Feign
- API接口新建方法调用后端服务端
- 修改@FeignClient(value = "cloud-gateway")为网关服务名
- 实现:client82 -> Feign -> gateway9527 -> service8001
3. 客户端82,新建OrderGateWayController
4. 功能测试
1. 启动客户端82、服务端8001、网关9527
2. 测试1:服务:8001功能正常、通过gateway9527访问服务端8001正常
3. 测试2:启动网关服务9527,客户端82通过FeignAPI调用网关服务9527,再调用服务端正常
4. 测试3:关闭网关服务9527,客户端82通过FeignAPI调用后端服务失败
### Predicate断言
- Predicate(断言)匹配条件,可以匹配HTTP请求中的所有内容,如果请求与断言相匹配则进行路由。
- 配置方式:Shortcut Configuration用=和,分割; Fully Expanded Arguments用:分割。
- Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理。
- 常用断言API
1. After Route Predicate
2. Before Route Predicate
3. Between Route Predicate
- 指定时间范围内的请求有效,满足的放行
- 通过执行代码获取配置的时间格式zbj:` ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区 `
4. Cookie Route Predicate
- 指定请求必须包含Cookie,参数:Cookie name值、正则表达式。都匹配的请求执行。
1. 原生命令测试
- 不带cookie参数:curl http://localhost:9527/pay/gateway/get/1
- 自带cookie参数:curl http://localhost:9527/pay/gateway/get/1 --cookie "username=fan"
2. Postman测试:在Header中添加kv:key填写Cookie,value填写username=fan
3. 浏览器测试:F12进入开发者模式,在Application页面找到Cookie,添加Name填写username,Value填写fan
5. Header Route Predicate
- 指定请求头信息
- 原生命令测试:curl http://localhost:9527/pay/gateway/get/1 -H "X-Request-Id:123456"
- Postman测试:在Header中添加kv:key填写X-Request-Id,value填写123456
6. Host Route Predicate Da
- 指定匹配的域名列表,用.号作为分隔符。
- 原生命令测试:curl http://localhost:9527/pay/gateway/get/3 -H "Host:www.xfanonymous.com"
- Postman测试:在Header中添加kv:key填写Host,value填写www.xfanonymous.com
7. Path Route Predicate
- 指定能匹配后端服务提供的URL
8. Query Route Predicate
- 指定URL中必须包含的?param
- Postman测试:http://localhost:9527/pay/gateway/get/3?username=123
9. RemoteAddr route predicate
- 指定外部能访问的远程访问地址
- 访问时不能再使用localhost:9527,而是192.168.124.x:9527
10. Method Route Predicate
- 指定请求方法:GET POST DELETE PUT
- 自定义断言API
1. 自定义类要以 xxxRoutePredicateFactory 结尾: MyRoutePredicateFactory
2. 继承 AbstractRoutePredicateFactory<> 类,<>中填写xxxRoutePredicateFactory.Config
3. @Component
4. 可以参考自带的断言API:AfterRoutePredicateFactory、BeforeRoutePredicateFactory....
5. 空参构造方法,内部调用super
6. 添加Config类,定义配置文件中的参数
7. 重写apply方法,完成断言实现
8. 在yml配置类中配置自定义断言,以 -xx 自定义断言类的前缀作为配置参数顶层
9. 重写shortcutFieldOrder方法,否则yml配置文件无法进行Shortcut Configuration方式配置
### Filter过滤器
- SpringMVC里的拦截器,对整个SpringMVC的请求进行统一处理。preHandle()、postHandler()。
- Servlet里的过滤器Filter,指Spring框架中GatewayFilter的实例,pre、post分别在请求执行前后调用,用于修改请求和响应信息。
- pre类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等; post类型的过滤器可以做响应内容、响应头的修改,日志输出,流量监控等。
- 步骤
1. 8001服务端PayGateWayController新增方法
2. Gateway9527配置文件新增Gateway配置pay_routh3
3. 通过9527访问后端服务 http://localhost:9527/pay/gateway/filter
- 内置过滤器配置
1. 请求头 RequestHeader AddRequestHeader 配置,拦截请求后,在请求头添加kv,修改响应信息
2. 请求头 RequestHeader RemoveRequestHeader 配置,拦截请求后,删除请求头的key
3. 请求头 RequestHeader SetRequestHeader 配置,拦截请求后,修改指定请求头key的value
4. 请求参数 RequestParameter AddRequestParameter 配置,新增url请求参数,?customerId=9527001
5. 请求参数 RequestParameter RemoveRequestParameter 配置,删除url请求参数,?customerName的value会为null
6. 回应头 ResponseHeader AddResponseHeader 配置,新增回应头Header参数kv
7. 回应头 ResponseHeader SetResponseHeader 配置,设置回应头参数值
8. 回应头 ResponseHeader RemoveResponseHeader 配置,删除回应头参数
9. 路径配置 PrefixPath,指定断言拦截路径前缀,实现对外隐藏部分路径,URL为predicates:-Path,实际路径为 PrefixPath + Path
10. 路径配置 SetPath,指定实际访问路径,实现断言拦截器对外路径随便修改,1)替换predicates:-Path中固定路径 2)URL中占位符{segment}部分直接拼接,不替换。
- 浏览器访问地址: http://localhost:9527/XYZ/abc/filter,{$segment}就是filter,如果是filter333不能匹配后端服务的,无法访问
- 实际微服务地址:http://localhost:9527/pay/gateway/filter
11. 路径配置 RedirectTo,重定向,状态码302,指定跳转地址
12. 全局配置 default-filters,指定全局Filter设置,对所有Gateway生效
- 自定义过滤器
1. 自定义全局过滤器,统计所有Handler请求耗时
- gateway9527新建 MyGlobalFilter
- 实现全局默认过滤接口 GlobalFilter,实现方法 filter(),记录接口调用时间,求解接口请求耗时
- 实现 springframework.core 接口 Ordered,实现方法 getOrder() 指定优先级,数字越小,优先级越高。
- 注解 @Component、@Slf4j 酸辣粉日志
2. 自定义单一过滤器
1. gateway9527新建 MyGatewayFilterFactory,类名以xxxGatewayFilterFactory结尾
2. 继承AbstractGatewayFilterFactory,<>内为当前类.Config
3. @Component
4. 无参构造函数,内部调用super
5. 新建Config内部类,定义配置文件中的参数
6. 重写apply()、shortcutFieldOrder()方法
7. 在yml配置类中配置自定义断言,以 -xx 自定义断言类的前缀作为配置参数顶层
8. 格式:-自定义类前缀 = 指定代码内部类Config中的参数值
# SpringCloudAlibaba
- spring-cloud-alibaba-dependencies
- 2022.0.0.0-RC2
## Nacos
- Nacos = Dynamic Naming and Configuration Service
- Nacos 是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理的平台,对标 Spring Cloud Consul。
- Nacos特性
1. 支持基于DNS和基于RPC的服务发现。
2. 支持传输层和应用层的健康检查,阻止向不健康的主机或服务实例发送请求。
3. 对于复杂的云环境和网络拓扑环境中(如 VPC、边缘网络等)服务的健康检查,
4. Nacos 提供了 agent 上报模式和服务端主动检测2种健康检查模式。
5. Nacos 还提供了统一的健康检查仪表盘,帮助您根据健康状态管理服务的可用性及流量。
- 下载安装,nacos/bin下,单机运行cmd执行: startup.cmd -m standalone,关闭运行 shutdown.cmd
- 登录:http://localhost:8848/nacos,默认账号密码都是nacos
- 服务注册中心、配置中心
### 服务注册与发现
1. 建Module:服务端 cloudalibaba-provider-payment9001、消费端 cloudalibaba-consumer-nacos-order83
2. 改pom:依赖导入 spring-cloud-starter-alibaba-nacos-discovery,消费端还需要 spring-cloud-starter-loadbalancer
3. 写yml:服务端口号、服务注册名、Nacos地址、消费者要访问的服务名
4. 主启动:@SpringBootApplication、@EnableDiscoveryClient
5. 业务类:PayAlibabaController
6. 负载均衡,服务配置拷贝
1. Services->选中服务右键->Copy Configuration->
2. Name:Main9002(CopyFrom9001)->Modify Options->Add VM Options->-DServer.port=9002->启动9002
### 分布式配置管理
1. 建Module:cloudalibaba-config-nacos-client3377
2. 改pom:spring-cloud-starter-bootstrap、spring-cloud-starter-alibaba-nacos-config
3. 写yml:两个配置文件,先通过bootstrap.yml从配置中心获取相关配置,满足动态刷新和全局广播通知,再启动项目,通过application.yml指定生效的配置
1. 必须使用bootstrap.yml配置文件配置 Nacos Server
2. bootstrap.yml:
1. 服务名 spring:application:name
2. Nacos注册中心地址 cloud:nacos:discovery:server-addr:
3. Nacos配置中心地址 cloud:nacos:config:server-addr:
3. application.yml:
1. 服务端口号server:port
2. 指定在Nacos配置中心拉取的生效的配置文件
4. 主启动
5. 业务类:新建NacosConfigClientController
1. `@RefreshScope` 使当前类支持在Nacos配置的动态刷新
2. `@Value("${}")` 从Nacos获取配置信息
6. 在Nacos控制台的配置管理模块,新建配置,输入配置的DataId
1. DataId命名规则:`${prefix}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}`
2. ${prefix} 默认为bootstrap.yml的 spring.application.name,可以通过 spring.cloud.nacos.config.prefix 指定。
3. ${spring.profiles.active} 为application.yml指定的生效配置文件名 dev/prod/test
4. ${spring.cloud.nacos.config.file-extension} 为bootstrap.yml指定的Nacos中配置的配置格式
5. `${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}`
6. DataId例:nacos-config-client-dev.yml
7. 配置管理
1. Namespace:进行配置空间划分,出厂默认保留空间public。控制台新建命名空间,建议指定命名空间ID,不可修改。
1. 通过bootstrap.yml中的spring.cloud.nacos.config.namespace指定生效配置所在命名空间
2. Group:通过bootstrap.yml中的spring.cloud.nacos.config.group指定生效配置所在分组,默认分组DEFAULT_GROUP
3. DataId:通过application.yml中的spring.profiles.active指定生效文件名后缀,在控制台创建对应DataId
## Sentinel
- Sentinel(岗哨,哨兵) 面向分布式、多语言结构化服务架构的流量治理组件,对标SpringCloud CircuitBreaker。
- [Sentinel](https://sentinelguard.io/zh-cn/)
- 从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度保障微服务的稳定性。
- 下载安装:java -jar sentinel-dashboard-1.8.6.jar
- 端口:前端控制台8080默认开启、后端默认8719,如果8719被占用会自动依次+1扫描,直至找到未被占用的端口。
- 控制台:http://localhost:8080
### 分布式问题
- 服务雪崩:微服务间存在复杂调用关系,由于单一故障导致系统发生级联故障而崩溃。
- 服务熔断:熔断就是保险丝,服务端由于访问压力过大导致调用失败过多时,消费端为保证系统的整体可用性,暂时断开与消费端的连接。然后调用服务降级的方法返回友好提示。
- 服务降级:就是兜底方案,服务无法完成正常调用流程时,使用默认的兜底方案返回数据。
- 服务限流:防止瞬时流量过大导致系统崩溃,保护服务节点。漏桶算法、令牌桶算法、滚动时间窗、滑动时间窗。
- 服务隔离:舱壁,按规则将系统划分成多个服务模块,服务模块间互相独立,故障不会扩散。信号量隔离、线程池隔离。
- 服务超时:消费端调用服务端时,超过最大响应时间未响应的,断开上下游服务间的请求连接,释放资源。
### 实时监控
1. 建Model:新建服务cloudalibaba-sentinel-service8401
2. 改pom:spring-cloud-starter-alibaba-sentinel
3. 写yml:spring.cloud.sentinel,将8401服务注册到Nacos8848,用Sentinel监控8401
4. 主启动:@SpringBootApplication、@EnableDiscoveryClient
5. 业务类:FlowLimitController新建测试方法 testA、testB
6. 测试:访问Sentinel控制台http://localhost:8080
7. 在控制台实时监控模块查看访问情况,Sentinel是懒加载,只有访问URL后,有需要才加载。
### 流控规则
- Sentinel 可以监控应用的QPS流量或者并发线程数等指标,对流量进行控制。避免服务被瞬时高并发流量击垮,保证服务的高可靠性。
- 流控模式
1. 直接模式
1. 默认的流控模式,当接口达到限流条件时,直接开启限流功能。
2. 控制台->流控规则-》新增流控规则-》资源名/testA-》针对来源default-》阈值类型QPS-》单机阈值1-》非集群-》流控模式直接-》流控效果快速失败
3. 表示限制1秒钟内查询1次,否则快速失败,报默认错误 Blocked by Sentinel (flow limiting)
2. 关联模式
1. 与其他服务建立关联,当关联的服务达到限制阈值时,就限制当前服务
2. 控制台->流控规则-》新增流控规则-》资源名/testA-》针对来源default-》阈值类型QPS-》单机阈值1-》非集群-》流控模式关联-》关联资源/testB-》流控效果快速失败
3. 当关联资源/testB的QPS阀值超过1时,就限流/testA的访问
4. 启动 Apache JMeter 5.6.2,双击运行 bin\jmeter.bat
5. JMeter客户端配置压力测试:TestPlan->创建ThreadGroup->设置用户线程数80-》运行时长4s-》循环次数1
6. 添加HTTP Request http://localhost:8401/testB-》启动
7. 4s内大批量访问testB导致testA访问失败,报默认错误 Blocked by Sentinel (flow limiting)
3. 链路模式
1. 访问同一目标,对不同链路来源分别进行限制
2. service8401新建FlowLimitService,FlowLimitController新建方法testC() testD()调用Service方法common
3. 改yml:web-context-unify: false Sentinel链路模式流控配置,设置上下文是否为同一链路,controller对service调用不认为是同一根链路
4. Sentinel 控制台-》簇点链路-》testC common-> 新增+流控—》单机阈值1-》流控模式链路-》入口资源 /testC -》快速失败
5. 当访问testC超过阈值时,再次访问报错。testD没有配置流控限制,可以无限制访问。
- 流控效果
1. 快速失败:默认。直接失败,抛出异常Blocked by Sentinel (flow limiting)
2. WarmUP:预热,从 单机阈值/默认冷却因子3 = 初始访问数 开始,经过预热时间后,逐渐上升到单机阈值。逐渐放开流量,保护系统,避免访问数直接达到阈值限制。
3. 排队等待:用于处理间隔性突发的流量,在空闲期间逐渐处理请求,而不是直接拒绝多余请求。
1. FlowLimitController新建方法testE
2. Sentinel控制台-》新增流控规则-》资源名testE-》针对来源default-》阈值类型QPS-》单机阈值1-》非集群-》流控模式直接-》流控效果排队等待-》超时时间10000ms
3. 按单机阈值,一秒钟通过一个请求,10秒后的请求作为超时处理,放弃。
4. JMeter客户端-》线程数20-》持续时间1-》循环次数1。1s发送20个请求到testE
5. 效果:JMeter发送的20个请求中,只有10个请求通过,按顺序打印请求的时间
### 熔断规则
- Sentinel 熔断降级,对不稳定的弱依赖服务调用暂时切断,避免局部不稳定导致整体雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。
- [Sentinel熔断降级](https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7)
- 熔断策略
1. 慢调用比例 (SLOW_REQUEST_RATIO)
1. 当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。
2. 设置RT(最大的响应时间),请求响应时间大于该值则统计为慢调用。
3. 经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
4. FlowLimitController新建方法testF,设置线程访问超时
5. Sentinel testF + 熔断规则-》新增熔断规则-》熔断策略 慢调用比例-》最大RT-》最小请求数-》比例阈值-》统计时长1000ms-》熔断时长
6. JMeter客户端-》线程数10-》持续时间1-》循环次数Infinite
7. 在统计时长内,testF请求数目一秒钟10个线程>大于最小请求数5,且慢调用比例>比例阈值,断路器打开微服务不可用,进入熔断状态5秒。停止压测后,断路器关闭,服务正常访问。
2. 异常比例 (ERROR_RATIO)
1. 当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。
2. 经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
3. 异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
4. FlowLimitController新建方法testG,设置线程访问异常。直接访问testG,会异常报错
5. Sentinel testG + 熔断规则-》新增熔断规则-》熔断策略 异常比例-》比例阈值0.2-》熔断时长5s-》最小请求数5-》统计时长1000ms
6. JMeter客户端-》线程数10-》持续时间1-》循环次数Infinite
7. 在统计时长内,实际请求数目>设定的最小请求数,且请求异常比例>比例阈值,断路器进入熔断状态。不再报错异常error,而是服务熔断+服务降级,提示 Blocked by Sentinel (flow limiting)。
3. 异常数 (ERROR_COUNT)
1. 当单位统计时长内的异常数目超过阈值之后会自动进行熔断。
2. 经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
3. FlowLimitController新建方法testH,设置线程访问异常。
4. Sentinel testF + 熔断规则-》新增熔断规则-》熔断策略 异常数-》异常数1-》熔断时长5s-》最小请求数2-》统计时长1000ms
5. JMeter客户端-》线程数10-》持续时间1-》循环次数Infinite
6. 当请求异常数大于1时,会触发熔断操作,微服务不可用,熔断的时长为5秒。访问时不再报错异常error,而是服务降级提示Blocked by Sentinel (flow limiting) 。
### @SentinelResource
- `@SentinelResource(value = "common")`是流量防护组件注解,用于指定防护资源,对配置的资源进行流量控制、熔断降级等功能。
1. 新建业务类 RateLimitController
2. 方法byUrl()验证默认对URL添加流控,Sentinel触发熔断后,返回默认限流提示Blocked by Sentinel (flow limiting)
3. 方法byResource()验证通过注解@SentinelResource(value = "limitResourceName", blockHandler = "handlerName")自定义资源名和限流提示
4. 方法doAction()验证通过注解@SentinelResource()自定义降级方法,fallback指定程序异常后,要执行的降级方法
### 热点规则
- 热点即经常访问的数据,统计或限制某个热点数据中访问频次最高的TopN数据,并对其访问进行限流或者其它操作
1. RateLimitController方法testHotKey()添加两个参数p1、p2
2. 注解@SentinelResource(),value指定资源名与控制台簇点链路中一致,blockHandler指定触发阈值规则后的限流提示方法
3. Sentinel控制台-》簇点链路-》找到对应资源名-》添加热点规则-》参数索引,指定要监控限流的参数-》限流默认QPS模式
4. 只有带有?p1=xx的高流量访问会触发限流
5. 参数例外项:当p1参数为特定值时,可以放通不触发限流,即对指定参数值设置新的阈值。
6. Sentinel控制台-》热点规则-》编辑配置的规则项-》高级选项-》选定相同的参数类型-》参数值-》限流阈值-》添加按钮
### 授权规则
- 授权规则,对请求来源设置黑白名单
1. 新建EmpowerController
2. 创建请求来源处理器转换MyRequestOriginParser实现接口RequestOriginParser和方法parseOrigin()
3. 例如:可以设置serverName为URL的必传参数,在此方法中对?serverName=test设置为黑名单
4. Sentinel控制台-》簇点链路-》找到对应资源名-》添加授权规则-》资源名-》流控应用,指定参数值-》选择黑白名单-》新增
### 规则持久化
- 防止Sentinel中配置的规则消失,可以将配置信息保存到Nacos。
1. 服务cloudalibaba-sentinel-service8401
2. 改pom:sentinel-datasource-nacos
3. 写yml:spring:cloud:sentinel:datasource,指定Sentinel流控规则持久化到Nacos
4. Nacos控制台:新建配置-》命名空间-》DataID建议和服务名相关-》Group和yml一致-》配置格式JSON-》配置内容:
```json
[
{
"resource": "/rateLimit/byUrl", // 资源名
"limitApp": "default", // 来源应用
"grade": 1, // 阈值类型,0表示线程数,1表示QPS
"count": 1, // 单机阈值
"strategy": 0, // 流控模式,0表示直接,1表示关联,2表示链路
"controlBehavior": 0, // 流控效果,0表示快速失败,1表示Warm Up,2表示排队等待
"clusterMode": false // 是否集群
}
]
```
5. 访问配置的资源名路径,Nacos配置自动配置到Sentinel中
6. Sentinel是懒加载,只有访问URL后,有需要才加载
## OpenFeign和Sentinel集成
1. 原服务,消费端cloudalibaba-consumer-nacos-order83通过OpenFeign调用cloudalibaba-provider-payment9001
1. 当服务端异常访问error时,消费端所有方法各自添加fallback导致代码膨胀。策略:feign接口中定义的全部方法统一配置服务降级方法。
2. 服务端配置了sentinel流控规则,限流策略同样会触发@SentinelResource的blockHandler方法。策略:降级方法统一由Feign处理。
2. 代码实现
1. cmd启动Nacos:startup.cmd -m standalone
2. jar启动Sentinel:java -jar sentinel-dashboard-1.8.6.jar
3. 服务端cloudalibaba-provider-payment9001
1. 改pom:spring-cloud-starter-openfeign、spring-cloud-starter-alibaba-sentinel
2. 写yml:spring:cloud:sentinel,Sentinel前后端端口配置
3. 业务类:PayAlibabaController方法getPayByOrderNo()
4. @SentinelResource() value指定资源名,blockHandler指定限流提示方法
5. fallback服务降级方法统一纳入到Feign接口处理
4. Feign接口cloud-api-commons
1. 改pom:spring-cloud-starter-openfeign、spring-cloud-starter-alibaba-sentinel
2. 接口API:新建PayFeignSentinelApi接口方法getPayByOrderNo(),就是服务端9001对外提供的handler
3. @FeignClient() value指定服务端在Nacos中注册的服务名,fallback指定降级方法
5. cloud-api-commons新建PayFeignSentinelApiFallBack类
1. PayFeignSentinelApiFallBack类实现Feign接口
2. 实现接口中对应的方法,返回触发异常后的降级处理结果
6. 消费端cloudalibaba-consumer-nacos-order83
1. 改pom:引入自定义API通用包cloud-api-commons、spring-cloud-starter-openfeign、spring-cloud-starter-alibaba-sentinel
2. 写yml:Feign+Sentinel集成,激活Sentinel对Feign的支持,feign.sentinel.enabled:true
3. 主启动:@EnableFeignClients 开启OpenFeign客户端服务调用功能。
4. 业务类:OrderNacosController方法getPayByOrderNo()通过Feign调用消费端9001
5. 服务启动报错:'PayFeignSentinelApi' that could not be found. 原因:alibaba版本低还不兼容。
6. 解决:临时降级,boot版本3.2.0降为3.0.9,cloud版本2023.0.0降为2022.0.2。
## Gateway和Sentinel集成
- 访问gateway9528保护payment9001
1. 建module:新建服务cloudalibaba-sentinel-gateway9528
2. 改pom:spring-cloud-starter-gateway、sentinel-transport-simple-http、sentinel-spring-cloud-gateway-adapter、javax.annotation-api
3. 写yml:spring.cloud.gateway 创建路由规则,断言predicates匹配后端提供服务的9001的路由地址、URL
4. 主启动
5. [Sentinel网关限流](https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81)
6. 配置类:根据官网规则编写配置文件 GatewayConfiguration,初始化方法initBlockHandler();部分自定义
7. 测试:可以访问gateway9528保护payment9001
## Seata
- 一次业务操作需要跨多个数据源或多个系统进行远程调用,就会产生分布式事务问题。关系型数据库提供单机事务,本地事务保证每个服务数据一致性,全局数据一致性同Seata实现。
- Seata是开源的分布式事务框架,解决微服务架构下分布式事务问题。
- Seata通过1+3对分布式事务的协调和控制
1. XID 是全局事务的唯一标识,在服务的调用链路中传递,绑定到服务的事务上下文中。
2. TC (Transaction Coordinator)事务协调器,就是Seata,维护全局和分支事务的状态,驱动全局事务提交或回滚。
3. TM (Transaction Manager)事务管理器,@GlobalTransactional标注事务发起者,定义全局事务的范围,开始、提交、回滚全局事务。
4. RM (Resource Manager)资源管理器,就是MySQL数据库本身,可以是多个,管理分支事务处理的资源,向TC注册分支事务、报告分支事务状态、驱动分支事务提交或回滚。
- Seata分布式事务执行流程
1. TC 以SeataServer服务器形式独立部署,TM和RM以SeataClient形式集成在业务微服务中运行
2. TM 向 TC 申请开启全局事务,全局事务创建成功并生成全局唯一的XID
3. XID 在微服务调用链路的上下文中传播
4. RM 分别向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖
5. TM 向 TC 发起针对 XID 的全局提交或回滚决议
6. TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求
- Seata AT 模式,是非侵入式的分布式事务解决方案,Seata 自带对数据库操作的代理 DataSourceProxy。
1. 两阶段提交
2. 第一阶段:Seata解析SQL语义,找到要更新的业务数据,保存快照before image,执行数据更新后,保存快照after image,生成行锁。在一个数据库事务内完成操作,保证原子性。
3. 第二阶段:正常提交,只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。
4. 第二阶段:异常回滚,需要回滚一阶段已经执行的SQL
1. 先校验,对比当前数据和after image
2. 如果一致说明没有脏写,用before image还原业务数据,删除中间数据和undo_log。
3. 如果不一致,需要转人工处理。
- 环境初始化
1. 建库:```sql CREATE DATABASE seata; USE seata;```
2. 建表:https://github.com/seata/seata/blob/develop/script/server/db/mysql.sql
3. 修改seata-server-2.0.0\conf\application.yml配置文件,记得先备份
4. 启动nacos: startup.cmd -m standalone
5. 启动seata:CMD执行seata-server-2.0.0\bin\seata-server.bat
6. 访问nacos: http://localhost:8848,服务seata-server注册到了SEATA_GROUP分组
7. 访问seata:http://localhost:7091
- 业务初始化
1. 建库:seata order订单数据库、 seata storage存储数据库、seata account账户信息数据库。
2. 日志表:AT模式下,三个数据库都要创建各自的undo_log回滚日志表
3. 业务表:三个数据库分别创建业务表
- 代码初始化
1. 下订单->减库存->扣余额->改(订单)状态
2. Mybatis逆向数据库mybatis_generator01,修改config.properties连接的数据库、修改generatorConfig.xml,maven运行插件mybatis-generator:generate
3. 在mybatis_generator01获取自动生成的Order,OrderMapper
4. Feign:cloud-api-commons新建API接口,库存接口StorageFeignApi、账户接口Seata分布式事务管理,库存类
- 代码实现
1. 建Model:新建服务seata-order-service2001、seata-storage-service2002、seata-account-service2003
2. 改pom: nacos、seata、feign、LB
3. 写yml: 在Nacos的注册信息 seata.registry、TC服务集群 seata.tx-service-group、默认配置seata.service
4. 主启动:@SpringBootApplication、@MapperScan("seata.order.mapper")、@EnableDiscoveryClient、@EnableFeignClients
5. 业务类:seata-order-service2001
1. entities类,实现序列化接口 implements Serializable,添加@ToString注解生成toString()方法
2. Service接口OrderService添加创建订单方法void create(Order order);
3. ServiceImp实现类OrderServiceImpl
1. 注解 @Service、酸辣粉 @Slf4j
2. 通过mapper接口执行数据库操作 @Resource
3. 实现Service接口的业务方法 implements Service
4. 通过Seata上下文 RootContext.getXID(); 获取全局事务ID,方便定位
5. 创建订单后,将订单初始状态数据插入数据库。插入且能查出,说明插入数据成功。
6. 通过Feign调用服务扣减库存、扣减账户余额
7. 修改订单状态,表示下订单完整流程结束
6. @GlobalTransactional
1. OrderServiceImpl兼具TM、RM角色,@GlobalTransactional用于TM事务起始方法
2. `@GlobalTransactional(name = "zzyy-create-order",rollbackFor = Exception.class)`
3. name指定全局事务id,即Seata控制台transactionName项
4. rollbackFor指定触发事务管理的异常
- 功能测试
1. 在账户业务类AccountServiceImpl构造异常,检验数据库数据是否会回退
2. Seata控制台,TransactionInfo查看全局事务信息,调用链路xid组成:ip+端口+id、transactionName= "zzyy-create-order"
3. Seata控制台,GlobalLockInfo查看全局锁信息,三个服务为三个分支,xid、transactionId、branchId、resourceId、tableName
4. 分支出现异常后,数据库数据回滚,undo_log日志在回滚后自动删除