zipkin Span 分析

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 List<Annotation> annotations;
  
  public final List<BinaryAnnotation> binaryAnnotations;

  @Nullable
  public final Boolean debug;

來看一個存儲的Span

兩個SPAN
Trace 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 List<Annotation> annotations;
  final Map<String, String> 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”,方便查詢

* <p>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<String, String> 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"
              }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章