博客
关于我
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/

    你可能感兴趣的文章
    Network Dissection:Quantifying Interpretability of Deep Visual Representations(深层视觉表征的量化解释)
    查看>>
    Network Sniffer and Connection Analyzer
    查看>>
    Network 灰鸽宝典【目录】
    查看>>
    Network-Emulator Network-Emulator-Toolkit网络模拟器使用
    查看>>
    Networkx写入Shape文件
    查看>>
    NetworkX系列教程(11)-graph和其他数据格式转换
    查看>>
    Networkx读取军械调查-ITN综合传输网络?/读取GML文件
    查看>>
    NetworkX:是否为每个节点添加超链接?
    查看>>
    network小学习
    查看>>
    Netwox网络工具使用详解
    查看>>
    Net与Flex入门
    查看>>
    Net任意String格式转换为DateTime类型
    查看>>
    net包之IPConn
    查看>>
    net发布的dll方法和类显示注释信息(字段说明信息)[图解]
    查看>>
    Net和T-sql中的日期函数操作
    查看>>
    Net处理html页面元素工具类(HtmlAgilityPack.dll)的使用
    查看>>
    Net操作Excel(终极方法NPOI)
    查看>>
    Net操作配置文件(Web.config|App.config)通用类
    查看>>
    net网络查看其参数state_dict,data,named_parameters
    查看>>
    Net连接mysql的公共Helper类MySqlHelper.cs带MySql.Data.dll下载
    查看>>