• CompletableFuture使用不当的生产问题记录


    问题描述:

    某个时间段,多台服务器大量出现502错误。之后自行恢复

    排查过程:

    查询该时间段服务响应情况,发现很多后端服务响应时间超级慢 60s加,之后nginx大量尝试后端服务,直至诊断服务不可用,nginx直接返回502.

    多个后端服务均60s+,抽查了几个业务代码功能,发现并不是接口性能问题。

    排查了网络问题,网络也无问题

    排查了cpu情况,cpu使用率也无问题

    最后怀疑是某一个接口卡死,导致其他接口线程等待,造成很多接口都无法正常响应

    为了确认是否是这个原因,对改时间段的有问题的请求进行了次数汇总,发现一个合并请求的接口发生问题的次数明显很高,所以重点排查

    改服务业务代码采用了 CompletableFuture 来并发处理多个请求

    1、这个是一个http 请求合并接口,响应时间等于最后一个http 请求完成时间

    2、接口内使用CompletableFuture,没有设置过线程池,所以默认线程池大小为cpu 核数。

    3、短时间大量请求过来先堵塞CompletableFuture的默认线程池,然后堵塞 tomcat 线程池,最后造成 502

    但为什么一直没问题,突然有问题呢?

    此接口请求并发数该时间段远远大于日常请求并发数

    优化:

    1、使用CompletableFuture,设置过线程池,配置线程数100

    2、对Http请求设置响应超时时间

    1. final ExecutorService executorService = Executors.newFixedThreadPool(100);
    2. @PostMapping("/combine")
    3. @ApiOperation(value = "接口合并")
    4. public BaseDtoResponse<Map<String, String>> combine(HttpServletRequest req,
    5. @RequestBody List<CombineRequest> request) {
    6. Map<String, String> headers = new HashMap<>();
    7. for (HeaderEnum headerEnum : HeaderEnum.values()) {
    8. headers.put(headerEnum.getKey(), req.getHeader(headerEnum.getKey()));
    9. }
    10. Map<String, String> map = new ConcurrentHashMap<>();
    11. // request.parallelStream().forEach(v -> resolve(headers, v, map));
    12. long start = System.currentTimeMillis();
    13. List<CompletableFuture> futures = Lists.newArrayList();
    14. for (CombineRequest r : request) {
    15. futures.add(CompletableFuture.runAsync(() -> {
    16. long end = System.currentTimeMillis();
    17. log.info("combine.combine.time1:{}",(end - start));
    18. DefaultHttpParam defaultHttpParam = new DefaultHttpParam();
    19. defaultHttpParam.setHeaders(headers);
    20. defaultHttpParam.setUrl(r.getUrl());
    21. defaultHttpParam.setBody(r.getBody());
    22. defaultHttpParam.setMethod(r.getMethod());
    23. try {
    24. String resp = HttpClient.send(defaultHttpParam);
    25. map.put(r.getMethod() + " " + r.getUrl(), resp);
    26. log.info("combine.combine.getMethod:{},getUrl{},getBody:{},headers:{},resp:{}",r.getMethod(),r.getUrl(),r.getBody(),JSON.toJSONString(headers), resp);
    27. } catch (IOException e) {
    28. log.info("combine.combine.error: {}", e);
    29. }
    30. },executorService).thenRunAsync(() -> {
    31. long end = System.currentTimeMillis();
    32. log.info("combine.combine.time2:{}",(end - start));
    33. },executorService));
    34. }
    35. CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenRun(() -> {
    36. long end = System.currentTimeMillis();
    37. log.info("combine.combine.time3:{}",(end - start));
    38. }).join();
    39. return ResponseFormatterHelper.success(map);
    40. }

  • 相关阅读:
    docker 数据卷
    《向量数据库指南》——用 Milvus Cloud和 NVIDIA Merlin 搭建高效推荐系统结果
    好心情精神心理科医生:精神病人为何会出现幻觉?
    1-Pytorch初始化张量和张量的类型
    Android 使用Kotlin封装RecyclerView
    几种单例模式
    K8S 在docker上部署 Selenium Grid (最新版)
    springboot项结构分析
    测试用例的设计方法(全):判定表驱动分析方法
    传感器融合与自动驾驶
  • 原文地址:https://blog.csdn.net/qq_36042938/article/details/125544563