记一次接口优化操作

  项目正式上线之后,后期主要是不断地进行版本迭代,开发新的功能。自己参与

开发的项目正式开始使用后,人数还不少,早上高峰期的时候一个接口一个小时的请求

数达到约3万。而且这只是部分用户在进行使用,还没有大规模地放开,服务器已经

开始告警,某一个接口的查询超过四五秒。收到这个信息后,负责人立马让我们查看

日志信息,排查问题。通过命令 grep 接口请求的URL” 日志文件名,查看多台服务

器上面打印的日志信息,发现确实有多台服务器上打印的接口耗时都超过5S以上。

  查看日志的方式是自己在网上搜索的,可是接口耗时却是系统中写的,如何衡量一个

接口的好坏?其中一个指标就是处理请求的能力,可以通过jmeter来做接口的性能测试。

这个在测试阶段,专业的测试人员都已经测试过,肯定是符合要求我们才上生产。可是

现在已经是服务在生产上面跑,在生产阶段,不能在使用那种方式来处理。对于后端

开发人员来说,可以在需要的接口请求中,打印每一次接口的耗时。简单处理方式如下:

首先需要定义一个拦截器:

@Slf4j

public class InterfaceInterceptor implements HandlerInterceptor {

    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        request.setAttribute("REQUEST_START_TIME", String.valueOf(System.currentTimeMillis()));

        return true;

    }

 

    @Override

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

        String requestURI = request.getRequestURI();

        String requestStartTime = (String)request.getAttribute("REQUEST_START_TIME");

        log.info("接口 {} 请求耗时: {} ms", requestURI, System.currentTimeMillis() - Long.parseLong(requestStartTime));

    }

}

重写两个方法,一个方法是在处理请求前调用的preHandle方法,处理逻辑很简单,设置一个固定的变量值,

具体的值设置为当前的时间戳,将其放在请求对象中。接口请求处理完成之后,调用afterCompletion方法,

将之前存入的值取出来,转换为Long类型,然后用当前时间时间戳减去最开始请求的时间戳,就可以计算出单个接口处理的耗时。

接下来写一个简单的测试方法,如下

@Slf4j

@RestController

@RequestMapping("/happy/yilang")

public class TestControlelr {

    @GetMapping("/interface")

    public String interfaceTest(){

        return "接口耗时统计";

    }

}

最后将拦截器添加到拦截器配置类中,即是注册拦截器,拦截的指定的路径为/happy/**,可以

按需要进行灵活的配置,如下

@Configuration

public class WebMvcConfig extends WebMvcConfigurerAdapter {

 

    /**

     *  Function:  addInterceptors

     *  Author :  kaye0110,

 *  Version : 1.0

     *  Description : 注册拦截器

     *  Param and Description :

     *  @param registry

     */

    @Override

    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(InterfaceInterceptor()).addPathPatterns("/happy/**");

        super.addInterceptors(registry);

    }

    @Bean

    public InterfaceInterceptor InterfaceInterceptor(){

        return  new InterfaceInterceptor();

    }

}

启动项目,测试结果如下

 

 

 

拿到每一次接口请求的耗时数据,对于性能要求是一个重要的参考指标。每个公司的要求都不一样,有的可能要求1s内返回,

有的可能要求500ms内返回,有的可能要求20ms内返回。根据公司自己的要求,然后来对比接口的耗时,就可以判断出这个

接口的性能如何,是否需要优化。

  自己在排查日志的过程中发现,这个接口就是一个简单的接口,没有做比较复杂的计算操作,就是根据主键ID查询一条数据

信息,为什么会导致这么慢呢?怎么进行优化呢?之后经过仔细分析,发现在请求高峰期的时候,数据库的CPU达到90%多,

因为数据库的性能急剧下降。负责人经过查看数据库服务器的相关信息,普通开发人员没权限看,还发现MQ在快速的大量的

向数据库中写数据, 还有定时任务也在频繁的向数据库写数据,所以导致数据库的性能解决下降。由于起了多台服务器,定时

任务的处理就更加地频繁,如果每间隔两分钟处理一次定时任务,5台服务器两分钟就处理10次。因此就找到对应的解决方案:

.1.扩容数据库;.2.这个接口实时查询数据库修改为从缓存中取数据;.3.增加定时任务的处理时间间隔;4.降低MQ消息消费的流量。

定时将需要查询的所有数据,预先加载到缓存中,然后查询的时候就直接从缓存当中取,不再去查询数据库。通过这几步的优化,

最终很好地解决了这个接口的性能问题。

  如果有其他更好建议的小伙伴,欢迎留言讨论。

posted @ 2022-12-01 22:13  一只爱阅读的程序员  阅读(78)  评论(0编辑  收藏  举报