问题描述:
某个时间段,多台服务器大量出现502错误。之后自行恢复
排查过程:
查询该时间段服务响应情况,发现很多后端服务响应时间超级慢 60s加,之后nginx大量尝试后端服务,直至诊断服务不可用,nginx直接返回502.
多个后端服务均60s+,抽查了几个业务代码功能,发现并不是接口性能问题。
排查了网络问题,网络也无问题
排查了cpu情况,cpu使用率也无问题
最后怀疑是某一个接口卡死,导致其他接口线程等待,造成很多接口都无法正常响应
为了确认是否是这个原因,对改时间段的有问题的请求进行了次数汇总,发现一个合并请求的接口发生问题的次数明显很高,所以重点排查
改服务业务代码采用了 CompletableFuture 来并发处理多个请求
1、这个是一个http 请求合并接口,响应时间等于最后一个http 请求完成时间
2、接口内使用CompletableFuture,没有设置过线程池,所以默认线程池大小为cpu 核数。
3、短时间大量请求过来先堵塞CompletableFuture的默认线程池,然后堵塞 tomcat 线程池,最后造成 502
但为什么一直没问题,突然有问题呢?
此接口请求并发数该时间段远远大于日常请求并发数
优化:
1、使用CompletableFuture,设置过线程池,配置线程数100
2、对Http请求设置响应超时时间
- final ExecutorService executorService = Executors.newFixedThreadPool(100);
-
-
- @PostMapping("/combine")
- @ApiOperation(value = "接口合并")
- public BaseDtoResponse<Map<String, String>> combine(HttpServletRequest req,
- @RequestBody List<CombineRequest> request) {
-
-
-
- Map<String, String> headers = new HashMap<>();
-
- for (HeaderEnum headerEnum : HeaderEnum.values()) {
- headers.put(headerEnum.getKey(), req.getHeader(headerEnum.getKey()));
- }
-
- Map<String, String> map = new ConcurrentHashMap<>();
- // request.parallelStream().forEach(v -> resolve(headers, v, map));
- long start = System.currentTimeMillis();
- List<CompletableFuture> futures = Lists.newArrayList();
- for (CombineRequest r : request) {
- futures.add(CompletableFuture.runAsync(() -> {
- long end = System.currentTimeMillis();
- log.info("combine.combine.time1:{}",(end - start));
-
- DefaultHttpParam defaultHttpParam = new DefaultHttpParam();
-
- defaultHttpParam.setHeaders(headers);
- defaultHttpParam.setUrl(r.getUrl());
- defaultHttpParam.setBody(r.getBody());
- defaultHttpParam.setMethod(r.getMethod());
- try {
- String resp = HttpClient.send(defaultHttpParam);
- map.put(r.getMethod() + " " + r.getUrl(), resp);
- log.info("combine.combine.getMethod:{},getUrl{},getBody:{},headers:{},resp:{}",r.getMethod(),r.getUrl(),r.getBody(),JSON.toJSONString(headers), resp);
- } catch (IOException e) {
- log.info("combine.combine.error: {}", e);
- }
-
- },executorService).thenRunAsync(() -> {
- long end = System.currentTimeMillis();
- log.info("combine.combine.time2:{}",(end - start));
- },executorService));
- }
-
- CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenRun(() -> {
- long end = System.currentTimeMillis();
- log.info("combine.combine.time3:{}",(end - start));
- }).join();
-
- return ResponseFormatterHelper.success(map);
- }