温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Spring Cloud Sleuth Span的示例分析

发布时间:2021-09-03 14:44:30 来源:亿速云 阅读:251 作者:小新 栏目:大数据

这篇文章主要介绍了Spring Cloud Sleuth Span的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

Span自定义

感谢SpanInjector和SpanExtractor,您可以自定义spans的创建和传播方式。

目前有两种在进程之间传递跟踪信息的内置方式:

  • 通过Spring Integration

  • 通过HTTP

Span ids从Zipkin兼容(B3)头(Message或HTTP头)中提取,以启动或加入现有跟踪。跟踪信息被注入到任何出站请求中,所以下一跳可以提取它们。

与以前版本的Sleuth相比,重要的变化是Sleuth正在实施Open Tracing的TextMap概念。在Sleuth,它被称为SpanTextMap。基本上这个想法是通过SpanTextMap可以抽象出任何通信手段(例如消息,http请求等)。这个抽象定义了如何将数据插入到载体中以及如何从那里检索数据。感谢这样,如果您想要使用一个使用FooRequest作为发送HTTP请求的平均值的新HTTP库,那么您必须创建一个SpanTextMap的实现,它将调用委托给FooRequest检索和插入HTTP标头。

Spring Integration

对于Spring Integration,有2个接口负责从Message创建Span。这些是:

  • MessagingSpanTextMapExtractor

  • MessagingSpanTextMapInjector

您可以通过提供自己的实现来覆盖它们。

HTTP

对于HTTP,有2个接口负责从Message创建Span。这些是:

  • HttpSpanExtractor

  • HttpSpanInjector

您可以通过提供自己的实现来覆盖它们。

我们假设,而不是标准的Zipkin兼容的跟踪HTTP头名称

  • for trace id - correlationId

  • for span id - mySpanId

这是SpanExtractor的一个例子

static class CustomHttpSpanExtractor implements HttpSpanExtractor {	@Override public Span joinTrace(SpanTextMap carrier) {	Map<String, String> map = TextMapUtil.asMap(carrier);	long traceId = Span.hexToId(map.get("correlationid"));	long spanId = Span.hexToId(map.get("myspanid"));	// extract all necessary headers	Span.SpanBuilder builder = Span.builder().traceId(traceId).spanId(spanId);	// build rest of the Span	return builder.build();	} } static class CustomHttpSpanInjector implements HttpSpanInjector {	@Override	public void inject(Span span, SpanTextMap carrier) {	carrier.put("correlationId", span.traceIdString());	carrier.put("mySpanId", Span.idToHex(span.getSpanId()));	} }
  •  

  •  

  •  

  •  

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

你可以这样注册:

@Bean HttpSpanInjector customHttpSpanInjector() {	return new CustomHttpSpanInjector(); } @Bean HttpSpanExtractor customHttpSpanExtractor() {	return new CustomHttpSpanExtractor(); }
  •  

  •  

Spring Cloud为了安全起见,Sleuth不会将跟踪/跨度相关的标头添加到Http响应。如果您需要标题,那么将标题注入Http响应的自定义SpanInjector,并且可以使用以下方式添加一个使用此标签的Servlet过滤器:

static class CustomHttpServletResponseSpanInjector extends ZipkinHttpSpanInjector {	@Override	public void inject(Span span, SpanTextMap carrier) {	super.inject(span, carrier);	carrier.put(Span.TRACE_ID_NAME, span.traceIdString());	carrier.put(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));	} } static class HttpResponseInjectingTraceFilter extends GenericFilterBean {	private final Tracer tracer;	private final HttpSpanInjector spanInjector;	public HttpResponseInjectingTraceFilter(Tracer tracer, HttpSpanInjector spanInjector) {	this.tracer = tracer;	this.spanInjector = spanInjector;	}	@Override	public void doFilter(ServletRequest request, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {	HttpServletResponse response = (HttpServletResponse) servletResponse;	Span currentSpan = this.tracer.getCurrentSpan();	this.spanInjector.inject(currentSpan, new HttpServletResponseTextMap(response));	filterChain.doFilter(request, response);	}	 class HttpServletResponseTextMap implements SpanTextMap {	 private final HttpServletResponse delegate;	 HttpServletResponseTextMap(HttpServletResponse delegate) {	 this.delegate = delegate;	 }	 @Override	 public Iterator<Map.Entry<String, String>> iterator() {	 Map<String, String> map = new HashMap<>();	 for (String header : this.delegate.getHeaderNames()) {	map.put(header, this.delegate.getHeader(header));	 }	 return map.entrySet().iterator();	 }	 @Override	 public void put(String key, String value) {	this.delegate.addHeader(key, value);	 }	 } }
  •  

  •  

你可以这样注册:

@Bean HttpSpanInjector customHttpServletResponseSpanInjector() {	return new CustomHttpServletResponseSpanInjector(); } @Bean HttpResponseInjectingTraceFilter responseInjectingTraceFilter(Tracer tracer) {	return new HttpResponseInjectingTraceFilter(tracer, customHttpServletResponseSpanInjector()); }

Zipkin中的自定义SA标签

有时你想创建一个手动Span,将一个电话包裹到一个没有被检测的外部服务。您可以做的是创建一个带有peer.service标签的跨度,其中包含要调用的服务的值。下面你可以看到一个调用Redis的例子,它被包装在这样一个跨度里。

org.springframework.cloud.sleuth.Span newSpan = tracer.createSpan("redis"); try {	newSpan.tag("redis.op", "get");	newSpan.tag("lc", "redis");	newSpan.logEvent(org.springframework.cloud.sleuth.Span.CLIENT_SEND);	// call redis service e.g	// return (SomeObj) redisTemplate.opsForHash().get("MYHASH", someObjKey); } finally {	newSpan.tag("peer.service", "redisService");	newSpan.tag("peer.ipv4", "1.2.3.4");	newSpan.tag("peer.port", "1234");	newSpan.logEvent(org.springframework.cloud.sleuth.Span.CLIENT_RECV);	tracer.close(newSpan); }
重要记住不要添加peer.service标签和SA标签!您只需添加peer.service。

自定义服务名称

默认情况下,Sleuth假设当您将跨度发送到Zipkin时,您希望跨度的服务名称等于spring.application.name值。这并不总是这样。在某些情况下,您希望为您应用程序中的所有spans提供不同的服务名称。要实现这一点,只需将以下属性传递给应用程序即可覆盖该值(foo服务名称的示例):

spring.zipkin.service.name: foo
  • 1

主机定位器

为了定义与特定跨度对应的主机,我们需要解析主机名和端口。默认方法是从服务器属性中获取它。如果由于某些原因没有设置,那么我们正在尝试从网络接口检索主机名。

如果您启用了发现客户端,并且更愿意从服务注册表中注册的实例检索主机地址,那么您必须设置属性(适用于基于HTTP和Stream的跨度报告)。

spring.zipkin.locator.discovery.enabled: true
  • 1

Span Data作为消息

您可以通过将spring-cloud-sleuth-stream jar作为依赖关系来累加并发送跨越Spring Cloud Stream的数据,并为RabbitMQ或spring-cloud-starter-stream-kafka添加通道Binder实现(例如spring-cloud-starter-stream-rabbit)为Kafka)。通过将spring-cloud-sleuth-stream jar作为依赖关系,并添加RabbitMQ或spring-cloud-starter-stream-kafka的Binder通道spring-cloud-starter-stream-rabbit来实现{ 22 /} Stream的累积和发送范围数据。
Kafka)。这将自动将您的应用程序转换为有效载荷类型为Spans的邮件的制作者。

Zipkin消费者

有一个特殊的便利注释,用于为Span数据设置消息使用者,并将其推入Zipkin SpanStore。这个应用程序

@SpringBootApplication @EnableZipkinStreamServer public class Consumer {	public static void main(String[] args) {	SpringApplication.run(Consumer.class, args);	} }
  •  

  •  

将通过Spring Cloud StreamBinder(例如RabbitMQ包含spring-cloud-starter-stream-rabbit)来收听您提供的任何运输的Span数据,Redis和Kafka的类似起始者) 。如果添加以下UI依赖关系

<groupId>io.zipkin.java</groupId> <artifactId>zipkin-autoconfigure-ui</artifactId>
  • 1

  • 2

然后,您将有一个Zipkin服务器,您的应用程序在端口9411上承载UI和API。

默认SpanStore是内存中的(适合演示,快速入门)。对于更强大的解决方案,您可以将MySQL和spring-boot-starter-jdbc添加到类路径中,并通过配置启用JDBC SpanStore,例如:

spring:   rabbitmq:     host: ${RABBIT_HOST:localhost}   datasource:     schema: classpath:/mysql.sql     url: jdbc:mysql://${MYSQL_HOST:localhost}/test     username: root     password: root # Switch this on to create the schema on startup:     initialize: true     continueOnError: true   sleuth:     enabled: false zipkin:   storage:     type: mysql
注意@EnableZipkinStreamServer还用@EnableZipkinServer注释,因此该过程还将公开标准的Zipkin服务器端点,以通过HTTP收集spans,并在Zipkin Web UI中进行查询。

定制消费者

也可以使用spring-cloud-sleuth-stream并绑定到SleuthSink来轻松实现自定义消费者。例:

@EnableBinding(SleuthSink.class) @SpringBootApplication(exclude = SleuthStreamAutoConfiguration.class) @MessageEndpoint public class Consumer {     @ServiceActivator(inputChannel = SleuthSink.INPUT)     public void sink(Spans input) throws Exception {         // ... process spans     } }
注意上面的示例消费者应用程序明确排除SleuthStreamAutoConfiguration,因此它不会向其自己发送消息,但这是可选的(您可能实际上想要将消息跟踪到消费者应用程序中)。

为了自定义轮询机制,您可以创建名称等于StreamSpanReporter.POLLER的PollerMetadata类型的bean。在这里可以找到这样一个配置的例子。

@Configuration public static class CustomPollerConfiguration {	@Bean(name = StreamSpanReporter.POLLER)	PollerMetadata customPoller() {	PollerMetadata poller = new PollerMetadata();	poller.setMaxMessagesPerPoll(500);	poller.setTrigger(new PeriodicTrigger(5000L));	return poller;	} }

度量

目前Spring Cloud Sleuth注册了与spans相关的简单指标。它使用Spring Boot的指标支持
来计算接受和删除的数量spans。每次发送到Zipkin时,接受的spans的数量将增加。如果出现错误,那么删除的数字spans将会增加。

感谢你能够认真阅读完这篇文章,希望小编分享的“Spring Cloud Sleuth Span的示例分析”这篇文章对大家有帮助,同时也希望大家多多支持亿速云,关注亿速云行业资讯频道,更多相关知识等着你来学习!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI