阻止 sleuth fron 为新线程创建新跟踪 ID 的方法
The way to stop sleuth fron creating new trace id for new threads
1.
线程 1 主线程
myClient.methohThaCallAnotherRemoteService();
2.
在我的 MyClient
class 中,我们通过 restTeamplate
调用另一个服务
Single<MyObject> methohThaCallAnotherRemoteService() {
return Single.fromCallable( () -> { ...
final ResponseEntity<MyObject> response = restTemplate.postForEntity...
return response.getBody();
})
.subscribeOn(Schedulers.io()); // to be run on separate threads / scheduler
3.
然后在ClientHttpRequestInterceptorImpl
中定义为
ClientHttpRequestInterceptorImpl implements org.springframework.http.client.ClientHttpRequestInterceptor {
...
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) { ...
log.info("HTTP_CLIENT_REQUEST" + logResponse));
问题是:
Sleuth 创建了一个带有跨度的单独跟踪 ID (trace-id-2)。日志看起来像这样:
来自主线程:
> INFO [my-app,trace-id-1, span-id-1] [nio-8080-exec-2] ...
来自 io 线程:
> INFO [my-app,trace-id-2, span-id-2,false] 117037 ---
> [readScheduler-2] d.p.i.w.ClientHttpRequestInterceptorImpl :
> {"logType":"HTTP_CLIENT_REQUEST"
我希望 trace-id-2 是 trace-id-1 这样我就可以将请求从主线程转移到 io 线程。 (否则在跟踪方面没有意义)。
我仍然希望我的 logger.info()
位于 ClientHttpRequestInterceptorImpl
内
问:具体是如何实现的?
我认为你可以像
一样继续主跨度
https://cloud.spring.io/spring-cloud-sleuth/reference/html/#continuing-spans-2
// method declaration
@ContinueSpan(log = "testMethod11")
void testMethod11(@SpanTag("testTag11") String param);
// method execution
this.testBean.testMethod11("test");
this.testBean.testMethod13();
或
https://cloud.spring.io/spring-cloud-sleuth/reference/html/#continuing-spans
// let's assume that we're in a thread Y and we've received
// the `initialSpan` from thread X
Span continuedSpan = this.tracer.toSpan(newSpan.context());
try {
// ...
// You can tag a span
continuedSpan.tag("taxValue", taxValue);
// ...
// You can log an event on a span
continuedSpan.annotate("taxCalculated");
}
finally {
// Once done remember to flush the span. That means that
// it will get reported but the span itself is not yet finished
continuedSpan.flush();
}
或
https://cloud.spring.io/spring-cloud-sleuth/reference/html/#creating-spans-with-explicit-parent
// let's assume that we're in a thread Y and we've received
// the `initialSpan` from thread X. `initialSpan` will be the parent
// of the `newSpan`
Span newSpan = null;
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(initialSpan)) {
newSpan = this.tracer.nextSpan().name("calculateCommission");
// ...
// You can tag a span
newSpan.tag("commissionValue", commissionValue);
// ...
// You can log an event on a span
newSpan.annotate("commissionCalculated");
}
finally {
// Once done remember to finish the span. This will allow collecting
// the span to send it to Zipkin. The tags and events set on the
// newSpan will not be present on the parent
if (newSpan != null) {
newSpan.finish();
}
}
或
https://cloud.spring.io/spring-cloud-sleuth/reference/html/#runnable-and-callable
Runnable runnable = new Runnable() {
@Override
public void run() {
// do some work
}
@Override
public String toString() {
return "spanNameFromToStringMethod";
}
};
// Manual `TraceRunnable` creation with explicit "calculateTax" Span name
Runnable traceRunnable = new TraceRunnable(this.tracing, spanNamer, runnable,
"calculateTax");
// Wrapping `Runnable` with `Tracing`. That way the current span will be available
// in the thread of `Runnable`
Runnable traceRunnableFromTracer = this.tracing.currentTraceContext()
.wrap(runnable);
以下示例显示如何为 Callable 执行此操作:
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
return someLogic();
}
@Override
public String toString() {
return "spanNameFromToStringMethod";
}
};
// Manual `TraceCallable` creation with explicit "calculateTax" Span name
Callable<String> traceCallable = new TraceCallable<>(this.tracing, spanNamer,
callable, "calculateTax");
// Wrapping `Callable` with `Tracing`. That way the current span will be available
// in the thread of `Callable`
Callable<String> traceCallableFromTracer = this.tracing.currentTraceContext()
.wrap(callable);
这样,您可以确保为每次执行创建并关闭一个新跨度。
我不太精通这个,但是我看到了一些帮助
https://gitter.im/spring-cloud/spring-cloud-sleuth?at=5eb2b6f397338850a2ee2b3f
这里粘贴了一些建议:
https://github.com/spring-cloud/spring-cloud-sleuth/issues/1570#issuecomment-627181235
您可以先尝试订阅,然后"then"您的可调用包装器
为了解决这个问题我不得不使用 LazyTraceExecutor
- org.springframework.cloud.sleuth.instrument.async.LazyTraceExecutor
实例化示例
LazyTraceExecutor lazyTraceExecutor;
@PostConstrucrt
void init() {
LazyTraceExecutor lazyTraceExecutor = new LazyTraceExecutor(this.beanFactory,
java.util.concurrent.Executors.newFixedThreadPool(10, threadFactory("Sched-A-%d"))
);
}
然后在生成新线程的 RX 方法中。
Single<MyObject> methohThaCallAnotherRemoteService() {
return Single.fromCallable( () -> { ...
final ResponseEntity<MyObject> response = restTemplate.postForEntity...
return response.getBody();
})
.subscribeOn(Schedulers.from(lazyTraceExecutor)); // use it
现在我对 span 没问题了。
1.
线程 1 主线程
myClient.methohThaCallAnotherRemoteService();
2.
在我的 MyClient
class 中,我们通过 restTeamplate
Single<MyObject> methohThaCallAnotherRemoteService() {
return Single.fromCallable( () -> { ...
final ResponseEntity<MyObject> response = restTemplate.postForEntity...
return response.getBody();
})
.subscribeOn(Schedulers.io()); // to be run on separate threads / scheduler
3.
然后在ClientHttpRequestInterceptorImpl
中定义为
ClientHttpRequestInterceptorImpl implements org.springframework.http.client.ClientHttpRequestInterceptor {
...
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) { ...
log.info("HTTP_CLIENT_REQUEST" + logResponse));
问题是: Sleuth 创建了一个带有跨度的单独跟踪 ID (trace-id-2)。日志看起来像这样:
来自主线程:
> INFO [my-app,trace-id-1, span-id-1] [nio-8080-exec-2] ...
来自 io 线程:
> INFO [my-app,trace-id-2, span-id-2,false] 117037 ---
> [readScheduler-2] d.p.i.w.ClientHttpRequestInterceptorImpl :
> {"logType":"HTTP_CLIENT_REQUEST"
我希望 trace-id-2 是 trace-id-1 这样我就可以将请求从主线程转移到 io 线程。 (否则在跟踪方面没有意义)。
我仍然希望我的 logger.info()
位于 ClientHttpRequestInterceptorImpl
问:具体是如何实现的?
我认为你可以像
一样继续主跨度https://cloud.spring.io/spring-cloud-sleuth/reference/html/#continuing-spans-2
// method declaration
@ContinueSpan(log = "testMethod11")
void testMethod11(@SpanTag("testTag11") String param);
// method execution
this.testBean.testMethod11("test");
this.testBean.testMethod13();
或
https://cloud.spring.io/spring-cloud-sleuth/reference/html/#continuing-spans
// let's assume that we're in a thread Y and we've received
// the `initialSpan` from thread X
Span continuedSpan = this.tracer.toSpan(newSpan.context());
try {
// ...
// You can tag a span
continuedSpan.tag("taxValue", taxValue);
// ...
// You can log an event on a span
continuedSpan.annotate("taxCalculated");
}
finally {
// Once done remember to flush the span. That means that
// it will get reported but the span itself is not yet finished
continuedSpan.flush();
}
或
https://cloud.spring.io/spring-cloud-sleuth/reference/html/#creating-spans-with-explicit-parent
// let's assume that we're in a thread Y and we've received
// the `initialSpan` from thread X. `initialSpan` will be the parent
// of the `newSpan`
Span newSpan = null;
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(initialSpan)) {
newSpan = this.tracer.nextSpan().name("calculateCommission");
// ...
// You can tag a span
newSpan.tag("commissionValue", commissionValue);
// ...
// You can log an event on a span
newSpan.annotate("commissionCalculated");
}
finally {
// Once done remember to finish the span. This will allow collecting
// the span to send it to Zipkin. The tags and events set on the
// newSpan will not be present on the parent
if (newSpan != null) {
newSpan.finish();
}
}
或
https://cloud.spring.io/spring-cloud-sleuth/reference/html/#runnable-and-callable
Runnable runnable = new Runnable() {
@Override
public void run() {
// do some work
}
@Override
public String toString() {
return "spanNameFromToStringMethod";
}
};
// Manual `TraceRunnable` creation with explicit "calculateTax" Span name
Runnable traceRunnable = new TraceRunnable(this.tracing, spanNamer, runnable,
"calculateTax");
// Wrapping `Runnable` with `Tracing`. That way the current span will be available
// in the thread of `Runnable`
Runnable traceRunnableFromTracer = this.tracing.currentTraceContext()
.wrap(runnable);
以下示例显示如何为 Callable 执行此操作:
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
return someLogic();
}
@Override
public String toString() {
return "spanNameFromToStringMethod";
}
};
// Manual `TraceCallable` creation with explicit "calculateTax" Span name
Callable<String> traceCallable = new TraceCallable<>(this.tracing, spanNamer,
callable, "calculateTax");
// Wrapping `Callable` with `Tracing`. That way the current span will be available
// in the thread of `Callable`
Callable<String> traceCallableFromTracer = this.tracing.currentTraceContext()
.wrap(callable);
这样,您可以确保为每次执行创建并关闭一个新跨度。
我不太精通这个,但是我看到了一些帮助 https://gitter.im/spring-cloud/spring-cloud-sleuth?at=5eb2b6f397338850a2ee2b3f
这里粘贴了一些建议:
https://github.com/spring-cloud/spring-cloud-sleuth/issues/1570#issuecomment-627181235
您可以先尝试订阅,然后"then"您的可调用包装器
为了解决这个问题我不得不使用 LazyTraceExecutor
- org.springframework.cloud.sleuth.instrument.async.LazyTraceExecutor
实例化示例
LazyTraceExecutor lazyTraceExecutor;
@PostConstrucrt
void init() {
LazyTraceExecutor lazyTraceExecutor = new LazyTraceExecutor(this.beanFactory,
java.util.concurrent.Executors.newFixedThreadPool(10, threadFactory("Sched-A-%d"))
);
}
然后在生成新线程的 RX 方法中。
Single<MyObject> methohThaCallAnotherRemoteService() {
return Single.fromCallable( () -> { ...
final ResponseEntity<MyObject> response = restTemplate.postForEntity...
return response.getBody();
})
.subscribeOn(Schedulers.from(lazyTraceExecutor)); // use it
现在我对 span 没问题了。