ServiceComb 是Apache开源的微服务架构,在微服务框架中,服务通过网络进行通信,我们必须处理所有与网络相关的问题,例如延迟,超时和分区。随着部署的服务越来越多,我们需要系统监控微服务网络延迟和请求流。
Zipkin是一种分布式跟踪系统。它有助于收集解决微服务架构中的延迟问题所需的时序数据。它管理这些数据的收集和查找。Zipkin的设计基于Google Dapper论文。参见Zipkin官网1
ServiceComb与Zipkin集成以提供自动跟踪,以便用户可以专注于满足他们的业务需求。
ServiceComb 提供了处理链机制,通过扩展handler处理链接口,已经实现负载均衡、熔断容错、流量控制等能力。同样,实现调用链追踪能力,也可以通过扩展处理链接口实现调用链追踪处理链。了解ServiceComb处理链参考 https://docs.servicecomb.io/java-chassis/zh_CN/references-handlers/intruduction.html
ServiceComb 扩展handler处理链接口,编写了handler-tracing-zipkin 模块,用于支持追踪基于ServiceComb java chassis 的微服务。Handler-tracing-zipkin 模块在java-chassis/handler处理链下,模块内容如下。那么handler-tracing-zipkin 模块究竟做了什么去支持zipkin调用链追踪?
事实上,zipkin服务端提供了数据收集、存储、API查询检索、Web UI等服务,我们只需要向zipkin服务上传调用链追踪数据,即可在zipkin提供的Web页面查看服务调用链追踪信息。如此一来,我们只需要使用zipkin的brave组件跟踪服务调用链并记录追踪数据,再使用reporter组件上传追踪数据到zipkin服务即可。
按照这个思路,ServiceComb只需要在后台启动zipkin服务,使用上面的Handler-tracing-zipkin模块实现追踪调用链记录数据和上传追踪数据到zipkin服务,即可支持zipkin调用链追踪。
ServiceComb项目如何使用zipkin实施调用链追踪,参考Distributed Tracing with ServiceComb and Zipkin2
上面提到我们只需要追踪调用链、记录数据、上传追踪数据到zipkin服务即可支持zipkin调用链追踪。下面我们从编码角度,去介绍如何具体实现它。
追踪调用链首先需要在程序中初始化配置zipkin追踪组件(zipkin提供了一系列用于调用链柱子的组件),然后适配追踪组件到调用链请求上(zipkin在调用链请求和追踪组件之间提供了一系列适配接口和类)。调用链追踪组件设置完毕,我们只需要定义好一系列追踪方法和调用这些方法即可。上面的过程可以归结为以下4步:
按照上面4步,我们实现了handler-tracing-zipkin 模块。但由于消费端和服务端对于调用链请求的适配追踪处理方法不同,handler-tracing-zipkin 模块分别实现了消费端和服务端追踪。下面我们从handler-tracing-zipkin 模块UML类图,去解读模块内类关系结构。
从handler-tracing-zipkin模块UML类图可以看到:
<config>
<handler id="tracing-consumer"
class="org.apache.servicecomb.tracing.zipkin.ZipkinConsumerTracingHandler"/>
<handler id="tracing-provider"
class="org.apache.servicecomb.tracing.zipkin.ZipkinProviderTracingHandler"/>
</config>
下面按照分布式调用链追踪步骤,深入代码解读handler-tracing-zipkin模块内类
初始化配置追踪组件:TracingConfiguration 类
class TracingConfiguration {
private String apiVersion = CONFIG_TRACING_COLLECTOR_API_V2;
//定义sender方法
@Bean
Sender sender(DynamicProperties dynamicProperties) {
apiVersion = dynamicProperties.getStringProperty(CONFIG_TRACING_COLLECTOR_API_VERSION,
CONFIG_TRACING_COLLECTOR_API_V2).toLowerCase();
// use default value if the user set value is invalid
if (apiVersion.compareTo(CONFIG_TRACING_COLLECTOR_API_V1) != 0) {
apiVersion = CONFIG_TRACING_COLLECTOR_API_V2;
}
String path = MessageFormat.format(CONFIG_TRACING_COLLECTOR_PATH, apiVersion);
return OkHttpSender.create(
dynamicProperties.getStringProperty(
CONFIG_TRACING_COLLECTOR_ADDRESS,
DEFAULT_TRACING_COLLECTOR_ADDRESS)
.trim()
.replaceAll("/+$", "")
.concat(path));
}
//初始化reporter组件
@Bean
Reporter<Span> zipkinReporter(Sender sender) {
if (apiVersion.compareTo(CONFIG_TRACING_COLLECTOR_API_V1) == 0) {
return AsyncReporter.builder(sender).build(SpanBytesEncoder.JSON_V1);
}
return AsyncReporter.builder(sender).build();
}
//初始化tracing追踪组件
@Bean
Tracing tracing(Reporter<Span> reporter, DynamicProperties dynamicProperties,
CurrentTraceContext currentTraceContext) {
return Tracing.newBuilder()
.localServiceName(dynamicProperties.getStringProperty(CONFIG_QUALIFIED_MICROSERVICE_NAME_KEY,
DEFAULT_MICROSERVICE_NAME))
.currentTraceContext(currentTraceContext) // puts trace IDs into logs
.spanReporter(reporter)
.build();
}
//定义currentTraceContext方法,获取追踪信息
@Bean
CurrentTraceContext currentTraceContext() {
return ThreadLocalCurrentTraceContext.newBuilder().addScopeDecorator(MDCScopeDecorator.create()).build();
}
//配置httpTracing
@Bean
HttpTracing httpTracing(Tracing tracing) {
return HttpTracing.create(tracing);
}
}
TracingConfiguration初始化配置类,首先初始化了Tracing,然后配置了httpTracing,使用@bean生成httpTracing方法的bean对象加载到spring管理的容器中。httpTracing包含对http追踪组件的引用,可用于创建http追踪处理器。初始化配置httpTracing主要包括配置追踪服务名字、定义 currentTraceContext 方法(用于获取追踪信息)、配置reporter方法以及sender发送组件(用于发送追踪数据到zipkin)。
ConsumerInvocationAdapter 类和 ProviderInvocationAdapter 类
消费端追踪适配调用链:ConsumerInvocationAdapter 类
//ConsumerInvocationAdapter继承了brave.http.HttpClientAdapter客户端调用链适配类
class ConsumerInvocationAdapter extends HttpClientAdapter<Invocation, Response> {
// 获取调用链请求的方法
@Nullable
@Override
public String method(@Nonnull Invocation invocation) {
return invocation.getOperationMeta().getHttpMethod();
}
// 获取调用链请求的url
@Nullable
@Override
public String url(@Nonnull Invocation invocation) {
return invocation.getEndpoint().getEndpoint();
}
// 获取调用链请求的path
@Nullable
@Override
public String path(@Nonnull Invocation request) {
return request.getOperationMeta().getOperationPath();
}
// 获取调用链请求头
@Nullable
@Override
public String requestHeader(@Nonnull Invocation invocation, @Nonnull String key) {
return invocation.getContext().get(key);
}
//获取调用链请求响应状态
@Nullable
@Override
public Integer statusCode(@Nonnull Response response) {
return response.getStatusCode();
}
}
ConsumerInbvocationAdapter类,继承了zipkin-brave组件的HttpClientAdapter客户端适配类,主要用于追踪组件适配消费端调用链,定义了一系列方法,用于获取调用链请求的method、url、path、requestHeader、statusCode。获取的信息有助于记录消费端追踪数据Span,比如说Span的name一般设置为请求的method(GET或POST),Span会记录请求的 url 和 path。追踪消费端调用链请求需要绑定请求的requestHeader,结束追踪需要根据请求的statusCode。
服务端追踪适配调用链:ProviderInvocationAdapter 类
// ProviderInvocationAdapter 继承了brave.http.HttpServerAdapter服务端调用链适配类
class ProviderInvocationAdapter extends HttpServerAdapter<Invocation, Response> {
@Nullable
@Override
public String method(@Nonnull Invocation invocation) {
return invocation.getOperationMeta().getHttpMethod();
}
@Nullable
@Override
public String url(@Nonnull Invocation invocation) {
return invocation.getEndpoint().getEndpoint();
}
@Nullable
@Override
public String path(@Nonnull Invocation request) {
return request.getOperationMeta().getOperationPath();
}
@Nullable
@Override
public String requestHeader(@Nonnull Invocation invocation, @Nonnull String key) {
return invocation.getContext().get(key);
}
@Nullable
@Override
public Integer statusCode(@Nonnull Response response) {
return response.getStatusCode();
}
}
ProviderInvocationAdapter类,继承了zipkin-brave组件的HttpProviderAdapter服务端适配类,主要用于追踪组件适配服务端调用链,定义了一系列方法去获取http请求的method、url、path,、requestHeader、statusCode。获取信息有助于记录服务端追踪数据Span,比如说Span的name一般设置为请求的method(GET或POST),Span会记录请求的 url 和 path。追踪服务端调用链请求需要获取请求的requestHeader内容,结束追踪需要根据请求的statusCode。
ZipkinConsumerDelegate 类和 ZipkinProviderDelegate 类
消费端追踪方法定义:ZipkinConsumerDelegate 类
class ZipkinConsumerDelegate implements ZipkinTracingDelegate {
private final HttpClientHandler<Invocation, Response> handler;
private final HttpTracing httpTracing;
private final Injector<Invocation> injector;
@SuppressWarnings("unchecked")
ZipkinConsumerDelegate(HttpTracing httpTracing) {
this.httpTracing = httpTracing;
this.injector = httpTracing.tracing().propagation().injector(injector());
this.handler = HttpClientHandler.create(httpTracing, new ConsumerInvocationAdapter());
}
// 追踪消费端发送的调用链请求,创建Span
@Override
public Span createSpan(Invocation invocation) {
return handler.handleSend(injector, invocation);
}
// 收到响应,上报Span到zipkin
@Override
public void onResponse(Span span, Response response, Throwable error) {
handler.handleReceive(response, error, span);
}
// 定义消费端追踪名字为Zipkin consumer
@Override
public String name() {
return "Zipkin consumer";
}
// 获取httpTracing的Tracing组件
@Override
public Tracing tracer() {
return httpTracing.tracing();
}
// 在调用链请求中设置值
private Setter<Invocation, String> injector() {
return (invocation, key, value) -> invocation.getContext().put(key, value);
}
}
ZipkinConsumerDelegate类,从消费端的调用链追踪实现了ZipkinTracingDelegate接口定义的追踪方法。首先创建了追踪处理器handler和注射器injector。然后定义了createSpan和onResponse方法,使用处理器向消费端发送的调用链请求中注入追踪ID、创建Span记录追踪数据,收到响应,发送追踪数据到zipkin。
this.handler = HttpClientHandler.create(httpTracing, new ConsumerInvocationAdapter())
: 依赖httpTracing追踪组件 和 ConsumerInvocationAdapter 适配器,创建了handler追踪处理器。
this.injector = httpTracing.tracing().propagation().injector(injector())
: 初始化定义了injector,用于将追踪器信息到设置在调用链请求中。
createSpan(Invocation)
:调用handler处理器的handlerSend方法,使用injector绑定追踪器到http请求,创建Span,开始记录追踪数据。
onResponse(Span,Response,Throwable)
:调用handler处理器的handlerReceive方法,接收到服务端响应后,完成Span,触发reporter组件上报Span数据到zipkin。
服务端追踪方法定义:ZipkinProviderDelegate 类
class ZipkinProviderDelegate implements ZipkinTracingDelegate {
private static final Logger LOG = LoggerFactory.getLogger(ZipkinProviderDelegate.class);
private final HttpServerHandler<Invocation, Response> handler;
private final HttpTracing httpTracing;
private final Extractor<Invocation> extractor;
public static final String SPAN_ID_HEADER_NAME = "X-B3-SpanId";
public static final String TRACE_ID_HEADER_NAME = Const.TRACE_ID_NAME;
//在调用链请求中获取值
private static final Getter<Invocation, String> INVOCATION_STRING_GETTER = (invocation, key) -> {
String extracted = invocation.getContext().get(key);
if (StringUtils.isEmpty(extracted) && SPAN_ID_HEADER_NAME.equals(key)) {
// use traceId as spanId to avoid brave's recreating traceId
extracted = invocation.getContext().get(TRACE_ID_HEADER_NAME);
LOG.debug("try to extract X-B3-SpanId, but the value is empty, replace with TraceId = [{}]", extracted);
}
return extracted;
};
@SuppressWarnings("unchecked")
ZipkinProviderDelegate(HttpTracing httpTracing) {
this.httpTracing = httpTracing;
this.extractor = httpTracing.tracing().propagation().extractor(extractor());
this.handler = HttpServerHandler.create(httpTracing, new ProviderInvocationAdapter());
}
// 获取httpTracing的Tracing组件
@Override
public Tracing tracer() {
return httpTracing.tracing();
}
//追踪收到的调用链请求,创建Span
@Override
public Span createSpan(Invocation invocation) {
return handler.handleReceive(extractor, invocation);
}
// 发出响应,上报Span到zipkin服务
@Override
public void onResponse(Span span, Response response, Throwable error) {
handler.handleSend(response, error, span);
}
// 定义服务端追踪名字为Zipkin provider
@Override
public String name() {
return "Zipkin provider";
}
private Getter<Invocation, String> extractor() {
return INVOCATION_STRING_GETTER;
}
}
ZipkinProviderDelegate类,从服务端的调用链追踪实现了ZipkinTracingDelegate接口定义的追踪方法。首先创建了追踪处理器handler和获取器extractor,然后定义了createSpan和onResponse方法,使用处理器从服务端接收的调用链请求中获取追踪ID、创建Span记录追踪数据、处理完成发送响应,发送追踪数据到zipkin。
this.handler = HttpServerHandler.create(httpTracing, new ProviderInvocationAdapter())
: 依赖httpTracing追踪组件 和 ProviderInvocationAdapter适配器 创建了handler追踪处理器。
this.extractor = httpTracing.tracing().propagation().extractor(extractor())
: 初始化定义了extractor,用于从调用链请求中获取追踪信息。
createSpan(Invocation)
:调用handler处理器的handleReceive方法,使用extractor从服务端收到的http请求中获取追踪内容,创建Span,开始记录追踪数据。
onResponse(Span,Response,Throwable)
:调用handler处理器的handleSend方法,处理完成发送响应后,完成Span,触发reporter组件上报Span数据到zipkin。
追踪处理:ZipkinTracingHandler 类
// ZipkinTracingHandler 实现了 org.apache.servicecomb.core.Handler.handler 处理链接口
class ZipkinTracingHandler implements Handler {
private static final Logger LOGGER = LoggerFactory.getLogger(ZipkinTracingHandler.class);
private final Tracing tracer;
private final ZipkinTracingDelegate tracingDelegate;
ZipkinTracingHandler(ZipkinTracingDelegate tracingDelegate) {
this.tracer = tracingDelegate.tracer();
this.tracingDelegate = tracingDelegate;
}
//实现zipkin调用链追踪的处理函数
@SuppressWarnings({"try", "unused"})
@Override
public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception {
Span span = tracingDelegate.createSpan(invocation);
try (SpanInScope scope = tracer.tracer().withSpanInScope(span)) {
LOGGER.debug("{}: Generated tracing span for {}",
tracingDelegate.name(),
invocation.getOperationName());
invocation.next(onResponse(invocation, asyncResp, span));
} catch (Exception e) {
LOGGER.debug("{}: Failed invocation on {}",
tracingDelegate.name(),
invocation.getOperationName(),
e);
tracingDelegate.onResponse(span, null, e);
throw e;
}
}
//调用链请求响应,上报Span数据
private AsyncResponse onResponse(Invocation invocation, AsyncResponse asyncResp, Span span) {
return response -> {
Throwable error = response.isFailed() ? response.getResult() : null;
tracingDelegate.onResponse(span, response, error);
LOGGER.debug("{}: Completed invocation on {}",
tracingDelegate.name(),
invocation.getOperationName(),
error);
asyncResp.handle(response);
};
}
}
ZipkinTracingHandler类实现了ServiceComb的handler处理链接口,调用追踪方法处理调用链请求。handler处理链是ServiceComb的核心部分,具体可参见java-chassiss使用手册——调用参考3。ZipkinTracingHandler 调用了ZipkinConsumerDelegate 的createSpan方法追踪消费端调用链,创建Span, 记录追踪数据;调用了onResponse方法接收到响应后,完成追踪调用链,并把调用链追踪情况反馈到日志中。
ZipkinConsumerTracingHandler 类 和 ZipkinProviderTracingHandler 类
消费端追踪处理:ZipkinConsumerTracingHandler 类
public class ZipkinConsumerTracingHandler extends ZipkinTracingHandler {
public ZipkinConsumerTracingHandler() {
this(new ZipkinConsumerDelegate(BeanUtils.getContext().getBean(HttpTracing.class)));
}
private ZipkinConsumerTracingHandler(ZipkinTracingDelegate tracingHandler) {
super(tracingHandler);
}
}
服务端追踪处理:ZipkinProviderTracingHandler 类
public class ZipkinProviderTracingHandler extends ZipkinTracingHandler {
public ZipkinProviderTracingHandler() {
this(new ZipkinProviderDelegate(BeanUtils.getContext().getBean(HttpTracing.class)));
}
private ZipkinProviderTracingHandler(ZipkinProviderDelegate tracingHandler) {
super(tracingHandler);
}
}
ZipkinConsumerTracingHandler 类 和 ZipkinProviderTracingHandler 类 继承了ZipkinTracingHandler 追踪处理类。从spring的容器中获取httpTracing的bean对象(TracingConfiguration 中配置),分别创建 ZipkinConsumerDelegate 和 ZipkinProviderDelegate 对象,用于ZipkinTracingHandler 追踪处理调用链请求。
至此,ServiceComb如何一步一步去实现zipkin分布式调用链追踪,已经解读完毕。
zipkin ↩︎
Distributed Tracing with ServiceComb and Zipkin ↩︎ ↩︎
java-chassiss使用手册——调用参考 ↩︎