项目正式上线之后,后期主要是不断地进行版本迭代,开发新的功能。自己参与
开发的项目正式开始使用后,人数还不少,早上高峰期的时候一个接口一个小时的请求
数达到约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")
@LcloudThreadLimiter(maxThread="max_thread", waitTime="wait_time")
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消息消费的流量。
定时将需要查询的所有数据,预先加载到缓存中,然后查询的时候就直接从缓存当中取,不再去查询数据库。通过这几步的优化,
最终很好地解决了这个接口的性能问题。
如果有其他更好建议的小伙伴,欢迎留言讨论。
标签:拦截器,请求,数据库,接口,耗时,操作,优化,public From: https://www.cnblogs.com/yilangcode/p/16942979.html