# springcloud-atguigu2024 **Repository Path**: pish7/springcloud-atguigu2024 ## Basic Information - **Project Name**: springcloud-atguigu2024 - **Description**: https://www.bilibili.com/video/BV1gW421P7RD/ - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-05-01 - **Last Updated**: 2025-10-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: SpringCloud, Java, SpringBoot ## README # spring cloud [项目视频](https://www.bilibili.com/video/BV1gW421P7RD/) ## 模块说明 | 模块 | 说明 | |-----------------------|----------------------------------------------| | mybatis_generator | MyBatis 代码生成工具 | | commons | 公用模块,包含 Util 工具类、接口调用工具类、DTO类型、统一异常处理、统一服务响应 | | base-provider-payment | 支付模块(微服务提供者) | | base-consumer-order | 订单模块(微服务调用者,将调用支付模块) | ## commons 这个是一个公共模块,其他模块都可能会调用这个模块的类,注意以下几点: 1. 包名与其他模块保持一致,这样方便其他模块调用 2. 这个包需要安装(clean, install),然后才能给其他模块使用 3. 其他模块需要在 pom.xml 中添加对 commons 模块的依赖 ## mybatis_generator 该模块是工具模块,专用于为数据表自动生成 DAO 层代码,需要先在数据库中创建好数据表后,再执行命令生成代码。 执行命令需要进入模块目录,然后运行 `mvn mybatis-generator:generate` 代码生成后,需要拷贝到目标模块中,mybatis_generator 模块中不保留生成的 entity/ 和 mapper/ https://github.com/abel533/Mapper https://mapper.mybatis.io/ ## 运行 web 项目时指定端口号 [参数链接](https://stackoverflow.com/questions/21083170/how-to-configure-port-for-a-spring-boot-application/56766604) ```shell # 指定端口为 8085 mvn spring-boot:run -Dspring-boot.run.jvmArguments='-Dserver.port=8085' ``` ## Swagger Swagger3 调用方式 `http://<你的主机 IP 地址>:/swagger-ui/index.html`,例如: [链接](http://127.0.0.1:8001/swagger-ui/index.html) ## RestTemplate RestTemplate 提供了多种便捷访问远程 Http 服务的方法, 是一种简单便捷的访问 restful 服务模板类,是 Spring 提供的用于访问 Rest 服务的客户端模板工具集 ## Consul 配置中心、服务注册与发现 UI 访问地址: http://127.0.0.1:8500 ### 集成服务注册与发现 不需要在 Consul 服务器上进行配置,其他服务直接注册上去即可 1. 添加 pom 依赖 ```xml org.springframework.cloud spring-cloud-starter-consul-discovery ``` 2. bootstrap.yml 配置 ```yaml spring: cloud: consul: host: localhost port: 8500 discovery: prefer-ip-address: true service-name: ${spring.application.name} ``` 3. 主启动添加 `@EnableDiscoveryClient` 4. RestTemplate 添加 `@LoadBalanced` 由于 consul 天生支持负载均衡,使用 RestTemplate 通过服务地址调用远程服务时,RestTemplate 需要启用负载均衡 ### 集成配置中心 需要在 consul 服务器上添加配置,其他服务则从 consul 服务拉取配置信息 consul 可以区分环境,为开发环境、生产环境等设置不同的配置 1. 添加 pom 依赖 ```xml org.springframework.cloud spring-cloud-starter-consul-config org.springframework.cloud spring-cloud-starter-bootstrap ``` 2. bootstrap.yml 配置 配置中心服务器的连接信息需要配置在 bootstrap.yml 中而不是在 application.yml 中。 Spring Cloud 会创建一个 `Bootstrap Context`,作为 Spring 应用的 `Application Context` 的父上下文。 初始化的时候,`Bootstrap Context` 负责从外部源加载配置属性并解析配置。这两个上下文共享一个从外部获取的 `Environment`。 `Bootstrap` 属性有高优先级,默认情况下,它们不会被本地配置覆盖。 `Bootstrap context` 和 `Application Context` 有着不同的约定,所以新增了一个 `bootstrap.yml` 文件,保证 `Bootstrap Context` 和 `Application Context` 配置的分离。 ```yaml spring: cloud: consul: host: localhost port: 8500 config: profile-separator: '-' # 配置文件夹中的环境分隔符 default value is ",",we update '-' format: YAML # 配置文件的类型 ``` #### 多环境 配置中可以为不同的环境定义不同的配置,如 `xxxx-dev` 就表示 `dev` 环境,至于要启动哪个环境,需要在 `application.yml` 中配置 ```yaml #application.yml spring: profiles: active: dev # 多环境配置加载内容 dev/prod, 不写就是默认 default 配置 ``` #### 示例 假设要为 `payment-service` 这个服务添加配置(dev 环境): 1. 进入 `key/Value` 2. 创建并进入 `config/` 目录 3. 创建并进入 `payment-service-dev/` 目录(表示 dev 环境,也可以指定其他环境,如果没有添加 -xxx 后缀,则表示默认环境) 4. 创建一个 `data` 文件,指定文件格式并添加配置内容 #### 注意事项 在 Spring Cloud 中,consul 有以下注意点: 1. consul 用作配置中心时,是将数据保存在它的 kv 存储中 2. 配置数据必需保存在 config 开头的目录中 3. 目录名中的环境分隔符默认是英文逗号,可以在配置中改为横杠,如 `xxxx-dev`,`xxxx-prod` 等 4. 配置数据要写在 data 文件中 #### 动态刷新 如果在 consul 服务器中更新了配置,应用要能及时获取到最新配置,需要在启动类上添加 `@RefreshScope`。 配置更新后,默认情况下应用需要有一定的延迟(55s)后才能收到更新,可以在 `bootstrap.yml` 指延迟时间 ```yaml spring: cloud: consul: config: watch: wait-time: 1 ``` [参考链接](https://docs.spring.io/spring-cloud-consul/reference/config.html) ## LoadBalancer 负载均衡 LoadBalancer 是一个客户端负载均衡器 1. 添加依赖 ```xml org.springframework.cloud spring-cloud-starter-loadbalancer ``` 2. 将 provider-payment 服务注册到注册中心(服务名称为 payment-service),并启动多个服务 ```shell mvn spring-boot:run mvn spring-boot:run -Dspring-boot.run.jvmArguments='-Dserver.port=8002' mvn spring-boot:run -Dspring-boot.run.jvmArguments='-Dserver.port=8003' ``` 3. 运行 consumer-order 服务 consumer-order 服务使用 restTemplate 发送请求,添加 `@LoadBalanced` 到生成 restTemplate 的 getBean 方法上,访问 payment-service 服务时目标地址为 `http://payment-service`。 ## OpenFeign OpenFeign 自带 LoadBalanced 负载均衡。 1. 添加依赖 ```xml org.springframework.cloud spring-cloud-starter-openfeign ``` 2. 定义接口 这个接口实现了对远程服务的 URL 和参数的封装,使用方只需要调用接口方法即可。不需要关心远程服务的 URL 和参数细节。 而且这个接口定义好之后,也可以被多个服务调用方使用。 ```java /** 支付服务对外暴露的 OpenFeign 接口 */ @FeignClient(value = "payment-service") // 指定目标服务名称 public interface PaymentApi { @PostMapping(value = "/pay/add") ResultData addPay(@RequestBody PayDTO payDTO); @GetMapping(value = "/pay/get/{id}") ResultData getPayInfo(@PathVariable("id") Integer id); } ``` 接口方法的定义格式与服务提供方的 controller 方法保持一致,下面是 payment-service 服务的控制器方法: ```java @PostMapping(value = "/pay/add") public ResultData addPay(@RequestBody PayDTO payDTO) { int i = payService.add(new Pay()); return ResultData.success("成功插入记录,返回值: " + i); } @GetMapping(value = "/pay/get/{id}") public ResultData getById(@PathVariable("id") Integer id) { Pay pay = payService.getById(id); return ResultData.success(pay); } ``` 3. 调用服务 首先要在启动类上添加 `@EnableFeignClients` 启用 OpenFeign 客户端 调用服务时只需要注入一个接口实例,直接调用接口方法即可,视觉上与调用本地方法无异。 ```java public class OrderController { // 通过 OpenFeign 调用远程服务 @Resource private PaymentApi paymentApi; public ResultData getPayInfo(Integer id) { // 使用 OpenFeign 技术调用远程服务 ResultData payInfo = paymentApi.getPayInfo(id); return payInfo; } } ``` 4. 参数配置 ```yaml spring: cloud: openfeign: client: config: default: # 默认配置 connectTimeout: 3000 # 连接超时时间 readTimeout: 3000 # 请求超时时间(默认是60000) payment-service: # 针对指定服务的配置 connectTimeout: 3000 # 连接超时时间 readTimeout: 3000 # 请求超时时间(默认是60000) ``` ### Apache HttpClient5 增强 如果不做特殊配置,OpenFeign 默认使用 JDK 自带的 HttpURLConnection 发送 HTTP 请求, HttpURLConnection 没有连接池、性能和效率也比较低。 通常可以使用 Apache HttpClient5 来代替,需要添加 httpclient5 和 feign-hc5 两个依赖 1. 添加依赖 ```xml org.springframework.cloud spring-cloud-starter-openfeign org.apache.httpcomponents.client5 httpclient5 5.3 io.github.openfeign feign-hc5 13.1 ``` 2. 添加配置 ```yaml spring: cloud: openfeign: httpclient: hc5: enabled: true ``` ### 超时重试 默认没有重试,需要自定义配置 Bean ```java @Configuration public class FeignConfig { // 超时重试 @Bean Retryer myRetryer() { // Feign 默认是不走重试的,一次请求超时了就直接返回失败 return Retryer.NEVER_RETRY; // 如果要修改 Feign 的重试策略,可以定义一个 Retryer Bean 进行配置 // 初始间隔时间为 100ms,重试间最大间隔时间为 1s,最大请求次数为 3(初始1次+重试2次) // return new Retryer.Default(100, 1, 3); } } ``` ### 压缩 添加配置即可 ```yaml spring: cloud: openfeign: compression: # 压缩 request: enabled: true response: enabled: true ``` 下面的配置内容指定压缩的请求数据类型并设置了请求压缩的大小下限,只有超过这个大小的请求才会进行压缩: 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 #最小触发压缩的大小 ### 日志 默认没有打印日志(相当于层级是 None) 1. 自定义配置 Bean ```java @Configuration public class FeignConfig { // 日志 @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } } ``` 2. 日志配置 为 com.example.cloud.apis.PaymentApi 开启日志,该接口使用了 `@FeignClient` 注解 ```yaml # feign 日志配置(具体到哪个接口,输出什么级别) logging: level: com: example: cloud: apis: PaymentApi: debug # 这里必须是 debug ``` ## CircuitBreaker 断路器 CircuitBreaker 只是一套规范和接口,落地实现者是 Resilience4J。 断路器配置在服务调用方。 - 服务熔断,类似保险丝,CLOSE 状态为接通,OPEN 为断开 - 服务降级,当服务不可用时,返回一个友好提示,例如:“服务器忙,请稍后再试” https://github.com/lmhmhl/Resilience4j-Guides-Chinese/tree/main https://github.com/lmhmhl/Resilience4j-Guides-Chinese/blob/main/core-modules/CircuitBreaker.md ## 基础依赖 ```xml org.springframework.cloud spring-cloud-starter-circuitbreaker-resilience4j org.springframework.boot spring-boot-starter-aop ``` ### 熔断 使用一个滑动窗口对请求进行统计,分为两种类型,一个是计数,一个是计时。 计数表示当请求失败(抛出异常)的次数达到一定比例时,开始熔断。 计时表示当慢请求的次数达到一定比例时,开始熔断。 1. 配置 ```yaml spring: application: name: consumer-order cloud: openfeign: circuitbreaker: # 开启 circuitbreaker 和分组激活 enabled: true group: # 没开分组永远不用分组的配置。精确优先、分组次之(开了分组)、默认最后 enabled: true resilience4j: # 服务熔断降级 circuitbreaker: configs: # 下面配置多组配置,可以把某一组配置分配给某一个目标服务 default: # 6 次访问中当执行方法的失败率达到 50% 时 CircuitBreaker 将进入开启 OPEN 状态。 # 等待 5 秒后,CircuitBreaker 将自动从开启 OPEN 状态过渡到半开 HALF_OPEN 状态。 slidingWindowType: COUNT_BASED # 滑动窗口的类型(计数) failureRateThreshold: 50 # 设置 50% 的调用失败时打开断路器,超过失败请求百分比 CircuitBreaker 变为 OPEN 状态 slidingWindowSize: 6 # 滑动窗口的大小配置 COUNT_BASED 表示最近 6 个请求,配置 TIME_BASED 表示 6 秒 minimumNumberOfCalls: 6 # 断路器计算失败率或慢调用率之前所需的最小样本(每个滑动窗口周期)。如果 minimumNumberOfCalls 为 10,则必须最少记录 10 个样本,然后才能计算失败率。如果只记录了 9 次调用,即使所有 9 次调用都失败,断路器也不会开启 automaticTransitionFromOpenToHalfOpenEnabled: true # 是否启用自动从开启状态过渡到半开状态,默认值为 true。如果启用,CircuitBreaker 将自动从开启状态过渡到半开状态,并允许一些请求通过以测试服务是否恢复正常 waitDurationInOpenState: 5s # 从 OPEN 到 HALF_OPEN 状态需要等待的时间 permittedNumberOfCallsInHalfOpenState: 2 # 半开状态允许的最大请求数,默认值为 10。在半开状态下,CircuitBreaker 将允许最多 permittedNumberOfCallsInHalfOpenState 个请求通过,如果其中有任何一个请求失败,CircuitBreaker 将重新进入开启状态 recordExceptions: # 指定哪些异常可认定为调用失败 - java.lang.Exception timeBased: slidingWindowType: TIME_BASED # 滑动窗口的类型(计时) failureRateThreshold: 50 slowCallDurationThreshold: 2s # 慢调用时间阈值,断路器把调用时间大于 slowCallDurationThreshold 视为慢调用并增加慢调用比例 slowCallRateThreshold: 30 # 慢调用百分比峰值,当慢调用比例高于阈值,断路器打开,并开启服务降级 slidingWindowSize: 2 # 滑动窗口的大小配置,窗口类型为 TIME_BASED 时表示最近 2 秒 minimumNumberOfCalls: 2 permittedNumberOfCallsInHalfOpenState: 2 waitDurationInOpenState: 5s recordExceptions: - java.lang.Exception instances: payment-service: # 这里配一个服务名称,表示目标服务 baseConfig: timeBased # 可以给每个服务单独指定配置 # 这里修改一下降级超时,才能更好地测试 TIME_BASED 熔断 timelimiter: configs: default: timeout-duration: 10s # timelimiter 默认限制远程 1s,超于 1s 就超时异常,如果配置了降级,就会走降级逻辑 ``` 2. 调用 ```java @RestController @RequestMapping("/consumer") public class OrderCircuitController { @Resource private PaymentApi paymentApi; // 在配置文件中,resilience4j.circuitbreaker.instances.payment-service 对实例 payment-service 进行了配置 // @CircuitBreaker 中的 `name = "payment-service"` 用于指定实例名称 // 熔断降级 @GetMapping(value = "/pay/circuit/{id}") @CircuitBreaker(name = "payment-service", fallbackMethod = "myCircuitFallback") String myCircuitBreaker(@PathVariable("id") Integer id) { return paymentApi.myCircuit(id); } // 服务降级后的兜底处理方法 String myCircuitFallback(Integer id, Throwable t) { // 这里是容错处理逻辑,返回备用结果 return "系统繁忙,请稍后再试-----/(ㄒoㄒ)/~~"; } } ``` ### 隔离 ## ZipKin 访问地址:http://localhost:9411/zipkin/