diff --git a/pom.xml b/pom.xml index 69fcbcd..093d502 100644 --- a/pom.xml +++ b/pom.xml @@ -12,6 +12,14 @@ Tacit Parent Tacit App Microservice Architecture Parent Project + + + tacit-gateway + tacit-admin + tacit-app-api + tacit-common + + 21 3.2.0 @@ -20,6 +28,7 @@ 3.5.5 1.18.30 0.11.5 + 2.1.0 @@ -83,15 +92,47 @@ ${jjwt.version} runtime + + + + + io.github.resilience4j + resilience4j-spring-boot3 + ${resilience4j.version} + + + + io.github.resilience4j + resilience4j-circuitbreaker + ${resilience4j.version} + + + + io.github.resilience4j + resilience4j-ratelimiter + ${resilience4j.version} + + + + io.github.resilience4j + resilience4j-retry + ${resilience4j.version} + + + + io.github.resilience4j + resilience4j-bulkhead + ${resilience4j.version} + + + + io.github.resilience4j + resilience4j-timelimiter + ${resilience4j.version} + - - tacit-gateway - tacit-admin - tacit-app-api - tacit-common - diff --git a/resilience4j-usage-template.md b/resilience4j-usage-template.md new file mode 100644 index 0000000..9d6cc0e --- /dev/null +++ b/resilience4j-usage-template.md @@ -0,0 +1,382 @@ +# Resilience4j 使用模板与文档 + +## 1. 概述 +Resilience4j 是一个轻量级的容错库,专为 Java 8+ 和函数式编程设计。它提供了断路器、限流、重试、舱壁隔离和超时控制等功能,帮助构建弹性和可靠的系统。 + +## 2. 配置文件示例 + +### 2.1 application.yml 配置 +```yaml +resilience4j: + # 断路器配置 + circuitbreaker: + instances: + userServiceCircuitBreaker: + # 失败阈值百分比 + failureRateThreshold: 50 + # 滑动窗口类型(count-based 或 time-based) + slidingWindowType: COUNT_BASED + # 滑动窗口大小 + slidingWindowSize: 10 + # 最小请求数 + minimumNumberOfCalls: 5 + # 半开状态下允许的最大请求数 + permittedNumberOfCallsInHalfOpenState: 3 + # 等待时间(毫秒) + waitDurationInOpenState: 10000 + # 自动从半开状态恢复到关闭状态 + automaticTransitionFromOpenToHalfOpenEnabled: true + # 忽略的异常类型 + ignoreExceptions: + - org.springframework.web.client.HttpClientErrorException$BadRequest + + # 限流配置 + ratelimiter: + instances: + userServiceRateLimiter: + # 每秒允许的请求数 + limitForPeriod: 100 + # 时间窗口(毫秒) + limitRefreshPeriod: 1000 + # 等待获取许可的超时时间(毫秒) + timeoutDuration: 500 + + # 重试配置 + retry: + instances: + userServiceRetry: + # 最大重试次数 + maxAttempts: 3 + # 初始重试间隔(毫秒) + waitDuration: 1000 + # 重试退避策略 + retryExponentialBackoff: + initialInterval: 1000 + multiplier: 2 + maxInterval: 10000 + # 重试的异常类型 + retryExceptions: + - org.springframework.web.client.HttpServerErrorException + # 忽略的异常类型 + ignoreExceptions: + - org.springframework.web.client.HttpClientErrorException$BadRequest + + # 舱壁配置 + bulkhead: + instances: + userServiceBulkhead: + # 最大并行执行的任务数 + maxConcurrentCalls: 20 + # 等待队列大小 + maxWaitDuration: 1000 + + # 超时控制配置 + timelimiter: + instances: + userServiceTimeLimiter: + # 超时时间(毫秒) + timeoutDuration: 3000 +``` + +## 3. 使用示例 + +### 3.1 Service 层使用示例 + +```java +package com.tacit.admin.service.impl; + +import io.github.resilience4j.bulkhead.annotation.Bulkhead; +import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker; +import io.github.resilience4j.ratelimiter.annotation.RateLimiter; +import io.github.resilience4j.retry.annotation.Retry; +import io.github.resilience4j.timelimiter.annotation.TimeLimiter; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.util.concurrent.CompletableFuture; + +@Service +public class UserService { + + private final RestTemplate restTemplate; + + public UserService(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + /** + * 使用断路器示例 + * @param userId 用户ID + * @return 用户信息 + */ + @CircuitBreaker( + name = "userServiceCircuitBreaker", + fallbackMethod = "getUserFallback" + ) + public String getUserById(Long userId) { + return restTemplate.getForObject("http://user-service/users/{id}", String.class, userId); + } + + /** + * 使用限流示例 + * @param userId 用户ID + * @return 用户信息 + */ + @RateLimiter( + name = "userServiceRateLimiter", + fallbackMethod = "getUserFallback" + ) + public String getUserWithRateLimit(Long userId) { + return restTemplate.getForObject("http://user-service/users/{id}", String.class, userId); + } + + /** + * 使用重试示例 + * @param userId 用户ID + * @return 用户信息 + */ + @Retry( + name = "userServiceRetry", + fallbackMethod = "getUserFallback" + ) + public String getUserWithRetry(Long userId) { + return restTemplate.getForObject("http://user-service/users/{id}", String.class, userId); + } + + /** + * 使用舱壁示例 + * @param userId 用户ID + * @return 用户信息 + */ + @Bulkhead( + name = "userServiceBulkhead", + fallbackMethod = "getUserFallback" + ) + public String getUserWithBulkhead(Long userId) { + return restTemplate.getForObject("http://user-service/users/{id}", String.class, userId); + } + + /** + * 使用超时控制示例 + * @param userId 用户ID + * @return 用户信息 + */ + @TimeLimiter( + name = "userServiceTimeLimiter", + fallbackMethod = "getUserAsyncFallback" + ) + public CompletableFuture getUserWithTimeout(Long userId) { + return CompletableFuture.supplyAsync(() -> + restTemplate.getForObject("http://user-service/users/{id}", String.class, userId) + ); + } + + /** + * 组合使用多个功能示例 + * @param userId 用户ID + * @return 用户信息 + */ + @CircuitBreaker( + name = "userServiceCircuitBreaker", + fallbackMethod = "getUserFallback" + ) + @Retry( + name = "userServiceRetry" + ) + @RateLimiter( + name = "userServiceRateLimiter" + ) + public String getUserWithMultipleFeatures(Long userId) { + return restTemplate.getForObject("http://user-service/users/{id}", String.class, userId); + } + + /** + * 同步回退方法 + * @param userId 用户ID + * @param ex 异常 + * @return 回退结果 + */ + private String getUserFallback(Long userId, Exception ex) { + return "{\"id\":0,\"name\":\"Fallback User\",\"message\":\"服务暂时不可用,请稍后重试\"}"; + } + + /** + * 异步回退方法 + * @param userId 用户ID + * @param ex 异常 + * @return 回退结果 + */ + private CompletableFuture getUserAsyncFallback(Long userId, Exception ex) { + return CompletableFuture.completedFuture( + "{\"id\":0,\"name\":\"Async Fallback User\",\"message\":\"服务暂时不可用,请稍后重试\"}" + ); + } +} +``` + +### 3.2 Controller 层使用示例 + +```java +package com.tacit.admin.controller; + +import com.tacit.admin.service.UserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.concurrent.CompletableFuture; + +@RestController +@RequestMapping("/user") +@Tag(name = "用户管理", description = "用户管理相关接口") +public class UserController { + + @Autowired + private UserService userService; + + @Operation(summary = "获取用户信息", description = "使用断路器模式获取用户信息") + @GetMapping("/get/{id}") + public String getUser(@PathVariable Long id) { + return userService.getUserById(id); + } + + @Operation(summary = "限流获取用户信息", description = "使用限流模式获取用户信息") + @GetMapping("/rate-limit/{id}") + public String getUserWithRateLimit(@PathVariable Long id) { + return userService.getUserWithRateLimit(id); + } + + @Operation(summary = "重试获取用户信息", description = "使用重试模式获取用户信息") + @GetMapping("/retry/{id}") + public String getUserWithRetry(@PathVariable Long id) { + return userService.getUserWithRetry(id); + } + + @Operation(summary = "超时控制获取用户信息", description = "使用超时控制获取用户信息") + @GetMapping("/timeout/{id}") + public CompletableFuture getUserWithTimeout(@PathVariable Long id) { + return userService.getUserWithTimeout(id); + } + + @Operation(summary = "组合功能获取用户信息", description = "组合使用多个容错功能获取用户信息") + @GetMapping("/multiple/{id}") + public String getUserWithMultipleFeatures(@PathVariable Long id) { + return userService.getUserWithMultipleFeatures(id); + } +} +``` + +## 4. 使用说明 + +### 4.1 注解说明 + +| 注解 | 功能 | 主要属性 | +|------|------|----------| +| `@CircuitBreaker` | 断路器 | name, fallbackMethod | +| `@RateLimiter` | 限流 | name, fallbackMethod | +| `@Retry` | 重试 | name, fallbackMethod | +| `@Bulkhead` | 舱壁隔离 | name, fallbackMethod, type | +| `@TimeLimiter` | 超时控制 | name, fallbackMethod | + +### 4.2 回退方法注意事项 + +1. 回退方法必须与原始方法在同一个类中 +2. 回退方法的参数列表必须与原始方法相同,并且最后一个参数是 Throwable 类型 +3. 回退方法的返回类型必须与原始方法相同 +4. 异步方法的回退方法必须返回 CompletableFuture + +### 4.3 监控与指标 + +Resilience4j 提供了与 Micrometer 和 Prometheus 的集成,可以监控各种指标: + +```yaml +# 启用 Resilience4j 指标management: + endpoints: + web: + exposure: + include: health,info,metrics,prometheus + endpoint: + health: + show-details: always + metrics: + export: + prometheus: + enabled: true +``` + +## 5. 最佳实践 + +1. **合理配置参数**:根据实际业务场景调整各功能的参数 +2. **使用回退策略**:为每个容错功能提供适当的回退方法 +3. **监控与告警**:定期监控各功能的指标,设置合理的告警阈值 +4. **组合使用**:根据需求组合使用多个容错功能 +5. **异常处理**:区分可重试和不可重试的异常类型 +6. **性能测试**:在生产环境部署前进行充分的性能测试 + +## 6. 常见问题与解决方案 + +### 6.1 断路器一直处于打开状态 +- 检查失败率阈值是否设置过低 +- 检查是否有足够的请求数触发断路器 +- 检查忽略的异常类型是否正确 + +### 6.2 限流不生效 +- 检查限流配置是否正确 +- 检查限流实例名称是否与注解中的名称一致 +- 检查是否有其他限流机制影响 + +### 6.3 重试不生效 +- 检查重试的异常类型是否正确 +- 检查重试次数和间隔是否合理 +- 检查是否有其他重试机制影响 + +## 7. 依赖管理 + +如果使用 Spring Boot,可以只引入以下依赖: + +```xml + + io.github.resilience4j + resilience4j-spring-boot3 + ${resilience4j.version} + +``` + +如果需要更精细的控制,可以单独引入所需的模块: + +```xml + + + io.github.resilience4j + resilience4j-core + ${resilience4j.version} + + + + + io.github.resilience4j + resilience4j-circuitbreaker + ${resilience4j.version} + + + + io.github.resilience4j + resilience4j-ratelimiter + ${resilience4j.version} + + + +``` + +## 8. 版本兼容 + +| Resilience4j 版本 | Spring Boot 版本 | Java 版本 | +|-------------------|------------------|-----------| +| 2.x | 3.x | 17+ | +| 1.x | 2.x | 8+ | + +请根据您的 Spring Boot 版本选择合适的 Resilience4j 版本。 \ No newline at end of file diff --git a/tacit-admin/pom.xml b/tacit-admin/pom.xml index 86dee2b..3254316 100644 --- a/tacit-admin/pom.xml +++ b/tacit-admin/pom.xml @@ -78,6 +78,12 @@ provided + + + io.github.resilience4j + resilience4j-spring-boot3 + + org.springframework.boot diff --git a/tacit-admin/src/main/java/com/tacit/admin/AdminApplication.java b/tacit-admin/src/main/java/com/tacit/admin/AdminApplication.java index cf6e603..0c9d06a 100644 --- a/tacit-admin/src/main/java/com/tacit/admin/AdminApplication.java +++ b/tacit-admin/src/main/java/com/tacit/admin/AdminApplication.java @@ -13,5 +13,6 @@ import org.springframework.cloud.openfeign.EnableFeignClients; public class AdminApplication { public static void main(String[] args) { SpringApplication.run(AdminApplication.class, args); + System.out.println("====== admin start ======"); } } diff --git a/tacit-admin/src/main/java/com/tacit/admin/config/SecurityConfig.java b/tacit-admin/src/main/java/com/tacit/admin/config/SecurityConfig.java index 55841d0..1e37e59 100644 --- a/tacit-admin/src/main/java/com/tacit/admin/config/SecurityConfig.java +++ b/tacit-admin/src/main/java/com/tacit/admin/config/SecurityConfig.java @@ -44,7 +44,7 @@ public class SecurityConfig { // 设置请求授权规则 .authorizeHttpRequests(auth -> auth // 允许访问的路径 - .requestMatchers("/test/**", "/user/login", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll() + .requestMatchers("/test/**", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll() // 其他请求需要认证 .anyRequest().authenticated() ); diff --git a/tacit-admin/target/classes/com/tacit/admin/AdminApplication.class b/tacit-admin/target/classes/com/tacit/admin/AdminApplication.class index d73ed80..86c1e3f 100644 Binary files a/tacit-admin/target/classes/com/tacit/admin/AdminApplication.class and b/tacit-admin/target/classes/com/tacit/admin/AdminApplication.class differ diff --git a/tacit-admin/target/classes/com/tacit/admin/config/SecurityConfig.class b/tacit-admin/target/classes/com/tacit/admin/config/SecurityConfig.class index 63fd27d..3c6fe69 100644 Binary files a/tacit-admin/target/classes/com/tacit/admin/config/SecurityConfig.class and b/tacit-admin/target/classes/com/tacit/admin/config/SecurityConfig.class differ diff --git a/tacit-admin/target/classes/com/tacit/admin/config/SwaggerConfig.class b/tacit-admin/target/classes/com/tacit/admin/config/SwaggerConfig.class index 534bd78..e458369 100644 Binary files a/tacit-admin/target/classes/com/tacit/admin/config/SwaggerConfig.class and b/tacit-admin/target/classes/com/tacit/admin/config/SwaggerConfig.class differ diff --git a/tacit-admin/target/classes/com/tacit/admin/controller/TestController.class b/tacit-admin/target/classes/com/tacit/admin/controller/TestController.class index 61da2cd..433f784 100644 Binary files a/tacit-admin/target/classes/com/tacit/admin/controller/TestController.class and b/tacit-admin/target/classes/com/tacit/admin/controller/TestController.class differ diff --git a/tacit-admin/target/classes/com/tacit/admin/entity/User.class b/tacit-admin/target/classes/com/tacit/admin/entity/User.class index a3b98df..333fc96 100644 Binary files a/tacit-admin/target/classes/com/tacit/admin/entity/User.class and b/tacit-admin/target/classes/com/tacit/admin/entity/User.class differ diff --git a/tacit-admin/target/classes/com/tacit/admin/mapper/UserMapper.class b/tacit-admin/target/classes/com/tacit/admin/mapper/UserMapper.class index ae164c4..6eaab51 100644 Binary files a/tacit-admin/target/classes/com/tacit/admin/mapper/UserMapper.class and b/tacit-admin/target/classes/com/tacit/admin/mapper/UserMapper.class differ diff --git a/tacit-app-api/src/main/java/com/tacit/app/AppApiApplication.java b/tacit-app-api/src/main/java/com/tacit/app/AppApiApplication.java index 866915e..719e644 100644 --- a/tacit-app-api/src/main/java/com/tacit/app/AppApiApplication.java +++ b/tacit-app-api/src/main/java/com/tacit/app/AppApiApplication.java @@ -13,5 +13,6 @@ import org.springframework.cloud.openfeign.EnableFeignClients; public class AppApiApplication { public static void main(String[] args) { SpringApplication.run(AppApiApplication.class, args); + System.out.println("====== app api start ======"); } } diff --git a/tacit-app-api/target/classes/com/tacit/app/AppApiApplication.class b/tacit-app-api/target/classes/com/tacit/app/AppApiApplication.class index 0ad3508..ab36f87 100644 Binary files a/tacit-app-api/target/classes/com/tacit/app/AppApiApplication.class and b/tacit-app-api/target/classes/com/tacit/app/AppApiApplication.class differ diff --git a/tacit-app-api/target/classes/com/tacit/app/config/SwaggerConfig.class b/tacit-app-api/target/classes/com/tacit/app/config/SwaggerConfig.class index 3294705..7c1bbe5 100644 Binary files a/tacit-app-api/target/classes/com/tacit/app/config/SwaggerConfig.class and b/tacit-app-api/target/classes/com/tacit/app/config/SwaggerConfig.class differ diff --git a/tacit-app-api/target/classes/com/tacit/app/controller/AuthController.class b/tacit-app-api/target/classes/com/tacit/app/controller/AuthController.class index aefb23d..22781f7 100644 Binary files a/tacit-app-api/target/classes/com/tacit/app/controller/AuthController.class and b/tacit-app-api/target/classes/com/tacit/app/controller/AuthController.class differ diff --git a/tacit-app-api/target/classes/com/tacit/app/controller/UserController.class b/tacit-app-api/target/classes/com/tacit/app/controller/UserController.class index 37e41a4..53b456d 100644 Binary files a/tacit-app-api/target/classes/com/tacit/app/controller/UserController.class and b/tacit-app-api/target/classes/com/tacit/app/controller/UserController.class differ diff --git a/tacit-app-api/target/classes/com/tacit/app/entity/User.class b/tacit-app-api/target/classes/com/tacit/app/entity/User.class index a42f388..774f3e6 100644 Binary files a/tacit-app-api/target/classes/com/tacit/app/entity/User.class and b/tacit-app-api/target/classes/com/tacit/app/entity/User.class differ diff --git a/tacit-app-api/target/classes/com/tacit/app/entity/dto/LoginRequest.class b/tacit-app-api/target/classes/com/tacit/app/entity/dto/LoginRequest.class index 7ca9d4f..57dfc00 100644 Binary files a/tacit-app-api/target/classes/com/tacit/app/entity/dto/LoginRequest.class and b/tacit-app-api/target/classes/com/tacit/app/entity/dto/LoginRequest.class differ diff --git a/tacit-app-api/target/classes/com/tacit/app/entity/dto/LoginResponse.class b/tacit-app-api/target/classes/com/tacit/app/entity/dto/LoginResponse.class index a129344..db60307 100644 Binary files a/tacit-app-api/target/classes/com/tacit/app/entity/dto/LoginResponse.class and b/tacit-app-api/target/classes/com/tacit/app/entity/dto/LoginResponse.class differ diff --git a/tacit-app-api/target/classes/com/tacit/app/mapper/UserMapper.class b/tacit-app-api/target/classes/com/tacit/app/mapper/UserMapper.class index bc7b27c..ae769a8 100644 Binary files a/tacit-app-api/target/classes/com/tacit/app/mapper/UserMapper.class and b/tacit-app-api/target/classes/com/tacit/app/mapper/UserMapper.class differ diff --git a/tacit-app-api/target/classes/com/tacit/app/service/UserService.class b/tacit-app-api/target/classes/com/tacit/app/service/UserService.class index ead780c..f1f3256 100644 Binary files a/tacit-app-api/target/classes/com/tacit/app/service/UserService.class and b/tacit-app-api/target/classes/com/tacit/app/service/UserService.class differ diff --git a/tacit-app-api/target/classes/com/tacit/app/service/impl/UserServiceImpl.class b/tacit-app-api/target/classes/com/tacit/app/service/impl/UserServiceImpl.class index acd38c0..6d1f612 100644 Binary files a/tacit-app-api/target/classes/com/tacit/app/service/impl/UserServiceImpl.class and b/tacit-app-api/target/classes/com/tacit/app/service/impl/UserServiceImpl.class differ diff --git a/tacit-common/target/classes/com/tacit/common/constant/CommonConstant.class b/tacit-common/target/classes/com/tacit/common/constant/CommonConstant.class index f5abb21..4c07f24 100644 Binary files a/tacit-common/target/classes/com/tacit/common/constant/CommonConstant.class and b/tacit-common/target/classes/com/tacit/common/constant/CommonConstant.class differ diff --git a/tacit-common/target/classes/com/tacit/common/entity/ResponseResult.class b/tacit-common/target/classes/com/tacit/common/entity/ResponseResult.class index 769a915..0eed428 100644 Binary files a/tacit-common/target/classes/com/tacit/common/entity/ResponseResult.class and b/tacit-common/target/classes/com/tacit/common/entity/ResponseResult.class differ diff --git a/tacit-common/target/classes/com/tacit/common/exception/BusinessException.class b/tacit-common/target/classes/com/tacit/common/exception/BusinessException.class index d19952e..7d53470 100644 Binary files a/tacit-common/target/classes/com/tacit/common/exception/BusinessException.class and b/tacit-common/target/classes/com/tacit/common/exception/BusinessException.class differ diff --git a/tacit-common/target/classes/com/tacit/common/handler/GlobalExceptionHandler.class b/tacit-common/target/classes/com/tacit/common/handler/GlobalExceptionHandler.class index 63cd5ba..34ad023 100644 Binary files a/tacit-common/target/classes/com/tacit/common/handler/GlobalExceptionHandler.class and b/tacit-common/target/classes/com/tacit/common/handler/GlobalExceptionHandler.class differ diff --git a/tacit-common/target/classes/com/tacit/common/utils/JwtUtils.class b/tacit-common/target/classes/com/tacit/common/utils/JwtUtils.class index 86be20c..cd14b09 100644 Binary files a/tacit-common/target/classes/com/tacit/common/utils/JwtUtils.class and b/tacit-common/target/classes/com/tacit/common/utils/JwtUtils.class differ diff --git a/tacit-gateway/src/main/java/com/tacit/gateway/GatewayApplication.java b/tacit-gateway/src/main/java/com/tacit/gateway/GatewayApplication.java index 76f7e76..96984be 100644 --- a/tacit-gateway/src/main/java/com/tacit/gateway/GatewayApplication.java +++ b/tacit-gateway/src/main/java/com/tacit/gateway/GatewayApplication.java @@ -9,5 +9,6 @@ import org.springframework.cloud.client.discovery.EnableDiscoveryClient; public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); + System.out.println("====== gateway start ======"); } } diff --git a/tacit-gateway/target/classes/com/tacit/gateway/GatewayApplication.class b/tacit-gateway/target/classes/com/tacit/gateway/GatewayApplication.class index 860313e..dc07a76 100644 Binary files a/tacit-gateway/target/classes/com/tacit/gateway/GatewayApplication.class and b/tacit-gateway/target/classes/com/tacit/gateway/GatewayApplication.class differ diff --git a/tacit-gateway/target/classes/com/tacit/gateway/filter/JwtAuthenticationFilter.class b/tacit-gateway/target/classes/com/tacit/gateway/filter/JwtAuthenticationFilter.class index 5171847..e3d89a6 100644 Binary files a/tacit-gateway/target/classes/com/tacit/gateway/filter/JwtAuthenticationFilter.class and b/tacit-gateway/target/classes/com/tacit/gateway/filter/JwtAuthenticationFilter.class differ