元件通常無法隨時運作。大部分元件都是 所以往往在等待下一個 FIDL 訊息 。然而,這些元件會佔用記憶體。本指南適用於 調整元件,使其停止自願,並在閒置時釋出資源 閒置中。
總覽
以下是期望的做法:
變更元件的程式碼後,程式碼即可決定 停止。您的元件將保留其狀態,並在此之前處理 停止。保留這類資料稱為「託管」。
用戶端存取元件時,不會發現您的元件已停止。 以這種方式停止元件並不會中斷這些元件與 元件。
Fuchsia 提供的程式庫可讓您監控 FIDL 連線及 傳出目錄連線變為閒置狀態,然後重新 來處理這種情況
元件架構為元件提供 API,可用於儲存帳號代碼和資料 並在下次執行時擷取這些物件 (通常是在控制代碼之後 或提出新的能力要求時以下將詳細說明其運作方式 後續章節將介紹
Fuchsia 快照和 Cobalt 資訊主頁含有實用的生命週期指標,
哪些要素適合使用?
建議您查看具有這些特性的元件:
尖峰流量。元件可以啟動並處理這些流量 工作完成後返回停止。啟動和更新路徑中的大量元件 只有在這些時段才需要 ,但其他情況卻曾坐在浪費 RAM,例如:
core/system-update/system-updater
。狀態不是「太有狀態」。您可以在元件停止前保留狀態。於 因此我們可以撰寫程式碼,保留所有重要狀態實務上 在節省記憶體與保存複雜度之間取得平衡 所需狀態
高記憶體用量:使用
ffx profile memory
查看元件的記憶體用量。例如,它顯示console-launcher.cm
系統使用732 KiB
的私人記憶體私人記憶體僅適用於記憶體 該元件參照,因此我們保證至少能釋出該元件 停止執行該元件詳情請見 測量記憶體用量。Process name: console-launcher.cm Process koid: 2222 Private: 732 KiB PSS: 1.26 MiB (Proportional Set Size) Total: 3.07 MiB (Private + Shared unscaled)
http-client.cm
是不會保留的
狀態,而且只會用於指標和當機情形
上傳。因此,我們已調整設定,在設定閒置時停止。
已知限制
檢查:如果您的元件透過檢查來發布診斷資訊, 這些資訊就會在元件停止後捨棄。 https://fxbug.dev/339076913 追蹤保留情形 並在元件停止後檢查資料
Hing-gets:如果您的元件是造成當機的伺服器或用戶端 FIDL 方法則難以保留連線,因為 您無法針對 FIDL 繫結的內容儲存及還原相關資訊 。您可以將 FIDL 方法轉換為事件,且 單向確認
目錄:如果您的元件提供目錄通訊協定,系統會 難以保留連線,因為系統通常會為 由 VFS 程式庫提供。VFS 程式庫目前並未公開復原的方法 基礎頻道和相關狀態 (例如搜尋指標)。
您可以視需要提供足夠的理由,藉此支援上述所有功能。你可能會與 以及您的用途
偵測閒置狀態
停止閒置元件的第一步是強化該元件的程式碼 得知閒置狀態,表示:
FIDL 連線閒置中:元件通常會宣告多個 FIDL 通訊協定功能和用戶端 。這些連線不應有要求 注意力元件
外寄目錄處於閒置狀態:元件會提供傳出目錄, 發布了對外功能在此找不到您 表示對這個元件發出的能力要求,因此不應 連至傳出目錄,
component_manager
。其他背景商業邏輯:例如某個元件 網路要求以回應 FIDL 方法,我們可能不會 除非該網路要求已完成,否則請將該元件視為閒置。 要求中途停止該元件可能不安全。
我們透過 Rust 程式庫偵測各種情況的閒置狀態。 https://fxbug.dev/332342122 追蹤相同的 提供的功能
偵測閒置的 FIDL 連線
您可以使用 detect_stall::until_stalled
轉換 Rust
FIDL 要求串流會意外解除 FIDL 端點的繫結狀態,
連線閒置超過指定的逾時時間。您必須將元件新增到
查看瀏覽權限清單:src/lib/detect-stall/BUILD.gn
參閱 API 說明文件
並進行各種測試以下是 http-client.cm
使用此功能的方式:
async fn loader_server(
stream: net_http::LoaderRequestStream,
idle_timeout: fasync::Duration,
) -> Result<(), anyhow::Error> {
// Transforms `stream` into another stream yielding the same messages,
// but may complete prematurely when idle.
let (stream, unbind_if_stalled) = detect_stall::until_stalled(stream, idle_timeout);
// Handle the `stream` as per normal.
stream.for_each_concurrent(None, |message| {
// Match on `message`...
}).await?;
// The `unbind_if_stalled` future will resolve if the stream was idle
// for `idle_timeout` or if the stream finished. If the stream was idle,
// it will resolve with the unbound server endpoint.
//
// If the connection did not close or receive new messages within the
// timeout, send it over to component manager to wait for it on our behalf.
if let Ok(Some(server_end)) = unbind_if_stalled.await {
// Escrow the `server_end`...
}
}
偵測閒置的傳出目錄
您可以使用
fuchsia_component::server::ServiceFs::until_stalled
方法,
將 ServiceFs
轉換成解除傳出目錄伺服器的繫結
端點 (如果檔案系統沒有工作的話)。使用 API
以及說明文件和測試以下是 http-client.cm
使用此功能的方式:
#[fuchsia::main]
pub async fn main() -> Result<(), anyhow::Error> {
// Initialize a `ServiceFs` and add services as per normal.
let mut fs = ServiceFs::new();
let _: &mut ServiceFsDir<'_, _> = fs
.take_and_serve_directory_handle()?
.dir("svc")
.add_fidl_service(HttpServices::Loader);
// Chain `.until_stalled()` before calling `.for_each_concurrent()`.
// This wraps each item in the `ServiceFs` stream into an enum of either
// a capability request, or an `Item::Stalled` message containing the
// outgoing directory server endpoint if the filesystem became idle.
fs.until_stalled(idle_timeout)
.for_each_concurrent(None, |item| async {
match item {
Item::Request(services, _active_guard) => {
let HttpServices::Loader(stream) = services;
loader_server(stream, idle_timeout).await;
}
Item::Stalled(outgoing_directory) => {
// Escrow the `outgoing_directory`...
}
}
})
.await;
}
等待其他背景商業邏輯
ServiceFs
產生
Item::Stalled
訊息。如果您有一點背景
可避免元件停止,但 ServiceFs
已成為
同時處於閒置狀態,且已提前解除傳出目錄繫結
端點如要處理這些情況,您可以防止 ServiceFs
進入閒置狀態ServiceFs
產生的 Item::Request
包含
ActiveGuard
。只要主動守衛在範圍內,
ServiceFs
不會處於閒置狀態,且會繼續產生能力要求,因為
來讓他們進入
同樣地,您可以建立 ExecutionScope
,
與處理 FIDL 連線相關的背景工作
ExecutionScope::wait()
等待作業完成。舉例來說,
直到那之前,http-client.cm
中的 loader_server
函式才會傳回
背景工作完成,這樣 active_guard
就會保存在
Item::Request
範圍內,導致 ServiceFs
無法停止。
由 Escrow 控制法和狀態授予架構
一旦連線處於閒置狀態,且程式庫會提供未繫結的伺服器 下一步就是託管這些帳號代碼,也就是將帳號代碼傳送到 以便安全維護
無狀態通訊協定
部分 FIDL 連線沒有狀態。每個要求的作用相同 透過相同或不同的連線傳送 您可以為這些連線執行下列步驟:
如果尚未在元件資訊清單中宣告能力,請先宣告。您可能需要 宣告能力,如果這個通訊協定連線是衍生自其他 連線,而且通常不會從傳出目錄提供。
在宣告能力時新增
delivery: "on_readable"
。您必須新增 並將元件設為delivery_type
瀏覽權限清單tools/cmc/build/restricted_features/BUILD.gn
。架構 然後監控伺服器端點的可讀訊號 建立新的連線要求,將伺服器端點連線至供應商 元件。範例:capabilities: [ { protocol: "fuchsia.net.http.Loader", delivery: "on_readable", }, ],
從
self
新增使用宣告,讓程式能力 會從傳入的命名空間對該命名空間進行連線您可以在/escrow
目錄,以便與其他函式 元件。範例:{ protocol: "fuchsia.net.http.Loader", from: "self", path: "/escrow/fuchsia.net.http.Loader", },
從傳入的命名空間連線至能力,並傳遞未繫結 伺服器端點:
detect_stalled::until_stalled
。if let Ok(Some(server_end)) = unbind_if_stalled.await { // This will open `/escrow/fuchsia.net.http.Loader` and pass the server // endpoint obtained from the idle FIDL connection. fuchsia_component::client::connect_channel_to_protocol_at::<net_http::LoaderMarker>( server_end.into(), "/escrow", )?; }
總體來說,這代表元件架構會監控閒置連線 然後將功能傳回元件 這種做法如果元件已停止,系統會啟動元件。
傳出目錄
我們必須使用其他 API 來託管主要傳出目錄連線
(也就是 Item::Stalled
中 ServiceFs
傳回的回應),因為該伺服器
端點是其他所有連線都會傳送至
元件。對於 ELF 元件,您可以將傳出目錄傳送至
透過 fuchsia.process.lifecycle/Lifecycle.OnEscrow
FIDL 事件建立架構:
將
lifecycle: { stop_event: "notify" }
新增至元件.cml
:program: { runner: "elf", binary: "bin/http_client", lifecycle: { stop_event: "notify" }, },
使用生命週期編號控制代碼,將其轉換成 FIDL 要求串流,然後 使用
send_on_escrow
傳送事件:let lifecycle = fuchsia_runtime::take_startup_handle(HandleInfo::new(HandleType::Lifecycle, 0)).unwrap(); let lifecycle: zx::Channel = lifecycle.into(); let lifecycle: ServerEnd<flifecycle::LifecycleMarker> = lifecycle.into(); let (mut lifecycle_request_stream, lifecycle_control_handle) = lifecycle.into_stream_and_control_handle().unwrap(); // Later, when `ServiceFs` has stalled and we have an `outgoing_dir`. let outgoing_dir = Some(outgoing_dir); lifecycle_control_handle .send_on_escrow(flifecycle::LifecycleOnEscrowRequest { outgoing_dir, ..Default::default() }) .unwrap();
元件傳送
OnEscrow
事件後,就無法 監控更多能力要求因此,系統應該會立即結束遊戲。 下次執行時,您的元件會回傳啟動資訊 先前執行時傳出的同一個outgoing_dir
控制代碼。請參閱
http-client
,瞭解上述所有項目的排列方式。
有狀態通訊協定和其他重要狀態
fuchsia.process.lifecycle/Lifecycle.OnEscrow
事件需要另一個引數
具備 escrowed_dictionary client_end:fuchsia.component.sandbox.Dictionary
是 Dictionary
物件的參照。字典:
可能存放資料或功能的鍵/值對應。
您可以使用
fuchsia.component.sandbox.Factory
建立Dictionary
並在Factory
通訊協定上呼叫CreateDictionary
:use: [ { protocol: "fuchsia.component.sandbox.Factory", from: "framework", } ]
let factory = fuchsia_component::client::connect_to_protocol::< fidl_fuchsia_component_sandbox::FactoryMarker >().unwrap(); let dictionary = factory.create_dictionary().await?;
若要將一些資料 (例如位元組向量) 新增至
Dictionary
,請呼叫Dictionary
FIDL 連線上的Insert
。詳情請參閱fuchsia.component.sandbox
FIDL 程式庫說明文件 其他方法:let bytes = vec![...]; let data = fidl_fuchsia_component_sandbox::Data::Bytes(bytes); let dictionary = dictionary.into_proxy().unwrap(); dictionary .insert( "my_data", fidl_fuchsia_component_sandbox::Capability::Data(data) ) .await??;
退出前,在
send_on_escrow
中傳送Dictionary
用戶端端點:lifecycle .control_handle() .send_on_escrow(flifecycle::LifecycleOnEscrowRequest { outgoing_dir: Some(outgoing_dir), escrowed_dictionary: Some(dictionary.into_channel().unwrap().into_zx_channel().into()), ..Default::default() })?;
下次啟動時,您可以從新創公司控制代碼取得這個字典:
if let Some(dictionary) = fuchsia_runtime::take_startup_handle( HandleInfo::new(HandleType::EscrowedDictionary, 0) ) { let dictionary = dictionary.into_proxy()?; let capability = dictionary.get("my_data").await??; match capability { fidl_fuchsia_component_sandbox::Capability::Data( fidl_fuchsia_component_sandbox::Data::Bytes(data) ) => { // Do something with the data... }, capability @ _ => warn!("unexpected {capability:?}"), } }
Dictionary
物件支援多種項目資料類型。如果您的
元件的狀態小於 fuchsia.component.sandbox/MAX_DATA_LENGTH
可以考慮儲存 fuchsia.component.sandbox/Data
項目
可容納位元組向量
我想等待管道可讀取
停止之前,如果想讓元件架構
等到管道內容清晰可讀後,再將管道轉達給
元件,您可以使用相同的 delivery: "on_readable"
技術。這個
一般化成元件不會公開的 FIDL 通訊協定,例如
服務成員甚至支援不採用 FIDL 通訊協定的管道。阿斯
例如,假設您的元件擁有 Zircon 例外狀況管道
指示架構先等可讀取的管道
元件,您可以宣告下列 .cml
:
capabilities: [
{
protocol: "exception_channel",
delivery: "on_readable",
path: "/escrow/exception_channel",
},
],
use: [
{
protocol: "exception_channel",
from: "self",
path: "/escrow/exception_channel",
}
]
請注意,exception_channel
能力並未公開。這項能力
用於元件本身元件可能會開啟 /escrow/exception_channel
與要等待的管道連線。建立管道後
讀取架構時,架構會在傳出時/escrow/exception_channel
開啟
目錄,如有需要,請啟動元件。總而言之,您可以宣告
功能,並使用 self
中的控點向 component_manager
託管控制代碼。
如需其他類型的資料,請與元件架構團隊聯絡 觸發事件,例如等待自訂信號或等待計時器。
測試
建議您加強現有的整合測試,以便同時測試
元件可自行停止並重新啟動,而不會中斷 FIDL 連線。
如果您已有整合測試來啟動元件並傳送
FIDL 要求收到 FIDL 要求,您可以使用元件事件比對器來驗證
在沒有任何訊息時,元件就會停止。詳情請參閱
http-client
測試會列出實際操作範例。
到達網頁和指標
如要為特定產品最佳化這項元件, 您可以為元件新增結構化設定,以控制是否顯示 閒置逾時。
元件架構會記錄元件的開始和停止時間長度 並上傳至 Cobalt您可能會在 資訊主頁,微調閒置逾時。
當擷取到意見回饋快照時,如
欄位,初始和最新元件執行作業的時間戳記將為
可在選取器「<component_manager>:root/lifecycle/early
」和
<component_manager>:root/lifecycle/late
。你也可以找出
事件記錄,協助您調查是否發生錯誤
以錯誤的方式停止元件