OpenTracing 語義說明

最近在為 TiDB 加一個 tracing 的工具。 雖然 TiDB 已經(jīng)開始使用 OpenTracing 工具了,但是還遠遠不夠。沒有做到全鏈路追蹤,沒法知道某個具體的 query 在那些節(jié)點慢。 在研究過程中,需要熟練掌握 OpenTracing 的概念,也就是這篇 semantic specification。 在這篇文章里規(guī)定了 不同語言間 OpenTracing 需要實現(xiàn)的函數(shù),類型。

因為 OpenTracing 是跨語言的,按照標準實現(xiàn)的時候,需要盡可能的根據(jù)通用的語言概念,而不能局限于某一個具體的語言特性。這也解釋了這篇文檔的必要性。

The OpenTracing 數(shù)據(jù)模型

首先需要闡明的是 Span 和 trace 的概念。 用圖論的觀點來看的話,traces 可以被認為是 spans 的 DAG。也就是說,對個 spans 形成的 DAG 是一個 Traces。

舉例來說,下圖是一個由八個 Spans 形成的一個 Trace。

單個 Trace 中 Span 之間的因果關(guān)系


        [Span A]  ←←←(the root span)
            |
     +------+------+
     |             |
 [Span B]      [Span C] ←←←(Span C is a `ChildOf` Span A)
     |             |
 [Span D]      +---+-------+
               |           |
           [Span E]    [Span F] >>> [Span G] >>> [Span H]
                                       ↑
                                       ↑
                                       ↑
                         (Span G `FollowsFrom` Span F)

某些時候, 用時間順序來具象化更讓人理解。下面就是一個例子。

單個 Trace 中 Spans 之間的時間關(guān)系

––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–> time

 [Span A···················································]
   [Span B··············································]
      [Span D··········································]
    [Span C········································]
         [Span E·······]        [Span F··] [Span G··] [Span H··]

每個 Span 包含一些狀態(tài):

  • Operation 的 名字(An operation name)
  • 開始 ts (A start timestamp)
  • 結(jié)束 ts (A finish timestamp)
  • 0個或多個以 keys:values 為形式組成的 Span Tags。 key 必須是 string, values 則可以是 strings, bool,numeric types
  • 0個或多個 Span logs
  • 一個 SpanContext
  • 通過 SpanContext 可以指向 0個 或者多個 因果相關(guān)的 Span。

每個 SpanContext 包含以下狀態(tài):

  • 任何 OpenTraceing 實現(xiàn)相關(guān)的狀態(tài)(比如 trace 和 span id)都需要被一個跨進程的 Span 所聯(lián)系。
  • ?Baggage Items: 跨進程的 key value 對。

References between Spans

一個 Span 可能, 因因果相關(guān),指向0個或者多個其他的 SpanContexts。 目前來說, OpenTracing 僅僅定義了兩種關(guān)系: ChildOfFollowsForm。如同字面上可以猜測到的, ChildOf 將成為當前 Span 的 child 而 FollowsFrom則會成為 parent。 這兩種關(guān)系為 child spanparent span 建立了直接因果關(guān)系。

ChildOf 引用: 某個 Span 可以是 ChildOfparent Span。在一個 ChildOf 的引用中, parent Span, 在某種程度上取決于child Span。 下面列舉能形成 ChildOf 關(guān)系的條件:

  • server 端的 RPC 的 Span 可能是 ChildOf client 端 RPC。
  • SQL insert 的 Span 可能是 ChildOf 一個 ORM save 方法的 Span
  • 許多做并發(fā)處理工作的 Span 可能都獨立的是 ChildOf 一個單獨的 合并這些在截止時間返回工作結(jié)果的 parent Span。
  [-Parent Span---------]
         [-Child Span----]

    [-Parent Span--------------]
         [-Child Span A----]
          [-Child Span B----]
        [-Child Span C----]
         [-Child Span D---------------]
         [-Child Span E----]

FollowsFrom 引用: 一些 parent Spans 并不依賴 child Span 的結(jié)果。如果是這種情況, 那么我們基于因果關(guān)系上說Child Span FollowsFrom parent Span。這里,有很多唯一的 FollowsFrom 引用的子類別。這些會在以后被更加正式的定義。

這些是有效的,基于時間順序的 FollowFrom 引用:

    [-Parent Span-]  [-Child Span-]


    [-Parent Span--]
     [-Child Span-]


    [-Parent Span-]
                [-Child Span-]

OpenTracing API

在 OpenTracing 有著三個關(guān)鍵的并且相互關(guān)聯(lián)的類型: Tracer, Span, SpanContext。下面,我們來介紹下每種類型的基本行為。 簡單地說,每種行為都會在具體的語言中變?yōu)橐粋€“方法”,though it may actually be a set of related sibling methods due to type overloading and so on.

在不同語言中,對于 “Optional” 參數(shù)的理解是不一樣的。 舉例來說, Go 里 我們會使用 “functional Options”,但是 Java 里可能會使用 builder 模式。

Tracer

Tracer interface 創(chuàng)造 Spans 并且理解 如何 Inject (serialize) and Extract (deserialize) them across process boundaries. Formally, it has the following capabilities:

Start a new Span

要求的參數(shù)

  • 一個 大家都能夠理解的 字符串operation name, 并且精確代表了被 Span 做完的工作。 (例如, 一個 RPC 方法名, 一個函數(shù)名, 或者 超大計算任務(wù)中某個子任務(wù)的名字). The operation name 應(yīng)該 可以辨認一個 Span 實例的最為一般的 string. 這是說, "get_user" 是比 "get_user/314159" 更好的.

舉例來說,假設(shè)我們需要獲得賬戶(account)信息, 下面是一些對于一個 Span 可能的 operation names:

Operation Name 指導(dǎo)
get 過于一般
get_account/792 過于具體
get_account 贊, 并且 account_id=792 會是一個很好的 Span tag

可選擇的參數(shù):

  • 零個或者多個 references 到相關(guān)的SpanContexts, 如果可能的話,包括一個對于 ChildOfFollowsFrom reference types 簡略
  • 一個可選的,顯性的 start timestamp; 如果被忽略,那么當前 walltime 會被默認使用
  • 零個或者多個 tags

返回 一個 已經(jīng)開始的 Span 實例 (但是尚未結(jié)束)

Inject a SpanContext into a carrier

要求參數(shù)

  • 一個 SpanContext 實例
  • 一個能告訴 Tracer 如何將 SpanContext 編碼的 格式 描述 (特別的,對于 string 來說,這個不是必須的)
  • 一個被 format 所指定的 carrier。某個Tracer實現(xiàn)根據(jù)這個 formatSpanContext編碼進入這個 carrier 對象
Extract a SpanContext from a carrier

要求參數(shù)

  • 一個 format 描述 (一般但不必要是一個 string 常數(shù))。 目的在于告訴某個 Tracer 實現(xiàn) 如何將SpanContext carrier 中解碼。
  • 一個 carrier。它的類型是由format 支配的。某個 Tracer 實現(xiàn)會依據(jù)這個 formatSpanContext 從這個 carrier 對象解碼。

**返回一個 SpanContext 實例。 當我們想要通過 Tracer 開始一個新的 Span, 這個實例是可以被用來當做一個。

Note: required formats for injection and extraction

injection 和 extraction 都依賴于一個可擴展的 **format 參數(shù)。 這個參數(shù)規(guī)定了關(guān)聯(lián) “carrier” 的類型,同時也描述了一個 SpanContext 是如何被編碼進入這個 carrier 的。 所有下面的 formats 都必須被所有的 Tracer 實現(xiàn)支持:

  • Text Map: 一個任意 string-to-string 的 map。 Keys 和 values 都是不受任何限制的字符 。
  • HTTP Headers: 一個 string-to-string 的 map。Keys 和 values 需要適配 HTTP headers (a la RFC 7230. 實踐上來說,因為 HTTP headers 存在被野蠻使用的現(xiàn)象,這里強烈推薦 Tracer 實現(xiàn)限定 HTTP headers 密鑰空間(Key Space) 的使用并且保守的將相應(yīng)的值轉(zhuǎn)義。
  • Binary: 一個 (單獨) 任意的 表示 一個 SpanContext binary blob。

Span

除去 將 Span's SpanContext 取回的函數(shù),下面任何一個函數(shù)都被不會在 Span 結(jié)束后被調(diào)用。

Retrieve the Spans SpanContext

沒有任何參數(shù)。

返回 特定 SpanContext,對于給定的 Span. 返回值可能在 Span結(jié)束后仍被使用。

Overwrite the operation name

要求參數(shù)。

  • 新的 operation name。它將取代在這 Span 開始時傳入的 operation name
Finish the Span

可選參數(shù)

  • Spanfinish timestamp;如果沒設(shè)定,那么當前的 walltime 就會被使用。

除去 將 Span's SpanContext 取回的函數(shù),下面任何一個函數(shù)都被不會在 Span 結(jié)束后被調(diào)用。

Set a Span tag

要求參數(shù)

  • the tag key, 必須是 string
  • The tag value, 只能是 string, boolean, numeric type 中任意一個。

Note that the OpenTracing project documents certain "standard tags" that have prescribed semantic meanings.

Log structured data

要求參數(shù)

  • 一個或者多個 key:value 對。 這些 keys 必須是 string; values 可以有任意類型 一些 OpenTracing 實現(xiàn)可能會處理更多的 log values。

可選參數(shù)

  • 一個顯性的 timestamp. 如何設(shè)定,這個值一定是在本地開始時間和 span 結(jié)束時間之間。

Note that the OpenTracing project documents certain "standard log keys" which have prescribed semantic meanings.

Set a baggage item

Baggage items 是 key:value 的 string 對。key:value 對適用于給定的 Span, 它的 SpanContext, 以及有著直接或者間接 引用關(guān)系(reference) 的所有的 Spans。也就是說,baggage items 在隨著 trace 一起在頻內(nèi)傳播(propagate in-band along with trace itself)。

對于一個 full-stack OpenTracing 集成來說,Baggage items 可實現(xiàn)強大的功能(例如,來自移動應(yīng)用程序的任意應(yīng)用程序數(shù)據(jù)可以透明地、深度地進入存儲系統(tǒng)中)中,并帶來有一些巨大的成本:請小心駕駛。

謹慎小心地使用此功能。每個鍵和值都被復(fù)制到相關(guān)Span的每個本地和遠程子元素中,并且這會增加很多網(wǎng)絡(luò)和CPU開銷。

要求參數(shù)

  • baggage key, 類型為 string
  • baggage value, 類型為 string
Get a baggage item

要求參數(shù)

  • The baggage key, 類型為 string

Returns the corresponding baggage value, xor some indication that such a value was missing.

SpanContext

SpanContext更像是一個“概念”,而不是通用 OpenTracing 層的有用功能。也就是說,這對于 OpenTracing 實現(xiàn)非常重要,并且確實呈現(xiàn)了一個自己的瘦API。大多數(shù) OpenTracing 用戶只能在啟動新的 Spans 時通過引用*與 SpanContext 進行交互。或者在某些傳輸協(xié)議中注入/提取跟蹤。

在 OpenTracing 中,我們強制 SpanContext 實例是不可變,以避免 Span 完成和引用周圍的復(fù)雜生命周期問題。

Iterate through all baggage items

這取決于語言以不同的方式進行建模,但在語義上,調(diào)用者應(yīng)該能夠在給定'SpanContext`實例的情況下一次遍歷所有的 baggage items。

NoopTracer

所有實現(xiàn) OpenTracing 語言 API 還必須提供某種 “NoopTracer” 實現(xiàn),可用于標記控制 OpenTracing 或為測試注入無害的東西(等等)。在某些情況下(例如Java),“NoopTracer” 可能在它自己的打包 artifact 中

Optional API Elements

某些語言還提供實用程序來在單個進程中傳入一個活動的“ Span” 和/或 “SpanContext”。例如,opentracing-go提供基于 Go 的 'context.Context機制 的 helpers 來設(shè)置和獲取 中的活動Span`。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容