Compare commits
11 Commits
ca60cd46a5
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 6bfc845693 | |||
| 6b50bfaeb4 | |||
| 50f3579f6a | |||
| 47cbb100d6 | |||
| 094044ed73 | |||
| 884967423f | |||
| 35b3cbfdc0 | |||
| 8b5648a3dc | |||
| bb92857db9 | |||
| ee7556f706 | |||
| ad54c0e1b5 |
5
pom.xml
5
pom.xml
@ -74,6 +74,11 @@
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
|
||||
@ -40,7 +40,7 @@ public class CustomRouteConfig {
|
||||
RouteLocatorBuilder.Builder routesBuilder = builder.routes();
|
||||
|
||||
// 定义需要路由的服务列表
|
||||
String[] serviceNames = {"sczx-user", "sczx-store", "sczx-car", "sczx-order", "sczx-sync", "sczx-singlepay"};
|
||||
String[] serviceNames = {"sczx-user", "sczx-store", "sczx-car", "sczx-order", "sczx-sync", "sczx-singlepay", "sczx-notify"};
|
||||
|
||||
// 为每个服务添加路由规则(如果服务存在)
|
||||
for (String serviceName : serviceNames) {
|
||||
|
||||
@ -59,7 +59,7 @@ public class DynamicRouteConfig implements RouteDefinitionLocator {
|
||||
List<RouteDefinition> routeDefinitions = new ArrayList<>();
|
||||
|
||||
// 定义需要路由的服务列表
|
||||
String[] serviceNames = {"sczx-user", "sczx-store", "sczx-car", "sczx-order", "sczx-sync", "sczx-singlepay"};
|
||||
String[] serviceNames = {"sczx-user", "sczx-store", "sczx-car", "sczx-order", "sczx-sync", "sczx-singlepay", "sczx-notify"};
|
||||
|
||||
// 为每个服务添加路由规则(如果服务存在)
|
||||
for (String serviceName : serviceNames) {
|
||||
|
||||
@ -42,7 +42,11 @@ public class AuthGlobalFilter implements GlobalFilter, Ordered {
|
||||
"/zc/car/carModel/pageStoreCarModel",
|
||||
//同步服务都放行
|
||||
"/zc/sync/**",
|
||||
//支付服务都放行
|
||||
"/zc/singlepay/**",
|
||||
"/zc/notify/**",
|
||||
"/zc/order/pubOrder/**",
|
||||
"/zc/order/verify/**",
|
||||
|
||||
|
||||
// Swagger 放行
|
||||
|
||||
@ -20,6 +20,7 @@ import org.springframework.web.server.ServerWebExchangeDecorator;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
|
||||
@ -64,7 +65,8 @@ public class LoggingGlobalFilter implements GlobalFilter, Ordered {
|
||||
private static class CustomExchange extends ServerWebExchangeDecorator implements CustomExchangeInterface {
|
||||
|
||||
private final StringBuilder requestBody = new StringBuilder();
|
||||
private String responseBody;
|
||||
private final StringBuilder responseBodyBuilder = new StringBuilder();
|
||||
|
||||
|
||||
|
||||
public CustomExchange(ServerWebExchange delegate) {
|
||||
@ -88,25 +90,31 @@ public class LoggingGlobalFilter implements GlobalFilter, Ordered {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ServerHttpResponse getResponse() {
|
||||
return new ServerHttpResponseDecorator(super.getResponse()) {
|
||||
private final StringBuilder responseBodyBuilder = new StringBuilder();
|
||||
ServerHttpResponse originalResponse = super.getResponse();
|
||||
DataBufferFactory bufferFactory = originalResponse.bufferFactory();
|
||||
|
||||
return new ServerHttpResponseDecorator(originalResponse) {
|
||||
@Override
|
||||
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
|
||||
if (body instanceof Flux) {
|
||||
return super.writeWith(Flux.from(body)
|
||||
.doOnNext(buffer -> {
|
||||
DataBufferUtils.join(Flux.just(buffer))
|
||||
.map(dataBuffer -> {
|
||||
byte[] content = new byte[dataBuffer.readableByteCount()];
|
||||
dataBuffer.read(content);
|
||||
responseBody = new String(content, StandardCharsets.UTF_8);
|
||||
return dataBuffer;
|
||||
})
|
||||
.subscribe();
|
||||
}));
|
||||
Flux<? extends DataBuffer> flux = (Flux<? extends DataBuffer>) body;
|
||||
return super.writeWith(flux.doOnNext(dataBuffer -> {
|
||||
// 复制数据以避免修改原始缓冲区
|
||||
DataBuffer duplicate = dataBuffer.factory().allocateBuffer(dataBuffer.readableByteCount());
|
||||
duplicate.write(dataBuffer);
|
||||
|
||||
// 读取数据内容用于日志记录
|
||||
byte[] bytes = new byte[duplicate.readableByteCount()];
|
||||
duplicate.read(bytes);
|
||||
String bodyContent = new String(bytes, StandardCharsets.UTF_8);
|
||||
responseBodyBuilder.append(bodyContent);
|
||||
|
||||
// 释放复制的缓冲区
|
||||
DataBufferUtils.release(duplicate);
|
||||
}));
|
||||
}
|
||||
return super.writeWith(body);
|
||||
}
|
||||
@ -120,7 +128,7 @@ public class LoggingGlobalFilter implements GlobalFilter, Ordered {
|
||||
|
||||
@Override
|
||||
public String getResponseBody() {
|
||||
return responseBody;
|
||||
return responseBodyBuilder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ -30,38 +31,46 @@ import java.util.List;
|
||||
//@Configuration
|
||||
public class ResponseLogGlobalFilter implements GlobalFilter, Ordered {
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
try {
|
||||
ServerHttpResponse originalResponse = exchange.getResponse();
|
||||
DataBufferFactory bufferFactory = originalResponse.bufferFactory();
|
||||
HttpStatus statusCode = originalResponse.getStatusCode();
|
||||
|
||||
// 排除特定路径,避免处理Swagger等静态资源
|
||||
String path = exchange.getRequest().getURI().getPath();
|
||||
if (path.contains("/v2/api-docs") ||
|
||||
path.contains("/swagger") ||
|
||||
path.contains("/webjars")) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
if (statusCode != HttpStatus.OK) {
|
||||
return chain.filter(exchange);//降级处理返回数据
|
||||
}
|
||||
|
||||
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
|
||||
@Override
|
||||
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
|
||||
if (body instanceof Flux) {
|
||||
Flux<? extends DataBuffer> fluxBody = Flux.from(body);
|
||||
|
||||
return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
|
||||
return super.writeWith(fluxBody.map(dataBuffer -> {
|
||||
// 复制数据以避免修改原始缓冲区
|
||||
byte[] content = new byte[dataBuffer.readableByteCount()];
|
||||
dataBuffer.read(content);
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
|
||||
// 合并多个流集合,解决返回体分段传输
|
||||
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
|
||||
DataBuffer buff = dataBufferFactory.join(dataBuffers);
|
||||
byte[] content = new byte[buff.readableByteCount()];
|
||||
buff.read(content);
|
||||
DataBufferUtils.release(buff);//释放掉内存
|
||||
|
||||
//排除Excel导出,不是application/json不打印。若请求是上传图片则在最上面判断。
|
||||
// 排除Excel导出,不是application/json不打印。若请求是上传图片则在最上面判断。
|
||||
MediaType contentType = originalResponse.getHeaders().getContentType();
|
||||
if (!MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) {
|
||||
if (contentType == null || !MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) {
|
||||
return bufferFactory.wrap(content);
|
||||
}
|
||||
|
||||
// 构建返回日志
|
||||
String joinData = new String(content);
|
||||
String joinData = new String(content, StandardCharsets.UTF_8);
|
||||
String result = modifyBody(joinData);
|
||||
List<Object> rspArgs = new ArrayList<>();
|
||||
rspArgs.add(originalResponse.getStatusCode().value());
|
||||
@ -69,8 +78,8 @@ public class ResponseLogGlobalFilter implements GlobalFilter, Ordered {
|
||||
rspArgs.add(result);
|
||||
log.info("<-- {} {}\n{}", rspArgs.toArray());
|
||||
|
||||
getDelegate().getHeaders().setContentLength(result.getBytes().length);
|
||||
return bufferFactory.wrap(result.getBytes());
|
||||
// 返回处理后的内容
|
||||
return bufferFactory.wrap(result.getBytes(StandardCharsets.UTF_8));
|
||||
}));
|
||||
} else {
|
||||
log.error("<-- {} 响应code异常", getStatusCode());
|
||||
@ -88,14 +97,23 @@ public class ResponseLogGlobalFilter implements GlobalFilter, Ordered {
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.HIGHEST_PRECEDENCE;
|
||||
// 设置较低优先级,确保在其他过滤器之后执行
|
||||
return Ordered.LOWEST_PRECEDENCE - 1;
|
||||
}
|
||||
|
||||
//返回统一的JSON日期数据 2024-02-23 11:00, null转空字符串
|
||||
private String modifyBody(String jsonStr){
|
||||
JSONObject json = JSON.parseObject(jsonStr, Feature.AllowISO8601DateFormat);
|
||||
JSONObject.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm";
|
||||
return JSONObject.toJSONString(json, (ValueFilter) (object, name, value) -> value == null ? "" : value, SerializerFeature.WriteDateUseDateFormat);
|
||||
if (jsonStr == null || jsonStr.isEmpty()) {
|
||||
return jsonStr;
|
||||
}
|
||||
try {
|
||||
JSONObject json = JSON.parseObject(jsonStr, Feature.AllowISO8601DateFormat);
|
||||
JSONObject.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm";
|
||||
return JSONObject.toJSONString(json, (ValueFilter) (object, name, value) -> value == null ? "" : value, SerializerFeature.WriteDateUseDateFormat);
|
||||
} catch (Exception e) {
|
||||
// 如果不是有效的JSON,直接返回原始字符串
|
||||
return jsonStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,55 +1,6 @@
|
||||
server:
|
||||
port: 8089
|
||||
shutdown: graceful # 启用优雅停机
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: sczx-gateway
|
||||
cloud:
|
||||
# gateway:
|
||||
# discovery:
|
||||
# locator:
|
||||
# enabled: true # 开启从注册中心动态获取路由
|
||||
# lower-case-service-id: true # 服务名转小写
|
||||
# routes:
|
||||
# - id: sczx_user
|
||||
## uri: http://172.18.0.4:8081
|
||||
# uri: lb://sczx_user
|
||||
# predicates:
|
||||
# - Path=/zc/user/**
|
||||
# filters:
|
||||
# - StripPrefix=2
|
||||
# - id: sczx_store
|
||||
# uri: lb://sczx_store
|
||||
# predicates:
|
||||
# - Path=/zc/store/**
|
||||
# filters:
|
||||
# - StripPrefix=2
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 115.190.8.52:8848 # Nacos 地址
|
||||
group: DEFAULT_GROUP
|
||||
watch-delay: 120000 # 60秒刷新一次
|
||||
loadbalancer:
|
||||
ribbon:
|
||||
enabled: false
|
||||
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: "*"
|
||||
endpoint:
|
||||
health:
|
||||
show-details: always
|
||||
|
||||
auth:
|
||||
secret-key: his-is-a-very-long-and-secure-secret-key-for-jwt-signing-please-dont-use-short-keys
|
||||
token-expiration: 86400000 # 24小时
|
||||
|
||||
#logging:
|
||||
# level:
|
||||
# org.springframework.cloud.gateway: DEBUG
|
||||
# org.springframework.cloud.loadbalancer: DEBUG
|
||||
# reactor.core.publisher: DEBUG
|
||||
|
||||
10
src/main/resources/bootstrap.yml
Normal file
10
src/main/resources/bootstrap.yml
Normal file
@ -0,0 +1,10 @@
|
||||
spring:
|
||||
application:
|
||||
name: sczx-gateway # 应用名称,对应 Nacos 配置的 dataId
|
||||
cloud:
|
||||
nacos:
|
||||
server-addr: 115.190.8.52:8848 # Nacos 服务器地址
|
||||
config:
|
||||
group: DEFAULT_GROUP # 配置分组
|
||||
file-extension: yaml # 配置文件格式
|
||||
timeout: 5000 # 配置读取超时时间
|
||||
Reference in New Issue
Block a user