博客
关于我
SpringCloud Alibaba实战二十九 | SpringCloud Gateway 请求响应日志
阅读量:630 次
发布时间:2019-03-14

本文共 5423 字,大约阅读时间需要 18 分钟。

Spring Cloud Gateway日志收集与存储优化方案

背景与目标

在微服务架构中,日志的收集与存储对于问题定位和服务优化具有重要意义。Spring Cloud Gateway作为API网关,在处理大量请求时,日志的清晰收集和存储成为关键环节。本节将详细介绍在网关层统一日志收集的实现方案。

实现目标

  • 日志收集:获取请求的输入输出参数,封装成统一的日志格式。
  • 日志存储:将收集到的日志存储到MongoDB中,支持后续检索。
  • 核心实现步骤

    1. 定义日志模型

    首先,我们需要定义一个日志模型 GatewayLog 来存储请求的相关信息。该模型应包含以下字段:

    @Data@Documentpublic class GatewayLog {    @Id    private String id;    /** 访问实例 */    private String targetServer;    /** 请求路径 */    private String requestPath;    /** 请求方法 */    private String requestMethod;    /** 协议 */    private String schema;    /** 请求体 */    private String requestBody;    /** 响应体 */    private String responseData;    /** 请求IP */    private String ip;    /** 请求时间 */    private Date requestTime;    /** 响应时间 */    private Date responseTime;    /** 执行时间 */    private long executeTime;}

    2. 定义日志存储接口

    接下来,定义一个 AccessLogRepository 作为数据存储接口:

    @Repositorypublic interface AccessLogRepository extends ReactiveMongoRepository
    { @Query("{ gatewayLog.ip : ?0 }") Flux
    findByIp(String ip);}

    3. 实现日志收集逻辑

    AccessLogFilter 中,实现日志收集逻辑。该过滤器需要:

  • 获取请求的输入输出参数。
  • 将日志封装到 GatewayLog 对象中。
  • 将日志存储到MongoDB。
  • @Slf4j@Componentpublic class AccessLogFilter implements GlobalFilter, Ordered {    @Autowired    private AccessLogService accessLogService;    private final List
    messageReaders = HandlerStrategies.withDefaults().messageReaders(); @Override public int getOrder() { return -100; } @Override public Mono
    filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); String requestPath = request.getPath().pathWithinApplication().value(); Route route = getGatewayRoute(exchange); String ipAddress = WebUtils.getServerHttpRequestIpAddress(request); GatewayLog gatewayLog = new GatewayLog(); gatewayLog.setSchema(request.getURI().getScheme()); gatewayLog.setRequestMethod(request.getMethodValue()); gatewayLog.setRequestPath(requestPath); gatewayLog.setTargetServer(route.getId()); gatewayLog.setRequestTime(new Date()); gatewayLog.setIp(ipAddress); if (isBodyReadable(request)) { return writeBodyLog(exchange, chain, gatewayLog); } else { return writeBasicLog(exchange, chain, gatewayLog); } } private boolean isBodyReadable(ServerWebExchange request) { return request.getHeaders().getContentType().isCompatibleWith( MediaType.APPLICATION_FORM_URLENCODED) || request.getHeaders().getContentType().isCompatibleWith(MediaType.APPLICATION_JSON); } private Mono
    writeBasicLog(...) { // 收集请求参数并封装到日志 // 获取响应体并记录日志 return chain.filter(exchange.mutate().response(decoratedResponse).build()) .then(Mono.fromRunnable(() -> writeAccessLog(gatewayLog))); } private Mono
    writeBodyLog(...) { // 处理请求体,避免只能读取一次的问题 return bodyInserter.insert(outputMessage, ...) .then(Mono.fromRunnable(() -> { decoratedRequest = requestDecorate(exchange, headers, outputMessage); decoratedResponse = recordResponseLog(exchange, gatewayLog); return chain.filter(exchange.mutate().request(decoratedRequest).response(decoratedResponse).build()) .then(Mono.fromRunnable(() -> writeAccessLog(gatewayLog))); })); } private void writeAccessLog(GatewayLog gatewayLog) { log.info(gatewayLog.toString()); accessLogService.saveAccessLog(gatewayLog); } private Route getGatewayRoute(ServerWebExchange exchange) { return exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR); } private ServerHttpRequestDecorator requestDecorate(...) { // 重新封装请求,处理分段传输问题 return new ServerHttpRequestDecorator(exchange.getRequest()) { @Override public HttpHeaders getHeaders() { HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.putAll(super.getHeaders()); if (contentLength > 0) { httpHeaders.setContentLength(contentLength); } else { httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); } return httpHeaders; } @Override public Flux
    .getBody() { return outputMessage.getBody(); } }; } private ServerHttpResponseDecorator recordResponseLog(...) { // 处理响应体分段传输问题 return new ServerHttpResponseDecorator(response) { @Override public Mono
    writeWith(Publisher
    body) { // 处理响应体,生成执行时间和存储日志 return super.writeWith(body); } }; }}

    4. 引入MongoDB

    在项目中引入MongoDB Reactive版本:

    org.springframework.boot
    spring-boot-starter-data-mongodb-reactive

    5. 配置MongoDB

    在配置中心(如Nacos)中添加MongoDB配置:

    spring:    data:        mongodb:            host: xxx.xx.x.xx            port: 27017            database: accesslog            username: accesslog            password: xxxx

    6. 测试与验证

  • 启动MongoDB服务。
  • 发送请求,通过Postman或其他工具验证日志是否正确存储在MongoDB中。
  • 检查日志内容,确保所有必要信息都已正确收集和存储。
  • 注意事项

  • 过滤器顺序getOrder() 方法返回值必须为 -1,否则标准过滤器会在获取后端响应之前发送响应。
  • 分段传输:处理大体积请求时,需要注意分段传输的问题,避免占用过多内存。
  • 日志格式:确保日志格式符合要求,方便后续分析和检索。
  • 通过以上优化方案,我们可以在网关层统一收集和存储日志,支持后续问题定位和服务监控。

    转载地址:http://cycoz.baihongyu.com/

    你可能感兴趣的文章
    oracle--用户,权限,角色的管理
    查看>>
    Oracle-定时任务-JOB
    查看>>
    oracle.dataaccess 连接池,asp.net使用Oracle.DataAccess.dll连接Oracle
    查看>>
    oracle00205报错,Oracle控制文件损坏报错场景
    查看>>
    Oracle10g EM乱码之快速解决
    查看>>
    Oracle10g下载地址--多平台下的32位和64位
    查看>>
    Oracle10g安装了11g的ODAC后,PL/SQL连接提示TNS:无法解析指定的连接标识符
    查看>>
    oracle11g dataguard物理备库搭建(关闭主库cp数据文件到备库)
    查看>>
    Oracle11G基本操作
    查看>>
    Oracle11g服务详细介绍及哪些服务是必须开启的?
    查看>>
    Oracle11g静默安装dbca,netca报错处理--直接跟换操作系统
    查看>>
    oracle12安装软件后安装数据库,然后需要自己配置监听
    查看>>
    Oracle——08PL/SQL简介,基本程序结构和语句
    查看>>
    Oracle——distinct的用法
    查看>>
    Oracle、MySQL、SQL Server架构大对比
    查看>>
    oracle下的OVER(PARTITION BY)函数介绍
    查看>>
    Oracle中DATE数据相减问题
    查看>>
    Oracle中merge into的使用
    查看>>
    oracle中sql查询上月、本月、上周、本周、昨天、今天的数据!
    查看>>
    oracle中sql的case语句运用--根据不同条件去排序!
    查看>>