热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

微服务全链路跟踪:jaeger集成istio,并兼容ubertraceid与b3

微服,务,全,链路,跟踪,jaeger,集成,istio,并,兼容,uber,tra

微服务全链路跟踪:grpc集成zipkin

微服务全链路跟踪:grpc集成jaeger

微服务全链路跟踪:springcloud集成jaeger

微服务全链路跟踪:jaeger集成istio,并兼容uber-trace-id与b3

公司有自己的一套基于k8s的paas系统,并且集成了istio,这里主要是想讲解下springcloud服务如何集成istio

jaeger跨进程传递

在基于HTTP协议的分布式调用中,通常会使用HTTP Header来传递SpanContext的内容。常见的Wire Protocol包含Zipkin使用的b3 HTTP header,Jaeger使用的uber-trace-id HTTP Header,LightStep使用的"x-ot-span-context" HTTP Header等。Istio1.0支持b3 header和x-ot-span-context header,可以和Zipkin,Jaeger及LightStep对接;istio1.4以上支持uber-trace-id,请参考github官方说明:https://github.com/istio/istio/issues/12400

uber-trace-id

在这里插入图片描述 图中可以看到其中traceId、spanId等字段都拼接到一个头钟了

b3

istio的b3头详情可以参考:https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-request-id b3并没有将字段都拼接,二十单个传递,下面是案例:

X-B3-TraceId:427fde2dc7edb084 X-B3-ParentSpanId:427fde2dc7edb084 X-B3-SpanId:827e270489aafbd7 X-B3-Sampled:1 

变更jaeger传输为b3

如果需要集成istio的jaeger,则需要将传输方式修改为b3 微服务全链路跟踪:springcloud集成jaeger该章中已经描述了如何集成jaeger,这里只需要修改一个配置enable-b3-propagation,如下

opentracing: jaeger: enable-b3-propagation: true// 默认为false udp-sender: host: localhost port: 6831 remote-reporter: flush-interval: 1000 max-queue-size: 5000 log-spans: true probabilistic-sampler: sampling-rate: 1 

这样springboot服务就可以与istio中的jaeger信息串起来形成完整全链路。 在这里插入图片描述

兼容uber-trace-id与b3

现在遇到了另外一个问题,公司已经很早就搭建了一套全链路jaeger,并且已经接入了大部分系统,采用的是默认的header传输,即:uber-trace-id 而下游有很多paas内部系统都是非java的不方便接入jaeger,只是注入了istio,并自动注入了jaeger-agent,这里使用的是b3头传输,这就导致了部分链路上下游无法串联起来。而如果需要统一传输方式暂不现实,首先如果都改成b3,则需要上游很多已接入的系统修改配置为b3,如果是都改成uber-trace-id,istio当前版本不支持,如果需要升级istio,则需要升级kubernetes,风险比较大,所以这里根据实际情况先采用集成两种头,即上游都是uber-trace-id,到中间层服务时手动注入b3相关头如下。

grpc注入b3头

这里需要使用grpc的拦截器

import com.google.common.collect.ImmutableMap; import io.grpc.*; import io.opentracing.Span; import io.opentracing.Tracer; import io.opentracing.propagation.Format; import io.opentracing.propagation.TextMap; import lombok.extern.slf4j.Slf4j; import javax.annotation.Nullable; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.TimeUnit; /** * An intercepter that applies tracing via OpenTracing to all client requests. */ @Slf4j public class ClientTracingInterceptor implements ClientInterceptor { private final Tracer tracer; private final OperationNameConstructor operationNameConstructor; private final boolean streaming; private final boolean verbose; private final Set tracedAttributes; private final ActiveSpanSource activeSpanSource; private final Metadata.Key b3TraceIdKey = Metadata.Key.of("X-B3-TraceId", Metadata.ASCII_STRING_MARSHALLER); private final Metadata.Key b3SpanIdKey = Metadata.Key.of("X-B3-SpanId", Metadata.ASCII_STRING_MARSHALLER); private final Metadata.Key b3ParentSpanIdKey = Metadata.Key.of("X-B3-ParentSpanId", Metadata.ASCII_STRING_MARSHALLER); private final Metadata.Key b3SampledKey = Metadata.Key.of("X-B3-Sampled", Metadata.ASCII_STRING_MARSHALLER); /** * @param */ public ClientTracingInterceptor(Tracer tracer) { this.tracer=tracer; this.operatiOnNameConstructor= OperationNameConstructor.DEFAULT; this.streaming = false; this.verbose = false; this.tracedAttributes = new HashSet(); this.activeSpanSource = ActiveSpanSource.GRPC_CONTEXT; } private ClientTracingInterceptor(Tracer tracer, OperationNameConstructor operationNameConstructor, boolean streaming, boolean verbose, Set tracedAttributes, ActiveSpanSource activeSpanSource) { this.tracer = tracer; this.operatiOnNameConstructor= operationNameConstructor; this.streaming = streaming; this.verbose = verbose; this.tracedAttributes = tracedAttributes; this.activeSpanSource = activeSpanSource; } /** * Use this intercepter to trace all requests made by this client channel. * @param channel to be traced * @return intercepted channel */ public Channel intercept(Channel channel) { return ClientInterceptors.intercept(channel, this); } @Override public  ClientCall interceptCall( MethodDescriptor method, CallOptions callOptions, Channel next ) { final String operatiOnName= operationNameConstructor.constructOperationName(method); Span activeSpan = this.activeSpanSource.getActiveSpan(); final Span span = createSpanFromParent(activeSpan, operationName); for (ClientRequestAttribute attr : this.tracedAttributes) { switch (attr) { case ALL_CALL_OPTIONS: span.setTag("grpc.call_options", callOptions.toString()); break; case AUTHORITY: if (callOptions.getAuthority() == null) { span.setTag("grpc.authority", "null"); } else { span.setTag("grpc.authority", callOptions.getAuthority()); } break; case COMPRESSOR: if (callOptions.getCompressor() == null) { span.setTag("grpc.compressor", "null"); } else { span.setTag("grpc.compressor", callOptions.getCompressor()); } break; case DEADLINE: if (callOptions.getDeadline() == null) { span.setTag("grpc.deadline_millis", "null"); } else { span.setTag("grpc.deadline_millis", callOptions.getDeadline().timeRemaining(TimeUnit.MILLISECONDS)); } break; case METHOD_NAME: span.setTag("grpc.method_name", method.getFullMethodName()); break; case METHOD_TYPE: if (method.getType() == null) { span.setTag("grpc.method_type", "null"); } else { span.setTag("grpc.method_type", method.getType().toString()); } break; case HEADERS: break; } } return new ForwardingClientCall.SimpleForwardingClientCall(next.newCall(method, callOptions)) { @Override public void start(Listener responseListener, Metadata headers) { if (verbose) { span.log("Started call"); } if (tracedAttributes.contains(ClientRequestAttribute.HEADERS)) { span.setTag("grpc.headers", headers.toString()); } tracer.inject(span.context(), Format.Builtin.HTTP_HEADERS, new TextMap() { @Override public void put(String key, String value) { log.info("jaeger key:{},value:{}",key,value); Metadata.Key headerKey = Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER); headers.put(headerKey, value); String[] mm=value.split("%3A"); if("uber-trace-id".equals(key)&&mm.length==4){ headers.put(b3TraceIdKey,mm[0]); log.info("jaeger traceId:{}",mm[0]); headers.put(b3SpanIdKey,mm[1]); headers.put(b3ParentSpanIdKey,mm[2]); headers.put(b3SampledKey,mm[3]); } } @Override public Iterator> iterator() { throw new UnsupportedOperationException( "TextMapInjectAdapter should only be used with Tracer.inject()"); } }); Listener tracingRespOnseListener= new ForwardingClientCallListener .SimpleForwardingClientCallListener(responseListener) { @Override public void onHeaders(Metadata headers) { if (verbose) { span.log(ImmutableMap.of("Response headers received", headers.toString())); } delegate().onHeaders(headers); } @Override public void onMessage(RespT message) { if (streaming || verbose) { span.log("Response received"); } delegate().onMessage(message); } @Override public void onClose(Status status, Metadata trailers) { if (verbose) { if (status.getCode().value() == 0) { span.log("Call closed"); } else { span.log(ImmutableMap.of("Call failed", status.getDescription())); } } span.finish(); delegate().onClose(status, trailers); } }; delegate().start(tracingResponseListener, headers); } @Override public void cancel(@Nullable String message, @Nullable Throwable cause) { String errorMessage; if (message == null) { errorMessage = "Error"; } else { errorMessage = message; } if (cause == null) { span.log(errorMessage); } else { span.log(ImmutableMap.of(errorMessage, cause.getMessage())); } delegate().cancel(message, cause); } @Override public void halfClose() { if (streaming) { span.log("Finished sending messages"); } delegate().halfClose(); } @Override public void sendMessage(ReqT message) { if (streaming || verbose) { span.log("Message sent"); } delegate().sendMessage(message); } }; } private Span createSpanFromParent(Span parentSpan, String operationName) { if (parentSpan == null) { return tracer.buildSpan(operationName).startManual(); } else { return tracer.buildSpan(operationName).asChildOf(parentSpan).startManual(); } } /** * Builds the configuration of a ClientTracingInterceptor. */ public static class Builder { private Tracer tracer; private OperationNameConstructor operationNameConstructor; private boolean streaming; private boolean verbose; private Set tracedAttributes; private ActiveSpanSource activeSpanSource; /** * @param tracer to use for this intercepter * Creates a Builder with default configuration */ public Builder(Tracer tracer) { this.tracer = tracer; this.operatiOnNameConstructor= OperationNameConstructor.DEFAULT; this.streaming = false; this.verbose = false; this.tracedAttributes = new HashSet(); this.activeSpanSource = ActiveSpanSource.GRPC_CONTEXT; } /** * @param operationNameConstructor to name all spans created by this intercepter * @return this Builder with configured operation name */ public Builder withOperationName(OperationNameConstructor operationNameConstructor) { this.operatiOnNameConstructor= operationNameConstructor; return this; } /** * Logs streaming events to client spans. * @return this Builder configured to log streaming events */ public Builder withStreaming() { this.streaming = true; return this; } /** * @param tracedAttributes to set as tags on client spans * created by this intercepter * @return this Builder configured to trace attributes */ public Builder withTracedAttributes(ClientRequestAttribute... tracedAttributes) { this.tracedAttributes = new HashSet( Arrays.asList(tracedAttributes)); return this; } /** * Logs all request life-cycle events to client spans. * @return this Builder configured to be verbose */ public Builder withVerbosity() { this.verbose = true; return this; } /** * @param activeSpanSource that provides a method of getting the * active span before the client call * @return this Builder configured to start client span as children * of the span returned by activeSpanSource.getActiveSpan() */ public Builder withActiveSpanSource(ActiveSpanSource activeSpanSource) { this.activeSpanSource = activeSpanSource; return this; } /** * @return a ClientTracingInterceptor with this Builder's configuration */ public ClientTracingInterceptor build() { return new ClientTracingInterceptor(this.tracer, this.operationNameConstructor, this.streaming, this.verbose, this.tracedAttributes, this.activeSpanSource); } } public enum ClientRequestAttribute { METHOD_TYPE, METHOD_NAME, DEADLINE, COMPRESSOR, AUTHORITY, ALL_CALL_OPTIONS, HEADERS } } 

主要改动点在header那一块在这里插入图片描述

feign注入b3头

@Configuration public class FeignConfig implements RequestInterceptor { @Autowired private final Tracer tracer; @Override public void apply(RequestTemplate requestTemplate) { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if(attributes!=null&&attributes.getRequest()!=null){ HttpServletRequest request = attributes.getRequest(); JaegerSpanContext cOntext=(JaegerSpanContext) tracer.activeSpan().context(); requestTemplate.header("X-B3-TraceId",String.valueOf(context.getTraceId())); requestTemplate.header("X-B3-SpanId", String.valueOf(context.getSpanId())); requestTemplate.header("X-B3-ParentSpanId", String.valueOf(context.getParentId())); requestTemplate.header("X-B3-Sampled", context.isSampled()?"1":"0"); } } @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } } 

经过手动注入后,就可以实现上下游串起来,暂时达到目标,后面的方案是统一传输方式,慢慢升级。


推荐阅读
  • 本文介绍了如何利用Struts1框架构建一个简易的四则运算计算器。通过采用DispatchAction来处理不同类型的计算请求,并使用动态Form来优化开发流程,确保代码的简洁性和可维护性。同时,系统提供了用户友好的错误提示,以增强用户体验。 ... [详细]
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • 利用爬虫技术抓取数据,结合Fiddler与Postman在Chrome中的应用优化提交流程
    本文探讨了如何利用爬虫技术抓取目标网站的数据,并结合Fiddler和Postman工具在Chrome浏览器中的应用,优化数据提交流程。通过详细的抓包分析和模拟提交,有效提升了数据抓取的效率和准确性。此外,文章还介绍了如何使用这些工具进行调试和优化,为开发者提供了实用的操作指南。 ... [详细]
  • 在本文中,我们将为 HelloWorld 项目添加视图组件,以确保控制器返回的视图路径能够正确映射到指定页面。这一步骤将为后续的测试和开发奠定基础。首先,我们将介绍如何配置视图解析器,以便 SpringMVC 能够识别并渲染相应的视图文件。 ... [详细]
  • Python 实战:异步爬虫(协程技术)与分布式爬虫(多进程应用)深入解析
    本文将深入探讨 Python 异步爬虫和分布式爬虫的技术细节,重点介绍协程技术和多进程应用在爬虫开发中的实际应用。通过对比多进程和协程的工作原理,帮助读者理解两者在性能和资源利用上的差异,从而在实际项目中做出更合适的选择。文章还将结合具体案例,展示如何高效地实现异步和分布式爬虫,以提升数据抓取的效率和稳定性。 ... [详细]
  • 在Cisco IOS XR系统中,存在提供服务的服务器和使用这些服务的客户端。本文深入探讨了进程与线程状态转换机制,分析了其在系统性能优化中的关键作用,并提出了改进措施,以提高系统的响应速度和资源利用率。通过详细研究状态转换的各个环节,本文为开发人员和系统管理员提供了实用的指导,旨在提升整体系统效率和稳定性。 ... [详细]
  • ### 优化后的摘要本学习指南旨在帮助读者全面掌握 Bootstrap 前端框架的核心知识点与实战技巧。内容涵盖基础入门、核心功能和高级应用。第一章通过一个简单的“Hello World”示例,介绍 Bootstrap 的基本用法和快速上手方法。第二章深入探讨 Bootstrap 与 JSP 集成的细节,揭示两者结合的优势和应用场景。第三章则进一步讲解 Bootstrap 的高级特性,如响应式设计和组件定制,为开发者提供全方位的技术支持。 ... [详细]
  • 使用 ListView 浏览安卓系统中的回收站文件 ... [详细]
  • 优化后的标题:深入探讨网关安全:将微服务升级为OAuth2资源服务器的最佳实践
    本文深入探讨了如何将微服务升级为OAuth2资源服务器,以订单服务为例,详细介绍了在POM文件中添加 `spring-cloud-starter-oauth2` 依赖,并配置Spring Security以实现对微服务的保护。通过这一过程,不仅增强了系统的安全性,还提高了资源访问的可控性和灵活性。文章还讨论了最佳实践,包括如何配置OAuth2客户端和资源服务器,以及如何处理常见的安全问题和错误。 ... [详细]
  • 深入探索HTTP协议的学习与实践
    在初次访问某个网站时,由于本地没有缓存,服务器会返回一个200状态码的响应,并在响应头中设置Etag和Last-Modified等缓存控制字段。这些字段用于后续请求时验证资源是否已更新,从而提高页面加载速度和减少带宽消耗。本文将深入探讨HTTP缓存机制及其在实际应用中的优化策略,帮助读者更好地理解和运用HTTP协议。 ... [详细]
  • 本文详细介绍了一种利用 ESP8266 01S 模块构建 Web 服务器的成功实践方案。通过具体的代码示例和详细的步骤说明,帮助读者快速掌握该模块的使用方法。在疫情期间,作者重新审视并研究了这一未被充分利用的模块,最终成功实现了 Web 服务器的功能。本文不仅提供了完整的代码实现,还涵盖了调试过程中遇到的常见问题及其解决方法,为初学者提供了宝贵的参考。 ... [详细]
  • 本指南介绍了 `requests` 库的基本使用方法,详细解释了其七个主要函数。其中,`requests.request()` 是构建请求的基础方法,支持其他高级功能的实现。此外,我们还重点介绍了如何使用 `requests.get()` 方法来获取 HTML 网页内容,这是进行网页数据抓取和解析的重要步骤。通过这些基础方法,读者可以轻松上手并掌握网页数据抓取的核心技巧。 ... [详细]
  • 深入解析 Android 中 EditText 的 getLayoutParams 方法及其代码应用实例 ... [详细]
  • 技术日志:使用 Ruby 爬虫抓取拉勾网职位数据并生成词云分析报告
    技术日志:使用 Ruby 爬虫抓取拉勾网职位数据并生成词云分析报告 ... [详细]
  • 本文深入探讨了Ajax的工作机制及其在现代Web开发中的应用。Ajax作为一种异步通信技术,改变了传统的客户端与服务器直接交互的模式。通过引入Ajax,客户端与服务器之间的通信变得更加高效和灵活。文章详细分析了Ajax的核心原理,包括XMLHttpRequest对象的使用、数据传输格式(如JSON和XML)以及事件处理机制。此外,还介绍了Ajax在提升用户体验、实现动态页面更新等方面的具体应用,并讨论了其在当前Web开发中的重要性和未来发展趋势。 ... [详细]
author-avatar
三封酒可_894
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有