本文共 10699 字,大约阅读时间需要 35 分钟。
zipkin 的Span有两个版本V1及V2,但是最终再代码的运转亦或是ES中存储的所体现的其实都是V2的Span,下面我们来分析分析这两个Span有什么异同。
V1的Span 应该是我们所熟悉的,它就是来源与谷歌的那篇论文,拥有CR,CS,SR,SS等Annotation,同时还拥有BinaryAnnotation。我们来看看其主要成员。
public final long traceIdHigh; public final long traceId; public final String name; @Nullable public final Long parentId; @Nullable public final Long timestamp; @Nullable public final Long duration; public final Listannotations; public final List binaryAnnotations; @Nullable public final Boolean debug;
来看一个存储的Span
两个SPANTrace e8d779e43243cd5b[ { "traceId": "e8d779e43243cd5b", "id": "e8d779e43243cd5b", "name": "ttt", "timestamp": 1553414640395000, "duration": 96581, "binaryAnnotations": [ { "key": "chane", "value": "1234", "endpoint": { "serviceName": "shopservicename", "ipv4": "192.168.88.103" } }, { "key": "lc", "value": "tttttt", "endpoint": { "serviceName": "shopservicename", "ipv4": "192.168.88.103" } } ] }, { "traceId": "e8d779e43243cd5b", "id": "ac75f268aab05339", "name": "queryflowprcpln", "parentId": "e8d779e43243cd5b", "timestamp": 1553414640399000, "duration": 94000, "annotations": [ { "timestamp": 1553414640399000, "value": "cs", "endpoint": { "serviceName": "shopservicename", "ipv4": "192.168.88.103" } }, { "timestamp": 1553414640493000, "value": "cr", "endpoint": { "serviceName": "shopservicename", "ipv4": "192.168.88.103" } } ] }]
我们再来看看V2 的Span
// Custom impl to reduce GC churn and Kryo which cannot handle AutoValue subclass // See https://github.com/openzipkin/zipkin/issues/1879 final String traceId, parentId, id; final Kind kind; final String name; final long timestamp, duration; // zero means null, saving 2 object references final Endpoint localEndpoint, remoteEndpoint; final Listannotations; final Map tags; final int flags; // bit field for timestamp and duration, saving 2 object references
实际存储
{ "took": 3, "timed_out": false, "_shards": { "total": 20, "successful": 20, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 0, "hits": [ { "_index": "zipkin:span-2019-03-24", "_type": "span", "_id": "xHu6rmkB4ZW4b3UBVS-t", "_score": 0, "_source": { "traceId": "e8d779e43243cd5b", "duration": 94000, "localEndpoint": { "serviceName": "shopservicename", "ipv4": "192.168.88.103" }, "timestamp_millis": 1553414640399, "kind": "CLIENT", "name": "queryflowprcpln", "id": "ac75f268aab05339", "parentId": "e8d779e43243cd5b", "timestamp": 1553414640399000 } }, { "_index": "zipkin:span-2019-03-24", "_type": "span", "_id": "xXu6rmkB4ZW4b3UBVS-t", "_score": 0, "_source": { "traceId": "e8d779e43243cd5b", "duration": 96581, "localEndpoint": { "serviceName": "shopservicename", "ipv4": "192.168.88.103" }, "timestamp_millis": 1553414640395, "name": "ttt", "id": "e8d779e43243cd5b", "timestamp": 1553414640395000, "tags": { "chane": "1234", "lc": "tttttt" } } } ] }}
两者有比较大的不一样。当zipkin Server 接收到V1 时会有一个转换器将其转换成V2。那这两者是怎么转换的呢。
CR 及 CS 的Annoation 会转换成Kind 为Client。其次CR 及CS 下的EndPoint 会被转换为localEndpoint。
至于我们经常用来存储key-value 的BinaryAnnoation 也会被转换成tags中的一条Entry<String,String>。
zipkin 采用的ES来进行存储,我们来看看其在ES中的索引是怎么定义的。
{ "zipkin:span-2019-03-24": { "aliases": {}, "mappings": { "span": { "_source": { "excludes": [ "_q" ] }, "dynamic_templates": [ { "strings": { "match": "*", "match_mapping_type": "string", "mapping": { "ignore_above": 256, "norms": false, "type": "keyword" } } } ], "properties": { "_q": { "type": "keyword" }, "annotations": { "type": "object", "enabled": false }, "duration": { "type": "long" }, "id": { "type": "keyword", "ignore_above": 256 }, "kind": { "type": "keyword", "ignore_above": 256 }, "localEndpoint": { "dynamic": "false", "properties": { "serviceName": { "type": "keyword" } } }, "name": { "type": "keyword" }, "parentId": { "type": "keyword", "ignore_above": 256 }, "remoteEndpoint": { "dynamic": "false", "properties": { "serviceName": { "type": "keyword" } } }, "tags": { "type": "object", "enabled": false }, "timestamp": { "type": "long" }, "timestamp_millis": { "type": "date", "format": "epoch_millis" }, "traceId": { "type": "keyword" } } }, "_default_": { "dynamic_templates": [ { "strings": { "match": "*", "match_mapping_type": "string", "mapping": { "ignore_above": 256, "norms": false, "type": "keyword" } } } ] } }, "settings": { "index": { "number_of_shards": "5", "provided_name": "zipkin:span-2019-03-24", "mapper": { "dynamic": "false" }, "creation_date": "1553400823203", "requests": { "cache": { "enable": "true" } }, "analysis": { "filter": { "traceId_filter": { "type": "pattern_capture", "preserve_original": "true", "patterns": [ "([0-9a-f]{1,16})$" ] } }, "analyzer": { "traceId_analyzer": { "filter": "traceId_filter", "type": "custom", "tokenizer": "keyword" } } }, "number_of_replicas": "1", "uuid": "eRkq_bCyTuuPByMREL4M_w", "version": { "created": "6020399" } } } }}
有一个需要主要的是zipkin 会将annoation 及tags。一起写如es的一个字段“_q”,方便查询
*Ex {@code curl -s localhost:9200/zipkin:span-2017-08-11/_search?q=_q:error=500} */ static byte[] prefixWithTimestampMillisAndQuery(Span span, long timestampMillis) { Buffer query = new Buffer(); JsonWriter writer = JsonWriter.of(query); try { writer.beginObject(); if (timestampMillis != 0L) writer.name("timestamp_millis").value(timestampMillis); if (!span.tags().isEmpty() || !span.annotations().isEmpty()) { writer.name("_q"); writer.beginArray(); for (Annotation a : span.annotations()) { if (a.value().length() > 255) continue; writer.value(a.value()); } for (Map.Entry
tag : span.tags().entrySet()) { if (tag.getKey().length() + tag.getValue().length() + 1 > 255) continue; writer.value(tag.getKey()); // search is possible by key alone writer.value(tag.getKey() + "=" + tag.getValue()); } writer.endArray(); } writer.endObject(); } catch (IOException e) { // very unexpected to have an IOE for an in-memory write assert false : "Error indexing query for span: " + span; if (LOG.isLoggable(Level.FINE)) { LOG.log(Level.FINE, "Error indexing query for span: " + span, e); } return SpanBytesEncoder.JSON_V2.encode(span); } byte[] document = SpanBytesEncoder.JSON_V2.encode(span); if (query.rangeEquals(0L, ByteString.of(new byte[] {'{', '}'}))) { return document; } byte[] prefix = query.readByteArray(); byte[] newSpanBytes = new byte[prefix.length + document.length - 1]; int pos = 0; System.arraycopy(prefix, 0, newSpanBytes, pos, prefix.length); pos += prefix.length; newSpanBytes[pos - 1] = ','; // starting at position 1 discards the old head of '{' System.arraycopy(document, 1, newSpanBytes, pos, document.length - 1); return newSpanBytes; }
由于tags。是不可搜索的,所以针对tags.我们可以利用_q进行搜索。针对文中的tag。可以这么搜索
"term": {
"_q": "chane" }"term": {
"_q": "chane=1234" }转载地址:http://yhagj.baihongyu.com/