gateway接口日志,Resilience4j熔断测试类
This commit is contained in:
parent
47e8f1ea5f
commit
1078f78060
|
|
@ -24,4 +24,7 @@ public class CommonConstant {
|
||||||
// 角色常量
|
// 角色常量
|
||||||
public static final String ROLE_ADMIN = "admin";
|
public static final String ROLE_ADMIN = "admin";
|
||||||
public static final String ROLE_USER = "user";
|
public static final String ROLE_USER = "user";
|
||||||
|
|
||||||
|
// Redis key
|
||||||
|
public static final String REDIS_KEY_USER_TOKEN = "user:token:";
|
||||||
}
|
}
|
||||||
|
|
@ -11,7 +11,7 @@ import org.springframework.context.annotation.ComponentScan;
|
||||||
@MapperScan("com.tacit.admin.mapper")
|
@MapperScan("com.tacit.admin.mapper")
|
||||||
@EnableDiscoveryClient
|
@EnableDiscoveryClient
|
||||||
@EnableFeignClients(basePackages = "com.tacit.common.feign")
|
@EnableFeignClients(basePackages = "com.tacit.common.feign")
|
||||||
@ComponentScan(basePackages = {"com.tacit.admin", "com.tacit.common"})
|
@ComponentScan(basePackages = {"com.tacit.admin", "com.tacit.common.redis"})
|
||||||
public class AdminApplication {
|
public class AdminApplication {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(AdminApplication.class, args);
|
SpringApplication.run(AdminApplication.class, args);
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ public class SecurityConfig {
|
||||||
.cors(AbstractHttpConfigurer::disable)
|
.cors(AbstractHttpConfigurer::disable)
|
||||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||||
.authorizeHttpRequests(auth -> auth
|
.authorizeHttpRequests(auth -> auth
|
||||||
.requestMatchers("/auth/**", "/test/hello", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll()
|
.requestMatchers("/auth/**","test/**", "/test/hello", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll()
|
||||||
.requestMatchers("/test/feign/**").authenticated()
|
.requestMatchers("/test/feign/**").authenticated()
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
package com.tacit.admin.controller;
|
||||||
|
|
||||||
|
import com.tacit.admin.service.Resilience4jDemoService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resilience4j演示控制器
|
||||||
|
* 用于测试断路器和重试功能
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/test/resilience4j")
|
||||||
|
@Slf4j
|
||||||
|
public class Resilience4jDemoController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private Resilience4jDemoService resilience4jDemoService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试断路器功能
|
||||||
|
* @param failCount 失败次数,用于模拟服务失败
|
||||||
|
* @return 响应结果
|
||||||
|
*/
|
||||||
|
@GetMapping("/circuit-breaker")
|
||||||
|
public ResponseEntity<String> testCircuitBreaker(@RequestParam(defaultValue = "0") int failCount) {
|
||||||
|
log.info("测试断路器,failCount: {}", failCount);
|
||||||
|
String result = resilience4jDemoService.testCircuitBreaker(failCount);
|
||||||
|
return ResponseEntity.ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试重试功能
|
||||||
|
* @param failCount 失败次数,用于模拟服务失败
|
||||||
|
* @return 响应结果
|
||||||
|
*/
|
||||||
|
@GetMapping("/retry")
|
||||||
|
public ResponseEntity<String> testRetry(@RequestParam(defaultValue = "0") int failCount) {
|
||||||
|
log.info("测试重试,failCount: {}", failCount);
|
||||||
|
String result = resilience4jDemoService.testRetry(failCount);
|
||||||
|
return ResponseEntity.ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试断路器和重试组合功能
|
||||||
|
* @param failCount 失败次数,用于模拟服务失败
|
||||||
|
* @return 响应结果
|
||||||
|
*/
|
||||||
|
@GetMapping("/combined")
|
||||||
|
public ResponseEntity<String> testCombined(@RequestParam(defaultValue = "0") int failCount) {
|
||||||
|
log.info("测试断路器和重试组合,failCount: {}", failCount);
|
||||||
|
String result = resilience4jDemoService.testCombined(failCount);
|
||||||
|
return ResponseEntity.ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置断路器状态
|
||||||
|
* @return 响应结果
|
||||||
|
*/
|
||||||
|
@GetMapping("/reset")
|
||||||
|
public ResponseEntity<String> resetCircuitBreaker() {
|
||||||
|
log.info("重置断路器状态");
|
||||||
|
resilience4jDemoService.resetCircuitBreaker();
|
||||||
|
return ResponseEntity.ok("断路器状态已重置");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
package com.tacit.admin.service;
|
||||||
|
|
||||||
|
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
|
||||||
|
import io.github.resilience4j.retry.annotation.Retry;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resilience4j演示服务
|
||||||
|
* 用于测试断路器和重试功能
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class Resilience4jDemoService {
|
||||||
|
|
||||||
|
// 用于记录调用次数的计数器
|
||||||
|
private int circuitBreakerCallCount = 0;
|
||||||
|
private int retryCallCount = 0;
|
||||||
|
private int combinedCallCount = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试断路器功能
|
||||||
|
* @param failCount 失败次数,用于模拟服务失败
|
||||||
|
* @return 响应结果
|
||||||
|
*/
|
||||||
|
@CircuitBreaker(name = "demoCircuitBreaker", fallbackMethod = "circuitBreakerFallback")
|
||||||
|
public String testCircuitBreaker(int failCount) {
|
||||||
|
circuitBreakerCallCount++;
|
||||||
|
log.info("断路器测试调用,第{}次,failCount: {}", circuitBreakerCallCount, failCount);
|
||||||
|
|
||||||
|
// 模拟服务失败
|
||||||
|
if (circuitBreakerCallCount <= failCount) {
|
||||||
|
log.error("断路器测试调用失败,第{}次", circuitBreakerCallCount);
|
||||||
|
throw new RuntimeException("服务调用失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
return "断路器测试调用成功,第" + circuitBreakerCallCount + "次调用";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 断路器的回退方法
|
||||||
|
* @param failCount 失败次数
|
||||||
|
* @param ex 异常信息
|
||||||
|
* @return 回退结果
|
||||||
|
*/
|
||||||
|
public String circuitBreakerFallback(int failCount, Exception ex) {
|
||||||
|
log.warn("断路器回退被调用,failCount: {}, 异常: {}", failCount, ex.getMessage());
|
||||||
|
return "断路器回退结果: " + ex.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试重试功能
|
||||||
|
* @param failCount 失败次数,用于模拟服务失败
|
||||||
|
* @return 响应结果
|
||||||
|
*/
|
||||||
|
@Retry(name = "demoRetry", fallbackMethod = "retryFallback")
|
||||||
|
public String testRetry(int failCount) {
|
||||||
|
retryCallCount++;
|
||||||
|
log.info("重试测试调用,第{}次,failCount: {}", retryCallCount, failCount);
|
||||||
|
|
||||||
|
// 模拟服务失败
|
||||||
|
if (retryCallCount <= failCount) {
|
||||||
|
log.error("重试测试调用失败,第{}次", retryCallCount);
|
||||||
|
throw new RuntimeException("服务调用失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
return "重试测试调用成功,第" + retryCallCount + "次调用";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重试的回退方法
|
||||||
|
* @param failCount 失败次数
|
||||||
|
* @param ex 异常信息
|
||||||
|
* @return 回退结果
|
||||||
|
*/
|
||||||
|
public String retryFallback(int failCount, Exception ex) {
|
||||||
|
log.warn("重试回退被调用,failCount: {}, 异常: {}", failCount, ex.getMessage());
|
||||||
|
return "重试回退结果: " + ex.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试断路器和重试组合功能
|
||||||
|
* @param failCount 失败次数,用于模拟服务失败
|
||||||
|
* @return 响应结果
|
||||||
|
*/
|
||||||
|
@CircuitBreaker(name = "demoCircuitBreaker", fallbackMethod = "combinedFallback")
|
||||||
|
@Retry(name = "demoRetry")
|
||||||
|
public String testCombined(int failCount) {
|
||||||
|
combinedCallCount++;
|
||||||
|
log.info("组合测试调用,第{}次,failCount: {}", combinedCallCount, failCount);
|
||||||
|
|
||||||
|
// 模拟服务失败
|
||||||
|
if (combinedCallCount <= failCount) {
|
||||||
|
log.error("组合测试调用失败,第{}次", combinedCallCount);
|
||||||
|
throw new RuntimeException("服务调用失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
return "组合测试调用成功,第" + combinedCallCount + "次调用";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组合功能的回退方法
|
||||||
|
* @param failCount 失败次数
|
||||||
|
* @param ex 异常信息
|
||||||
|
* @return 回退结果
|
||||||
|
*/
|
||||||
|
public String combinedFallback(int failCount, Exception ex) {
|
||||||
|
log.warn("组合回退被调用,failCount: {}, 异常: {}", failCount, ex.getMessage());
|
||||||
|
return "组合回退结果: " + ex.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置断路器状态
|
||||||
|
*/
|
||||||
|
public void resetCircuitBreaker() {
|
||||||
|
circuitBreakerCallCount = 0;
|
||||||
|
retryCallCount = 0;
|
||||||
|
combinedCallCount = 0;
|
||||||
|
log.info("所有计数器已重置");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,7 +9,7 @@ import org.springframework.context.annotation.ComponentScan;
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@MapperScan("com.tacit.app.mapper")
|
@MapperScan("com.tacit.app.mapper")
|
||||||
@EnableFeignClients(basePackages = "com.tacit.common.feign")
|
@EnableFeignClients(basePackages = "com.tacit.common.feign")
|
||||||
@ComponentScan(basePackages = {"com.tacit.app", "com.tacit.common"})
|
@ComponentScan(basePackages = {"com.tacit.app", "com.tacit.common.redis"})
|
||||||
public class AppApiApplication {
|
public class AppApiApplication {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(AppApiApplication.class, args);
|
SpringApplication.run(AppApiApplication.class, args);
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,12 @@
|
||||||
<version>${project.parent.version}</version>
|
<version>${project.parent.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tacit</groupId>
|
||||||
|
<artifactId>common-redis</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.tacit</groupId>
|
<groupId>com.tacit</groupId>
|
||||||
<artifactId>common-core</artifactId>
|
<artifactId>common-core</artifactId>
|
||||||
|
|
@ -62,6 +68,12 @@
|
||||||
<groupId>com.alibaba.cloud</groupId>
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tacit</groupId>
|
||||||
|
<artifactId>common-redis</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,11 @@ package com.tacit.gateway;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableDiscoveryClient
|
@EnableDiscoveryClient
|
||||||
|
@ComponentScan(basePackages = {"com.tacit.gateway", "com.tacit.common.redis"})
|
||||||
public class GatewayApplication {
|
public class GatewayApplication {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(GatewayApplication.class, args);
|
SpringApplication.run(GatewayApplication.class, args);
|
||||||
|
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
package com.tacit.gateway.config;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
|
||||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
|
||||||
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
|
||||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
public class RedisConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
|
|
||||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
|
||||||
template.setConnectionFactory(factory);
|
|
||||||
|
|
||||||
// key 使用 String 序列化
|
|
||||||
template.setKeySerializer(new StringRedisSerializer());
|
|
||||||
template.setHashKeySerializer(new StringRedisSerializer());
|
|
||||||
|
|
||||||
// value 使用 JSON 序列化
|
|
||||||
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
|
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
|
||||||
|
|
||||||
// 设置字段可见性
|
|
||||||
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
|
||||||
|
|
||||||
// 新版 Jackson 安全的多态类型配置
|
|
||||||
mapper.activateDefaultTyping(
|
|
||||||
LaissezFaireSubTypeValidator.instance,
|
|
||||||
ObjectMapper.DefaultTyping.NON_FINAL
|
|
||||||
);
|
|
||||||
|
|
||||||
template.setValueSerializer(serializer);
|
|
||||||
template.setHashValueSerializer(serializer);
|
|
||||||
|
|
||||||
template.afterPropertiesSet();
|
|
||||||
return template;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
package com.tacit.gateway.filter;
|
package com.tacit.gateway.filter;
|
||||||
|
|
||||||
import com.tacit.common.constant.CommonConstant;
|
import com.tacit.common.constant.CommonConstant;
|
||||||
|
import com.tacit.common.redis.utils.RedisUtils;
|
||||||
import com.tacit.common.utils.JwtUtils;
|
import com.tacit.common.utils.JwtUtils;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||||
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
|
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||||
|
|
@ -25,7 +25,7 @@ import java.util.List;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class JwtAuthenticationFilter extends AbstractGatewayFilterFactory<JwtAuthenticationFilter.Config> {
|
public class JwtAuthenticationFilter extends AbstractGatewayFilterFactory<JwtAuthenticationFilter.Config> {
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisTemplate<String, Object> redisTemplate;
|
private RedisUtils redisUtils;
|
||||||
// 不需要认证的路径
|
// 不需要认证的路径
|
||||||
private static final List<String> WHITE_LIST = List.of(
|
private static final List<String> WHITE_LIST = List.of(
|
||||||
"/auth/login",
|
"/auth/login",
|
||||||
|
|
@ -72,7 +72,7 @@ public class JwtAuthenticationFilter extends AbstractGatewayFilterFactory<JwtAut
|
||||||
|
|
||||||
// 验证JWT令牌
|
// 验证JWT令牌
|
||||||
try {
|
try {
|
||||||
Boolean isTokenValid = redisTemplate.hasKey(token);
|
boolean isTokenValid = redisUtils.hasKey(token);
|
||||||
if (!isTokenValid) {
|
if (!isTokenValid) {
|
||||||
return unauthorizedResponse(exchange, "Token已被注销");
|
return unauthorizedResponse(exchange, "Token已被注销");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
package com.tacit.gateway.filter;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||||
|
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||||
|
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求日志记录过滤器
|
||||||
|
* 用于记录请求的详细信息,包括请求路径、方法、参数、响应状态码、响应时间等
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class RequestLoggingFilter extends AbstractGatewayFilterFactory<RequestLoggingFilter.Config> {
|
||||||
|
|
||||||
|
public RequestLoggingFilter() {
|
||||||
|
super(Config.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GatewayFilter apply(Config config) {
|
||||||
|
return new GatewayFilter() {
|
||||||
|
@Override
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||||
|
// 记录请求开始时间
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
String requestId = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
|
||||||
|
|
||||||
|
// 获取请求信息
|
||||||
|
String method = exchange.getRequest().getMethod().name();
|
||||||
|
String path = exchange.getRequest().getURI().getPath();
|
||||||
|
String query = exchange.getRequest().getURI().getQuery();
|
||||||
|
|
||||||
|
// 安全获取客户端IP,添加空检查
|
||||||
|
String clientIp = "unknown";
|
||||||
|
if (exchange.getRequest().getRemoteAddress() != null &&
|
||||||
|
exchange.getRequest().getRemoteAddress().getAddress() != null) {
|
||||||
|
clientIp = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加请求ID到请求头
|
||||||
|
exchange.getRequest().mutate().header("X-Request-Id", requestId).build();
|
||||||
|
|
||||||
|
// 记录请求开始日志
|
||||||
|
log.info("[{}] Request received: {} {}?{}", requestId, method, path, query);
|
||||||
|
log.info("[{}] Client IP: {}", requestId, clientIp);
|
||||||
|
log.info("[{}] Headers: {}", requestId, exchange.getRequest().getHeaders());
|
||||||
|
|
||||||
|
// 处理响应
|
||||||
|
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
|
||||||
|
// 记录响应时间
|
||||||
|
long endTime = System.currentTimeMillis();
|
||||||
|
long responseTime = endTime - startTime;
|
||||||
|
|
||||||
|
// 安全获取响应状态码,添加空检查
|
||||||
|
int statusCode = 0;
|
||||||
|
if (exchange.getResponse().getStatusCode() != null) {
|
||||||
|
statusCode = exchange.getResponse().getStatusCode().value();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录响应日志
|
||||||
|
log.info("[{}] Response sent: {} {}", requestId, statusCode, path);
|
||||||
|
log.info("[{}] Response time: {}ms", requestId, responseTime);
|
||||||
|
log.info("[{}] Response headers: {}", requestId, exchange.getResponse().getHeaders());
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Config {
|
||||||
|
// 可以添加配置属性
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,8 @@ spring:
|
||||||
locator:
|
locator:
|
||||||
enabled: true
|
enabled: true
|
||||||
lower-case-service-id: true
|
lower-case-service-id: true
|
||||||
|
default-filters:
|
||||||
|
- name: RequestLoggingFilter
|
||||||
routes:
|
routes:
|
||||||
# Admin Service Route
|
# Admin Service Route
|
||||||
- id: tacit-admin
|
- id: tacit-admin
|
||||||
|
|
@ -17,7 +19,19 @@ spring:
|
||||||
filters:
|
filters:
|
||||||
- StripPrefix=1
|
- StripPrefix=1
|
||||||
|
|
||||||
# App API Service Route
|
# 接口级路由重写
|
||||||
|
- id: tacit-app-api-login
|
||||||
|
uri: lb://tacit-app-api
|
||||||
|
predicates:
|
||||||
|
- Path=/api/user/login/v1
|
||||||
|
- Method=POST
|
||||||
|
filters:
|
||||||
|
- StripPrefix=1
|
||||||
|
- RewritePath=/user/login/v1, /user/login/v2
|
||||||
|
- AddResponseHeader=X-Special-Route, login
|
||||||
|
- AddResponseHeader=X-Rewritten-Path, /user/login/v2
|
||||||
|
|
||||||
|
# App API Service Route - Other APIs (With Auth)
|
||||||
- id: tacit-app-api
|
- id: tacit-app-api
|
||||||
uri: lb://tacit-app-api
|
uri: lb://tacit-app-api
|
||||||
predicates:
|
predicates:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue