openFeign自定义耗时统计

本文介绍了如何在openFeign中添加耗时拦截器,记录请求总时长和远程调用时间,以解决调用耗时过大的问题,并在高并发场景下使用追踪ID和MDC进行更精确的性能监控和调试。
摘要由CSDN通过智能技术生成

最近测出调用耗时过大,为了看到全部问题,老大要求添加耗时拦截器,打印请求总时长、远程调用时长。
本次改造部分基于此博客:原部分内容

一、说明

  • openFeign 本身自带相关日志功能,但是打开debug级别日志,打印的东西过多。于是自己写

二、openFeign请求拦截器:设定远程请求起始时间

@Slf4j
@Component
public class CipherInterceptor implements RequestInterceptor {

    /**
     * 耗时统计
     */
    private long start = 0;

    @Override
    public void apply(RequestTemplate template) {
        if (log.isInfoEnabled()) {
            start = System.currentTimeMillis();
            log.info("---套件请求开始---");
        }
        try {
            byte[] body = template.body();
            String bodyStr = new String(body, StandardCharsets.UTF_8);
            // 打印请求消息
            log.info("cipher-request-path: {}",template.path());
            log.info("cipher-request-bodyStr : {}",bodyStr);
        }catch (Exception e){
            log.error("CipherInterceptor error :",e);
        }
    }

    /**
     * 请求结束
     */
    public void end(){
        if (log.isInfoEnabled()) {
            long cost = System.currentTimeMillis() - start;
            log.info("---套件请求结束-耗时: {} ms ---", cost);
        }
    }

}

三、openFeign请求解码器:统计远程请求耗时

@Slf4j
@Component
public class CipherDecoder implements Decoder,ErrorDecoder {

    @Resource
    CipherInterceptor interceptor;

    /**
     * 对象映射
     */
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public Object decode(Response response, Type type) throws FeignException {
        interceptor.end();
        if (null == response.body()) {
            log.error("解码异常,响应体为空 status: {}", response.status());
            throw new CipherSuiteRpcException();
        }
        String respond;
        // 解析通用响应
        try {
            respond = StreamUtils.copyToString(response.body().asInputStream(), StandardCharsets.UTF_8);
            log.info("cipher-respond: {}", respond);
            // 响应字符转dto
            return resultToDto(respond, type);
        } catch (Exception e) {
            log.error("CipherRespondDecoder error:", e);
            throw new CipherSuiteRpcException();
        }
    }

    /**
     * 响应转传输对象
     *
     * @param respond 响应
     * @param type    类型
     * @return 最终响应对象
     * @throws JsonProcessingException json处理异常
     */
    private Object resultToDto(String respond, Type type) throws JsonProcessingException {
        TypeFactory typeFactory = objectMapper.getTypeFactory();
        CipherClientResponse baseResponse = objectMapper.readValue(respond,
                typeFactory.constructParametricType(CipherClientResponse.class, typeFactory.constructType(type)));
        log.info("CipherClientResponse : {}", baseResponse);
        // 直接返回
        if (type instanceof CipherClientResponse) {
            log.debug("无响应返回:{}", baseResponse);
            return baseResponse;
        }
        // 过滤
        if (baseResponse.isOk()) {
            return baseResponse.getData();
        }
        // 抛出异常
        log.error("异常响应: {}", baseResponse);
        throw new CipherSuiteRpcException();
    }

    @Override
    public Exception decode(String methodKey, Response response) {
        interceptor.end();
        log.info("methodKey: {}", methodKey);
        // 成功响应码不做处理
        if (HttpStatus.SC_OK == response.status()) {
            return new ErrorDecoder.Default().decode(methodKey, response);
        }
        String respond = null;
        // 解析响应
        try {
            InputStream inputStream = response.body().asInputStream();
            respond = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error("error : ", e);
        }
        log.error("error respond : {}", respond);
        throw new CipherSuiteRpcException();
    }

}

四、设定服务内部:总耗时拦截器

@Slf4j
@Configuration
public class ServletFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (log.isInfoEnabled()) {
            long start = System.currentTimeMillis();
            log.info("---request-start---");
            chain.doFilter(request, response);
            long end = System.currentTimeMillis();
            long cost = end - start;
            log.info("---request-end 耗时 : {} ms ---", cost);
        }else{
            chain.doFilter(request, response);
        }
    }
}

五、后续补充:测试找我说“并发测试分辨不清晰”

日志没问题,但是测试需要对照“远程耗时”、“服务内部耗时”的时候,发现高并发情况下,打印出来的日志长一个模样
于是加上追踪id,调整拦截器的架构方式(第二、四步的对象合一了),代码如下:

@Slf4j
@Component
public class CipherInterceptor implements RequestInterceptor, Filter {

    /**
     * 耗时统计
     */
    private long start = 0;

    /**
     * 并发追踪
     */
    private String tranId = null;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 统计耗时
        if (log.isInfoEnabled()) {
            tranId = UUID.randomUUID().toString().replace("-", "");
            long reqStart = System.currentTimeMillis();
            log.info("--- {} request-start---", tranId);
            chain.doFilter(request, response);
            long reqEnd = System.currentTimeMillis();
            long cost = reqEnd - reqStart;
            log.info("--- {} request-end 耗时 : {} ms ---", tranId, cost);
        } else {
            chain.doFilter(request, response);
        }
    }

    @Override
    public void apply(RequestTemplate template) {
        if (log.isInfoEnabled()) {
            start = System.currentTimeMillis();
            log.info("--- {} 套件请求开始---", tranId);
        }
        try {
            byte[] body = template.body();
            String bodyStr = new String(body, StandardCharsets.UTF_8);
            // 打印请求消息
            log.info("cipher-request-path: {}", template.path());
            log.info("cipher-request-bodyStr : {}", bodyStr);
        } catch (Exception e) {
            log.error("CipherInterceptor error :", e);
        }
    }

    /**
     * 请求结束
     */
    public void end() {
        if (log.isInfoEnabled()) {
            long cost = System.currentTimeMillis() - start;
            log.info("--- {} 套件请求结束-耗时: {} ms ---", tranId, cost);
        }
    }

}

六、再次补充(高并发情况下,第五步有bug。最后改用了MDC)

改造博文链接:MDC改造

任何想法,欢迎留言讨论

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
u-boot是一种常用的开源引导程序,用于嵌入式系统的启动。它在加载Linux内核之前负责初始化硬件、加载文件系统和设备驱动程序等任务。为了优化系统性能,我们需要对u-boot的内核启动耗时进行统计和分析。 u-boot内核启动耗时统计是通过在u-boot的源代码中添加计时代码实现的。在初始化硬件之后、加载文件系统之前和加载设备驱动程序之前,我们可以添加计时代码来获取各个阶段的耗时。计时一般使用CPU的定时器或者系统时钟的计数器。 首先,在u-boot的源代码中找到合适的位置,插入计时代码。计时代码可以使用u-boot提供的计时函数来实现,也可以使用操作系统提供的计时功能。 其次,将获取到的耗时数据输出到串口、网络或者存储设备中,以便后续的分析。可以使用串口打印函数输出到串口终端,也可以使用网络调试工具输出到远程主机,在u-boot中运行脚本将数据写入存储设备等。 最后,根据获取到的耗时数据进行统计和分析。可以对各个阶段的耗时进行累积,计算出总的内核启动耗时。还可以对每个阶段的耗时进行比较,找出性能瓶颈,进行优化。 通过以上步骤,我们可以得到u-boot内核启动耗时统计数据,进而分析系统性能、优化启动时间。这对于嵌入式系统的开发和调试非常重要,能够有效提高系统的响应速度和用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

*crzep

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值