如何使用Spring Cloud Sleuth
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用Spring Cloud Sleuth相关的知识,希望对你有一定的参考价值。
本节将更详细地介绍如何使用Spring Cloud Sleuth。 它涵盖了诸如使用 Spring Cloud Sleuth API 或通过注释控制跨度生命周期等主题。 我们还介绍了一些Spring Cloud Sleuth最佳实践。
如果您从Spring Cloud Sleuth开始,则在进入本节之前,您可能应该阅读入门指南。
1. 使用春云侦探的 API 跨越生命周期
其模块中的春云侦探核心包含由跟踪器实现的所有必要接口。 该项目带有OpenZipkin Brave实现。 您可以通过查看来检查跟踪器如何桥接到侦探的 API。api
org.springframework.cloud.sleuth.brave.bridge
最常用的接口是:
-
org.springframework.cloud.sleuth.Tracer
- 使用跟踪器,可以创建捕获请求关键路径的根跨度。 -
org.springframework.cloud.sleuth.Span
- 跨度是需要启动和停止的单个工作单元。 包含计时信息以及事件和标记。
您也可以直接使用跟踪器实现的 API。
让我们看一下以下 Span 生命周期操作。
- 开始:启动跨度时,将分配其名称并记录开始时间戳。
- end:跨度完成(记录跨度的结束时间),如果跨度被采样,则有资格收集(例如到 Zipkin)。
- 继续:跨度继续,例如在另一个线程中。
- 使用显式父项创建:您可以创建新的范围并为其设置显式父项。
春云侦探为您创建一个实例。 为了使用它,您可以自动连线它。 |
1.1. 创建和结束跨度
您可以使用 手动创建跨度,如以下示例所示:Tracer
// Start a span. If there was a span present in this thread it will become
// the `newSpan`s parent.
Span newSpan = this.tracer.nextSpan().name("calculateTax");
try (Tracer.SpanInScope ws = this.tracer.withSpan(newSpan.start()))
// ...
// You can tag a span
newSpan.tag("taxValue", taxValue);
// ...
// You can log an event on a span
newSpan.event("taxCalculated");
finally
// Once done remember to end the span. This will allow collecting
// the span to send it to a distributed tracing system e.g. Zipkin
newSpan.end();
在前面的示例中,我们可以看到如何创建跨度的新实例。 如果此线程中已存在跨度,则它将成为新跨度的父级。
创建范围后始终清洁。 |
如果范围包含大于 50 个字符的名称,则该名称将被截断为 50 个字符。 你的名字必须明确和具体。 大牌会导致延迟问题,有时甚至会导致异常。 |
1.2. 连续跨度
有时,您不想创建一个新的跨度,但想要继续一个。 这种情况的示例可能如下所示:
- AOP:如果在达到一个方面之前已经创建了一个跨度,您可能不希望创建新跨度。
要继续跨度,您可以将跨度存储在一个线程中,然后将其传递给另一个线程,如下例所示。
Span spanFromThreadX = this.tracer.nextSpan().name("calculateTax");
try (Tracer.SpanInScope ws = this.tracer.withSpan(spanFromThreadX.start()))
executorService.submit(() ->
// Pass the span from thread X
Span continuedSpan = spanFromThreadX;
// ...
// You can tag a span
continuedSpan.tag("taxValue", taxValue);
// ...
// You can log an event on a span
continuedSpan.event("taxCalculated");
).get();
finally
spanFromThreadX.end();
1.3. 使用显式父级创建范围
您可能希望开始一个新的跨度,并提供该跨度的显式父级。 假设跨度的父级位于一个线程中,并且您希望在另一个线程中启动新的跨度。 每当您调用时,它都会创建一个范围来引用当前范围内的范围。 可以将范围放在作用域中,然后调用,如以下示例所示:Tracer.nextSpan()
Tracer.nextSpan()
// lets assume that were in a thread Y and weve received
// the `initialSpan` from thread X. `initialSpan` will be the parent
// of the `newSpan`
Span newSpan = null;
try (Tracer.SpanInScope ws = this.tracer.withSpan(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.event("commissionCalculated");
finally
// Once done remember to end the span. This will allow collecting
// the span to send it to e.g. Zipkin. The tags and events set on the
// newSpan will not be present on the parent
if (newSpan != null)
newSpan.end();
创建这样的跨度后,您必须完成它。 否则不报告(例如向Zipkin报告)。 |
您还可以使用版本显式提供父跨度。Tracer.nextSpan(Span parentSpan)
2. 命名跨度
选择范围名称并非易事。 范围名称应描述操作名称。 名称应为低基数,因此不应包含标识符。
由于正在进行大量检测,因此某些跨度名称是人为的:
-
controller-method-name
当由方法名称为controllerMethodName
-
async
对于使用包装和接口完成的异步操作。Callable
Runnable
- 方法批注返回类的简单名称。
@Scheduled
幸运的是,对于异步处理,您可以提供显式命名。
2.1.@SpanName注解
您可以使用注释显式命名范围,如以下示例所示:@SpanName
@SpanName("calculateTax")
class TaxCountingRunnable implements Runnable
@Override
public void run()
// perform logic
在这种情况下,当以下列方式处理时,范围被命名为:calculateTax
Runnable runnable = new TraceRunnable(this.tracer, spanNamer, new TaxCountingRunnable());
Future<?> future = executorService.submit(runnable);
// ... some additional logic ...
future.get();
2.2.toString()Method
创建单独的类是非常罕见的。 通常,创建这些类的匿名实例。 不能批注此类类。 为了克服这个限制,如果存在noannotation,我们检查类是否具有该方法的自定义实现。Runnable
Callable
@SpanName
toString()
运行此类代码会导致创建名为 span 的范围,如以下示例所示:calculateTax
Runnable runnable = new TraceRunnable(this.tracer, spanNamer, new Runnable()
@Override
public void run()
// perform logic
@Override
public String toString()
return "calculateTax";
);
Future<?> future = executorService.submit(runnable);
// ... some additional logic ...
future.get();
3. 使用注释管理跨度
使用批注管理跨度有很多很好的理由,包括:
- 与 API 无关意味着跨度协作。 使用批注允许用户添加到跨度,而不需要库依赖跨度 API。 这样做可以让 Sleuth 更改其核心 API,以减少对用户代码的影响。
- 减少了基本跨度操作的表面积。 如果没有此功能,则必须使用 span api,该 API 具有可能不正确使用的生命周期命令。 通过仅公开范围、标记和日志功能,您可以在不意外中断跨度生命周期的情况下进行协作。
- 与运行时生成的代码协作。 使用Spring Data和Feign等库,接口的实现是在运行时生成的。 因此,对象的跨度包装很乏味。 现在,您可以提供接口注释和这些接口的参数。
3.1. 创建新跨度
如果不想手动创建局部范围,可以使用注释。 此外,我们还提供注释以自动方式添加标签。@NewSpan
@SpanTag
现在我们可以考虑一些用法示例。
@NewSpan
void testMethod();
在没有任何参数的情况下批注方法会导致创建一个新范围,其名称等于批注的方法名称。
@NewSpan("customNameOnTestMethod4")
void testMethod4();
如果在批注中提供值(直接或通过设置参数),则创建的范围会将提供的值作为名称。name
// method declaration
@NewSpan(name = "customNameOnTestMethod5")
void testMethod5(@SpanTag("testTag") String param);
// and method execution
this.testBean.testMethod5("test");
您可以组合名称和标签。 让我们关注后者。 在这种情况下,批注方法的参数运行时值的值将成为标记的值。 在我们的示例中,标签键是,标签值是。testTag
test
@NewSpan(name = "customNameOnTestMethod3")
@Override
public void testMethod3()
您可以在类和接口上放置注释。 如果您覆盖接口的方法并为注释提供不同的值,则最具体的值获胜(在本例中为 set)。@NewSpan
@NewSpan
customNameOnTestMethod3
3.2. 连续跨度
如果要向现有范围添加标记和注释,可以使用注释,如以下示例所示:@ContinueSpan
// method declaration
@ContinueSpan(log = "testMethod11")
void testMethod11(@SpanTag("testTag11") String param);
// method execution
this.testBean.testMethod11("test");
this.testBean.testMethod13();
(请注意,与注释相比,您还可以使用参数添加日志。@NewSpan
log
这样,跨度就会继续,并且:
- 已创建命名的日志条目。
testMethod11.before
testMethod11.after
- 如果引发异常,则还会创建日志条目 namedis。
testMethod11.afterFailure
- 创建具有键 of 和值 of 的标记。
testTag11
test
3.3. 高级标签设置
有 3 种不同的方法可以将标签添加到范围。 所有这些都由注释控制。 优先级如下:SpanTag
- 尝试使用类型的 bean 和提供的名称。
TagValueResolver
- 如果尚未提供 Bean 名称,请尝试计算表达式。 我们寻找阿豆。 默认实现使用 SPEL 表达式解析。重要只能引用 SPEL 表达式中的属性。 由于安全约束,不允许方法执行。
TagValueExpressionResolver
- 如果我们找不到任何要计算的表达式,则返回参数的值。
toString()
3.3.1. 自定义提取器
以下方法的标记值由接口的实现计算。 它的类名必须作为属性的值传递。TagValueResolver
resolver
请考虑以下带批注的方法:
@NewSpan
public void getAnnotationForTagValueResolver(
@SpanTag(key = "test", resolver = TagValueResolver.class) String test)
现在进一步考虑以下 Bean 实现:TagValueResolver
@Bean(name = "myCustomTagValueResolver")
public TagValueResolver tagValueResolver()
return parameter -> "Value from myCustomTagValueResolver";
前面的两个示例导致将标记值设置为等于。Value from myCustomTagValueResolver
3.3.2. 解析值的表达式
请考虑以下带批注的方法:
@NewSpan
public void getAnnotationForTagValueExpression(
@SpanTag(key = "test", expression = "hello + characters") String test)
没有自定义实现会导致计算 SPEL 表达式,并且在跨度上设置了值为 of 的标记。 如果要使用其他表达式解析机制,可以创建自己的 Bean 实现。TagValueExpressionResolver
hello characters
3.3.3. 使用toString() 方法
请考虑以下带批注的方法:
@NewSpan
public void getAnnotationForArgumentToString(@SpanTag("test") Long param)
使用值 of 运行上述方法,以设置具有 String 值为 的标记。15
"15"
4. 接下来要读什么
您现在应该了解如何使用Spring Cloud Sleuth以及应遵循的一些最佳实践。 您现在可以继续了解特定的Spring CloudSleuth功能,或者您可以跳过并阅读Spring Cloud Sleuth中可用的集成。
以上是关于如何使用Spring Cloud Sleuth的主要内容,如果未能解决你的问题,请参考以下文章
Spring cloud sleuth 注解:自动装配不起作用
Spring Cloud Sleuth - 获取当前的 traceId?
Spring-Cloud-Gateway 创建的初始 Trace 都命名为“/”,无论路径如何