建立永久工作站

回報問題 查看原始碼 Nightly · 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

持續性工作站可加快建構作業速度。如果您在建構作業中發現重複動作,且啟動成本高昂,或可透過跨動作快取受益,建議您實作自己的永久工作站來執行這些動作。

Bazel 伺服器會使用 stdin/stdout 與 worker 通訊。它支援使用通訊協定緩衝區或 JSON 字串。

工作站實作項目包含兩個部分:

建立 worker

持續性工作站必須符合以下幾項規定:

  • 會從其 stdin 讀取 WorkRequests
  • 它會將 WorkResponses (僅限 WorkResponse) 寫入 stdout
  • 並接受 --persistent_worker 標記。包裝函式必須辨識 --persistent_worker 指令列旗標,且只在傳遞旗標時使自身永久持續運作,否則必須執行一次性編譯並結束。

如果您的程式符合這些規定,即可做為持續性 worker 使用!

工作要求

WorkRequest 包含工作站的引數清單、路徑摘要組合清單,代表工作站可存取的輸入內容 (這並未強制執行,但您可以使用這項資訊進行快取),以及要求 ID,單一工作站為 0。

注意:雖然通訊協定緩衝區規格使用「蛇形大小寫」(request_id),但 JSON 通訊協定使用「駝峰式大小寫」(requestId)。本文件在 JSON 範例中使用駝峰式大小寫,但在討論欄位時,無論通訊協定為何,都會使用蛇形大小寫。

{
  "arguments" : ["--some_argument"],
  "inputs" : [
    { "path": "/path/to/my/file/1", "digest": "fdk3e2ml23d"},
    { "path": "/path/to/my/file/2", "digest": "1fwqd4qdd" }
 ],
  "requestId" : 12
}

您可以使用選用的 verbosity 欄位,向 worker 要求額外的偵錯輸出內容。輸出內容和方式完全由 worker 決定。值越高,輸出內容就越詳細。將 --worker_verbose 標記傳遞至 Bazel 會將 verbosity 欄位設為 10,但您可以手動使用較小或較大的值,以便產生不同數量的輸出內容。

選用的 sandbox_dir 欄位僅供支援多重沙箱的工作者使用。

工作回應

WorkResponse 包含要求 ID、零或非零結束代碼,以及輸出訊息,說明在處理或執行要求時遇到的任何錯誤。工作站應擷取其呼叫的任何工具的 stdoutstderr,並透過 WorkResponse 回報。將其寫入 worker 程序的 stdout 是不安全的,因為這會干擾 worker 通訊協定。將其寫入 worker 程序的 stderr 是安全的,但結果會在個別 worker 記錄檔中收集,而非歸因於個別動作。

{
  "exitCode" : 1,
  "output" : "Action failed with the following message:\nCould not find input
    file \"/path/to/my/file/1\"",
  "requestId" : 12
}

根據 protobuf 的規範,所有欄位均為選填欄位。不過,Bazel 要求 WorkRequest 和對應的 WorkResponse 必須具有相同的要求 ID,因此如果要求 ID 非零,就必須指定該 ID。這是有效的 WorkResponse

{
  "requestId" : 12,
}

request_id 值為 0 表示「單向」要求,當這項要求無法與其他要求並行處理時,就會使用這項值。伺服器會保證特定 worker 收到的要求,只會包含 request_id 0 或 request_id 大於零。單工要求會以序列方式傳送,例如伺服器在收到回應前不會傳送其他要求 (取消要求除外,請參閱下文)。

注意事項

  • 每個通訊協定緩衝區前面都會加上其長度,格式為 varint (請參閱 MessageLite.writeDelimitedTo())。
  • JSON 要求和回應前方沒有大小指標。
  • JSON 要求的結構與 protobuf 相同,但會使用標準 JSON,並為所有欄位名稱使用駝峰式大小寫。
  • 為了維持與 protobuf 相同的向前和向後相容性屬性,JSON 工作者必須容許這些訊息中的不明欄位,並使用 protobuf 預設值來處理缺少的值。
  • Bazel 會將要求儲存為 protobuf,並使用 protobuf 的 JSON 格式將其轉換為 JSON

取消

工作站可選擇允許在完成工作要求前取消工作要求。這項功能在與動態執行作業連結時特別實用,因為本機執行作業可能會經常因更快速的遠端執行作業而中斷。如要允許取消,請將 supports-worker-cancellation: 1 新增至 execution-requirements 欄位 (請見下方說明),並設定 --experimental_worker_cancellation 標記。

取消要求是設定 cancel 欄位的 WorkRequest (同樣地,取消回應是設定 was_cancelled 欄位的 WorkResponse)。取消要求或取消回應中唯一必須包含的其他欄位是 request_id,用於指出要取消哪項要求。如果是單一複雜工作站,request_id 欄位會是 0,而如果是多 x 工作站,先前傳送 WorkRequest 的非 0 request_id 則會是非 0。伺服器可能會針對工作者已回應的要求傳送取消要求,在這種情況下,系統必須忽略取消要求。

每則非取消的 WorkRequest 訊息無論是否取消,您都必須回覆一次。伺服器傳送取消要求後,工作站可能會回應 WorkResponse,其中 request_id 已設為 request_id,而 was_cancelled 欄位則設為 true。系統也接受傳送一般 WorkResponse,但會忽略 outputexit_code 欄位。

一旦針對 WorkRequest 傳送回應,工作者就不得觸碰工作目錄中的檔案。伺服器可自由清除檔案,包括暫存檔案。

建立使用 worker 的規則

您還需要建立規則,用來產生工作站要執行的動作。建立使用 worker 的 Starlark 規則,就像建立任何其他規則一樣。

此外,規則必須包含對 worker 本身的參照,且產生的動作必須符合某些規定。

參照 worker

使用工作站的規則必須包含一個參照該工作站的欄位,因此您需要建立 \*\_binary 規則的執行個體來定義工作站。如果工作站稱為 MyWorker.Java,則相關規則可能如下:

java_binary(
    name = "worker",
    srcs = ["MyWorker.Java"],
)

這會建立「worker」標籤,用於參照 worker 二進位檔。接著,您將定義使用工作站的規則。這項規則應定義參照 worker 二進位檔的屬性。

如果您建構的工作機器人二進位檔位於名為「work」的套件中,且位於建構作業的頂層,則屬性定義可能會是以下內容:

"worker": attr.label(
    default = Label("//work:worker"),
    executable = True,
    cfg = "exec",
)

cfg = "exec" 表示工作站應建構於執行平台上,而非在目標平台上執行 (也就是說,在建構期間將工作站做為工具使用)。

工作行動要求

使用 worker 的規則會建立 worker 要執行的動作。這些動作有幾項必要條件。

  • "引數" 欄位。此函式會接收字串清單,其中除了最後一個字串外,所有字串都是在啟動時傳遞給 worker 的引數。「arguments」清單中的最後一個元素是 flag-file (@-preceded) 引數。工作站會根據個別工作要求,從指定的旗標檔案讀取引數。您的規則可以將非啟動引數寫入此標記檔案。

  • 「execution-requirements」欄位,可接受含有 "supports-workers" : "1""supports-multiplex-workers" : "1" 的字典,或兩者皆含。

    所有傳送至 worker 的動作都需要「arguments」和「execution-requirements」欄位。此外,應由 JSON 工作者執行的動作,必須在執行要求欄位中加入 "requires-worker-protocol" : "json""requires-worker-protocol" : "proto" 也是有效的執行要求,但 proto 工作站不需要這項要求,因為這是預設值。

    您也可以在執行要求中設定 worker-key-mnemonic。如果您要重複使用多種動作類型的可執行檔,並且想透過這個 worker 區分動作,這項功能就很實用。

  • 在執行動作期間產生的暫時性檔案應儲存在 worker 目錄中。這樣一來即可啟用沙盒功能。

假設在上述規則定義中,「worker」屬性設有代表輸入內容的「srcs」屬性、代表輸出內容的「output」屬性,以及代表工作站啟動引數的「args」屬性,則對 ctx.actions.run 的呼叫可能如下:

ctx.actions.run(
  inputs=ctx.files.srcs,
  outputs=[ctx.outputs.output],
  executable=ctx.executable.worker,
  mnemonic="someMnemonic",
  execution_requirements={
    "supports-workers" : "1",
    "requires-worker-protocol" : "json"},
  arguments=ctx.attr.args + ["@flagfile"]
 )

如需其他範例,請參閱「實作持續性工作站」。

範例

除了用於整合測試的 JSON 工作站範例之外,Bazel 程式碼集也會使用 Java 編譯器工作站

您可以使用結構體,透過傳入正確的回呼,將任何以 Java 為基礎的工具設為 worker。

如需使用工作站的規則範例,請查看 Bazel 的工作站整合測試

外部貢獻者已使用各種語言實作工作站,請參閱 Bazel 永續性工作站的多語言實作。您可以在 GitHub 上找到更多範例