Istio最佳实践系列:如何实现方法级调用跟踪? (2)

FollowsFrom:如果Parent Span并不依赖Child Span的执行结果,则可以用FollowsFrom表示。例如网上商店购物付款后会向用户发一个邮件通知,但无论邮件通知是否发送成功,都不影响付款成功的状态,这种情况则适用于用FollowsFrom表示。

跨进程调用信息传播

SpanContext是OpenTracing中一个让人比较迷惑的概念。在OpenTracing的概念模型中提到SpanContext用于跨进程边界传递分布式调用的上下文。但实际上OpenTracing只定义一个SpanContext的抽象接口,该接口封装了分布式调用中一个Span的相关上下文内容,包括该Span所属的Trace id,Span id以及其它需要传递到downstream服务的信息。SpanContext自身并不能实现跨进程的上下文传递,需要由Tracer(Tracer是一个遵循OpenTracing协议的实现,如Jaeger,Skywalking的Tracer)将SpanContext序列化后通过Wire Protocol传递到下一个进程中,然后在下一个进程将SpanContext反序列化,得到相关的上下文信息,以用于生成Child Span。

为了为各种具体实现提供最大的灵活性,OpenTracing只是提出了跨进程传递SpanContext的要求,并未规定将SpanContext进行序列化并在网络中传递的具体实现方式。各个不同的Tracer可以根据自己的情况使用不同的Wire Protocol来传递SpanContext。

在基于HTTP协议的分布式调用中,通常会使用HTTP Header来传递SpanContext的内容。常见的Wire Protocol包含Zipkin使用的b3 HTTP header,Jaeger使用的,LightStep使用的"x-ot-span-context" HTTP Header等。Istio/Envoy支持b3 header和x-ot-span-context header,可以和Zipkin,Jaeger及LightStep对接。其中b3 HTTP header的示例如下:

X-B3-TraceId: 80f198ee56343ba864fe8b2a57d3eff7 X-B3-ParentSpanId: 05e3ac9a4f6e3b90 X-B3-SpanId: e457b5a2e4d86bd1 X-B3-Sampled: 1 Istio对分布式调用跟踪的支持

Istio/Envoy为微服务提供了开箱即用的分布式调用跟踪功能。在安装了Istio和Envoy的微服务系统中,Envoy会拦截服务的入向和出向请求,为微服务的每个调用请求自动生成调用跟踪数据。通过在服务网格中接入一个分布式跟踪的后端系统,例如zipkin或者Jaeger,就可以查看一个分布式请求的详细内容,例如该请求经过了哪些服务,调用了哪个REST接口,每个REST接口所花费的时间等。

需要注意的是,Istio/Envoy虽然在此过程中完成了大部分工作,但还是要求对应用代码进行少量修改:应用代码中需要将收到的上游HTTP请求中的b3 header拷贝到其向下游发起的HTTP请求的header中,以将调用跟踪上下文传递到下游服务。这部分代码不能由Envoy代劳,原因是Envoy并不清楚其代理的服务中的业务逻辑,无法将入向请求和出向请求按照业务逻辑进行关联。这部分代码量虽然不大,但需要对每一处发起HTTP请求的代码都进行修改,非常繁琐而且容易遗漏。当然,可以将发起HTTP请求的代码封装为一个代码库来供业务模块使用,来简化该工作。

下面以一个简单的网上商店示例程序来展示Istio如何提供分布式调用跟踪。该示例程序由eshop,inventory,billing,delivery几个微服务组成,结构如下图所示:

Istio最佳实践系列:如何实现方法级调用跟踪?


eshop微服务接收来自客户端的请求,然后调用inventory,billing,delivery这几个后端微服务的REST接口来实现用户购买商品的checkout业务逻辑。本例的代码可以从github下载:https://github.com/aeraki-framework/method-level-tracing-with-istio

如下面的代码所示,我们需要在eshop微服务的应用代码中传递b3 HTTP Header。

@RequestMapping(value = "/checkout") public String checkout(@RequestHeader HttpHeaders headers) { String result = ""; // Use HTTP GET in this demo. In a real world use case,We should use HTTP POST // instead. // The three services are bundled in one jar for simplicity. To make it work, // define three services in Kubernets. result += restTemplate.exchange("http://inventory:8080/createOrder", HttpMethod.GET, new HttpEntity<>(passTracingHeader(headers)), String.class).getBody(); result += "<BR>"; result += restTemplate.exchange("http://billing:8080/payment", HttpMethod.GET, new HttpEntity<>(passTracingHeader(headers)), String.class).getBody(); result += "<BR>"; result += restTemplate.exchange("http://delivery:8080/arrangeDelivery", HttpMethod.GET, new HttpEntity<>(passTracingHeader(headers)), String.class).getBody(); return result; } private HttpHeaders passTracingHeader(HttpHeaders headers) { HttpHeaders tracingHeaders = new HttpHeaders(); extractHeader(headers, tracingHeaders, "x-request-id"); extractHeader(headers, tracingHeaders, "x-b3-traceid"); extractHeader(headers, tracingHeaders, "x-b3-spanid"); extractHeader(headers, tracingHeaders, "x-b3-parentspanid"); extractHeader(headers, tracingHeaders, "x-b3-sampled"); extractHeader(headers, tracingHeaders, "x-b3-flags"); extractHeader(headers, tracingHeaders, "x-ot-span-context"); return tracingHeaders; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjgxd.html