跨线程发送 TraceId

Posted

技术标签:

【中文标题】跨线程发送 TraceId【英文标题】:Send TraceId across Threads 【发布时间】:2018-06-10 15:50:40 【问题描述】:

我们有一个遵循微服务架构的分布式应用程序。在我们的一项微服务中,我们遵循生产者-消费者模式。

生产者接收请求,将其持久化到数据库,将请求推送到 BlockingQueue 并将响应发送回客户端。运行在单独线程上的消费者正在监听阻塞队列。获取请求对象的那一刻,它会对其执行特定操作。

生产者收到的请求使用 CompleteableFutures 异步持久化到数据库中。

这里的问题是如何将 TraceId 转发给消费者线程中处理 requestObject 的方法。因为消费者线程可能会在响应发送给消费者之后很久才处理这些对象。

还有如何跨异步调用转发traceId?

谢谢

【问题讨论】:

【参考方案1】:

这是一个有趣的问题。我认为您可以做的是将请求与其标头一起保留。然后在消费者端,您可以使用与我们在这里类似的方式使用SpanExtractor 接口 - https://github.com/spring-cloud/spring-cloud-sleuth/blob/v1.3.0.RELEASE/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java#L351 (Span parent = spanExtractor().joinTrace(new HttpServletRequestTextMap(request));)。这意味着我们从HttpServletRequest 中提取值来构建一个跨度。然后,一旦您检索到Span,您可以在处理之前使用Tracer#continueSpan(Span) 方法,然后在finally 块中使用Tracer#detach(Span)。例如。

Span parent = spanExtractor().joinTrace(new HttpServletRequestTextMap(request));
try 
   tracer.continueSpan(parent);
   // do whatever you need
 catch(Exception e) 
  tracer.addTag("error", doSthWithTheExceptionMsg(e));
 finally 
  tracer.detach(parent); 

【讨论】:

我们也会尝试这样做,但我们现在正在做的是,保持 traceId,然后在消费者线程中我们调用:- Span parent = Span.builder().traceId(Span.hexToId(traceId)).build(); tracer.createSpan("ConsumerSpan", parent); 你看到任何这种方法的缺点? 此外,消费者再次产生多个线程,因为消费者内部的所有调用都是异步的,处理这个问题的正确方法是什么? 在我看来还可以。您发现任何问题吗? 嗨,通过将 traceableExecutorPool 传递给 CompletableFuture.supplyAsync 解决了这个问题。由于restTemplate 正在一个新线程中执行,该线程没有与之关联的traceId。每次调用 restTemplate.exchange 都会生成一个新的 TraceId。 CompletableFuture.supplyAsync(() -> restTemplate.exchange(requestEntity, String.class), traceableExecutorService) 有人可能会觉得这很有用:) 此示例可在 docs fair 中找到。通过 Traceable ES 包装可调用对象的方法。无论如何,我很高兴事情对你有用!我们可以将此标记为已回答吗?

以上是关于跨线程发送 TraceId的主要内容,如果未能解决你的问题,请参考以下文章

Jmeter跨线程组调用token

跨线程操作Treeview

PyQt跨线程发出信号

Jmeter 跨线程组传递参数 之两种方法

全链路参数透传

全链路参数透传