Fremount 2024-05-11 21:56 采纳率: 11.1%
浏览 10
已结题

分布式追踪trace异常问题

分布式追踪trace异常问题
我部署了一个简单的微服务系统,这个系统的拓扑图如下所示

img

然后我使用一个分布式追踪系统jaeger对其进行链路追踪,由于我配置了istio,并对这个系统所涉及到的所有pod都安装了sidecar,所以无需对原有代码进行操作即可进行分布式追踪,但是现在问题是一个请求会形成多个trace

img


看了下代码,可能是异步操作的问题


public CustomResult addOrder(@RequestBody OrderDTO orderDTO) {
        String orderContent = orderDTO.getOrderContent();
        String userId = orderDTO.getOrderUserId();
        String price = orderDTO.getOrderPrice();

        // 首先请求库存服务来确认库存

//        String reserveUrl = "http://localhost:8004/api/reserve/getReserve?orderContent=" + orderContent;
        String reserveUrl = "http://reserve.demo.svc.cluster.local/api/reserve/getReserve?orderContent=" + orderContent;

        CustomResult reserveResult = restTemplate.getForObject(reserveUrl, CustomResult.class);

        // 检查库存服务的响应
        if (reserveResult != null && reserveResult.getStatus() == 20000) {
            // 库存确认后请求支付服务 Post请求
            String payUrl = "http://pay.demo.svc.cluster.local/api/pay/topay";
            PayDTO payDTO = new PayDTO(userId,price);
            CompletableFuture<CustomResult> payResultFuture = CompletableFuture.supplyAsync(() -> {
                return restTemplate.postForObject(payUrl, payDTO, CustomResult.class);
            });
            // 请求支付服务的响应
            CustomResult payResult = null;
            try {
                payResult = payResultFuture.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
            //  检查支付服务的响应
            if (payResult != null && payResult.getStatus() == 20000) {
                return new CustomResult(20000, "下单成功", null);
            } else {
                return new CustomResult(40000, "支付失败", null);
            }
        } else {
            return new CustomResult(40000, "库存不足", null);
        }
    }

但是在reserve服务请求下面的notification、delivery的时候我使用的是同步操作,但是这还是会形成两个trace

    @GetMapping("/getReserve")
    public CustomResult getReserve(@RequestParam String orderContent) {
        // 请求通知服务
        String noticeUrl = "http://notification.demo.svc.cluster.local/api/notification/toNotify";
        restTemplate.getForObject(noticeUrl,String.class);
        // 请求配送服务 post请求
        DeliveryDTO deliveryDTO = new DeliveryDTO("北京","北京","海淀","中关村南四街","中国科学院软件研究所" ,"粥隔离","18888888888");
        String deliveryUrl = "http://delivery.demo.svc.cluster.local/api/delivery/toDelivery";
        restTemplate.postForObject(deliveryUrl,deliveryDTO,String.class);
        return new CustomResult(20000, "库存充足", null);
    }

img


按理来说这本来应该是一个trace,但是为什么却出现了多个trace呢?
请各位big brother们分析下给出具体的原因解释以及问题的解决方案,谢谢!

  • 写回答

12条回答 默认 最新

  • 码农阿豪@新空间代码工作室 Java领域优质创作者 2024-05-11 21:57
    关注
    让阿豪来帮你解答,本回答参考chatgpt3.5编写提供,如果还有疑问可以评论或留言
    问题分析: 在代码中,使用了异步的 CompletableFuture.supplyAsync() 方法来发起对支付服务的请求,这种方式会新开一个线程进行请求,并且返回一个 CompletableFuture 对象,而这个对象最终会被阻塞等待支付服务响应结果。 在这种情况下,分布式追踪系统 Jaeger 会将该异步线程的请求信息作为单独的一个 Trace 记录下来,因此就会导致一个请求形成多个 Trace 的问题。 解决方案: 可以使用 CompletableFuture.thenCompose() 方法来改写对支付服务的请求,这种方式会将请求合并到当前的 Trace 中,从而避免多个 Trace 的产生。 具体的实现方式是,在请求库存服务之后,对支付服务的请求使用 thenCompose() 方法进行链式调用,代码示例如下所示:
    CompletableFuture<CustomResult> payResultFuture = restTemplate.postForObject(payUrl, payDTO, CustomResult.class);
    payResultFuture.thenCompose((result) -> CompletableFuture.supplyAsync(() -> result));
    

    这样做可以将对支付服务的请求嵌入到当前的 Trace 中,从而避免了一个请求形成多个 Trace 的问题。 完整的代码示例如下所示:

    @PostMapping("/addOrder")
    public CustomResult addOrder(@RequestBody OrderDTO orderDTO) {
        String orderContent = orderDTO.getOrderContent();
        String userId = orderDTO.getOrderUserId();
        String price = orderDTO.getOrderPrice();
        // 请求库存服务来确认库存
        String reserveUrl = "http://reserve.demo.svc.cluster.local/api/reserve/getReserve?orderContent=" + orderContent;
        CustomResult reserveResult = restTemplate.getForObject(reserveUrl, CustomResult.class);
        // 检查库存服务的响应
        if (reserveResult != null && reserveResult.getStatus() == 20000) {
            // 库存确认后请求支付服务 Post请求
            String payUrl = "http://pay.demo.svc.cluster.local/api/pay/topay";
            PayDTO payDTO = new PayDTO(userId, price);
            CompletableFuture<CustomResult> payResultFuture =
                    restTemplate.postForObject(payUrl, payDTO, CustomResult.class);
            payResultFuture.thenCompose((result) -> CompletableFuture.supplyAsync(() -> result));
            // 请求支付服务的响应
            CustomResult payResult = null;
            try {
                payResult = payResultFuture.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
            // 检查支付服务的响应
            if (payResult != null && payResult.getStatus() == 20000) {
                return new CustomResult(20000, "下单成功", null);
            } else {
                return new CustomResult(40000, "支付失败", null);
            }
        } else {
            return new CustomResult(40000, "库存不足", null);
        }
    }
    

    参考文献: - Jaeger 官方文档:https://www.jaegertracing.io/docs/ - CompletableFuture 官方文档:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html

    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 5月12日
  • 创建了问题 5月11日

悬赏问题

  • ¥20 centos7下载mysql提示DNS解析异常
  • ¥20 vx转账功能对方不能领取
  • ¥15 vue2+codemirror 运行后页面光标太大了
  • ¥15 pdfjs库如何在前端实现打印、旋转等功能
  • ¥15 combobox数据绑定问题
  • ¥15 maven打包时,为什么有的依赖打不进去包
  • ¥15 求复现机器学习文章一篇
  • ¥15 爬虫抓取数据|解析视频文本内容(相关搜索:自动化)
  • ¥15 combobox绑定值改变的问题
  • ¥15 from seleniumwire import webdriver 在抓取http://链接的时候会自动转https://这个怎么解决