RFC-0121:组件生命周期事件 | |
---|---|
状态 | 已接受 |
领域 |
|
说明 | 此 RFC 规定了组件框架事件功能。 |
问题 | |
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2021-05-26 |
审核日期(年-月-日) | 2021-08-11 |
摘要
本文档记录了设计审议、原则和制定的决策 组件事件:概念、清单语法和 FIDL 协议。
下文所述的许多决定是在 2020 年初制定并实施的。 还提出了一些其他概念,这些概念可以解决 决策。我们建议将许多功能列入许可名单, 在我们将其迁移到 更好的机制。
本文档涵盖以下组件框架 API:
- CML:用于编写组件清单的语言
fuchsia.sys2/events.fidl
:组件事件所在的当前 API Surface FIDL API 已发布。目标是让所有事件 API 都升级到fuchsia.component/events.fidl
,并在 SDK 中提供这些键。
设计初衷
组件管理器会在内部处理组件的生命周期,并且不会公开 向组件传递这些信息某些特权客户端(例如: 诊断、测试、组件监控器、调试程序)需要更多深入了解 进入组件生命周期以执行其工作。
我们引入了组件事件作为一种解决方案,用于向 这些特权组件所有这些事件都由组件分派 。通过 功能路由 API 在某些地方处于开放状态, 请参阅本文档进行进一步的设计, 事件本身
设计
本部分概述了组件事件与 API 的当前设计 修订,突出显示当前设计并非长期 设计并说出未来可以设计和实现的功能。
组件事件流功能
将组件事件建模为“事件流”功能。每个事件 流功能是指单个组件或组件子树 在拓扑中在撰写本文档时,将事件视为 个别事件功能(而非事件流)。组件管理器 在生命周期转换时,代表组件发出事件 。
与任何功能一样,事件流可以进行路由。当事件流符合以下条件时:
从框架公开/提供:事件是指 公开或提供相应事件。
从父/子使用:组件可以监听路由到以下地址的事件: 是来自子级还是父级组件的父级可以直接 使用来自未明确子项的子项的生命周期事件流 从框架公开该事件。
来自以上根目录的事件流
“parent”中的根领域可以使用/提供事件流。您 可能会好奇,如果要 是拓扑的根吗?在组件管理器术语中,功能 由组件管理器提供给根域的信息称为来自 “ above root”:
从“根目录”提供的事件流根域的作用域限定为
整个组件实例拓扑这些事件流的范围可缩小至
在路由时引用拓扑的子树。可以看出
类似于将目录功能作为整体或作为
子目录(目录路由声明中的 subdir
键)。
监听范围限定为整个领域的事件的唯一方法是使用
源于可用根目录的缩小范围事件流功能。活动
覆盖整个领域树的特权/敏感,会破坏封装
因此,我们会从上述根明确路由它们,
通过静态路由实现访问权限控制
请参考以下示例:
// root.cml
{
offer: [
{
event_stream: "started",
from: "parent",
to: "#core",
scope: "#core"
},
]
}
// core.cml
{
offer: [
{
event_stream: "started",
from: "parent",
to: "#test_manager",
scope: "#test_manager"
},
]
}
// test_manager.cml
{
offer: [
{
event_stream: "started",
from: "parent",
to: [ "#archivist", "#tests" ]
scope: "#tests",
}
]
}
// tests.cml
{
offer: [
{
event_stream: "started",
from: "parent",
to: [ "#test-12345" ]
scope: "#test-12345",
}
]
}
// test-12345.cml
{
offer: [
{
event_stream: "started",
from: "framework",
scope: "#bar",
to: "#foo"
}
],
use: [
{
event_stream: "started",
from: "parent"
},
{
event_stream: "stopped",
from: "framework",
scope: "#bar",
}
]
}
// foo.cml
{
use: [
{
event_stream: "started",
from: "parent"
}
]
}
// archivist.cml
{
use: [
{
event_stream: "started",
from: "parent",
}
]
}
在此示例中:
foo
可以获取bar
的started
事件,因为test-12345
将其路由至 。archivist
可以获取tests
下所有组件的started
事件 因为它的提供者是test_manager
提供started
从root
获得的core
,从above root
获得该物品, 不断缩小范围test-12345
是测试根,可以开始关于下所有组件的事件 原因与archivist
相同。但是,与档案管理员不同, (可获取来自tests
的所有事件),它只能获取以下事件: 测试,因为tests
缩小了test-12345
事件的范围。
此示例展示了目前事件的一个核心用例。通过 Archive Storage 能够观察每个测试内部发生的情况, 。此外,每个测试都可以获取与测试中所有组件相关的事件,或 这个主题。
合并事件流
同一类型的事件流可以合并为单个数据流。
例如,在上例中,#test-12345
可以提供 stopped
将 foo
和 bar
作为一项功能应用于一些其他组件。该组件
然后,系统会获取 foo
和 bar
的 stopped
事件。
// core.cml
offer: [
{
event_stream: "stopped",
from: [ "#netstack", "#supervisor" ],
to: "#someone",
}
]
目前,不允许公开/提供自己活动。不过, 这里还有增长空间:允许事件公开/提供自定义事件 分派。
事件模式
在编写此文档时,事件可以按异步方式 还是同步方式。目的是让只有异步事件并弃用 完全同步活动
同步使用事件可让订阅者屏蔽组件 并在处理事件时恢复。这是为 在进行初始测试时也要考虑使用调试程序。
从那时起,我们了解到可以使用同步事件编写测试, 通常都是异步事件因此, 工作 已打算完全消除同步事件,但组件中仍有一些用途 管理者内部测试我们相信同步事件对于 (例如 step 或 zxdb),但此时我们会制定解决方案, 准确满足调试程序需要的一系列函数。
我们的提案是将使用同步事件并努力实现 而完全消除了同步事件
事件类型
在编写本文档时,我们有两类事件:
- 生命周期事件。这些事件反映了 生命周期,并且会发出 由管理此类信息的组件管理器管理。
- 已弃用的事件。这些事件不会反映 组件实例,我们正努力将它们完全删除,取而代之的是 更合适的解决方案。
生命周期事件类型有以下几种:
Discovered
:这是组件生命周期的第一阶段。 为静态子级在创建动态子级时分派给这些子级。 当其父级被解析时,而对于根组件,当其位于组件管理器中时 。Resolved
:已成功解析第一个实例的声明 。Started
:根据组件管理器,此实例已启动。 不过,如果这是一个可执行组件,运行程序将进一步 启动组件所需执行的步骤组件已经开始运行,但是 可能尚未开始执行代码。Stopped
:实例已成功停止。此事件必须早于 已销毁。Destroyed
:已开始销毁实例。此实例 就此停止。该实例仍存在于父级的领域中,但 即将被移除。Purged
:已成功销毁实例。实例已停止 并且不再存在于父级的领域中。
以及以下已废弃的事件类型:
Running
:此事件由组件管理器针对所有用户合成 在订阅时已在运行的组件。此活动 派生自started
,但适用于已启动(且未停止)的组件 。最终,我们希望 组件框架查询 API 来了解正在运行的内容。因此, 计划是将此活动列入许可名单,以供其唯一客户(存档员)查看, 请使用新的 API 并移除running
。Capability Routed
:为在测试中使用而引入此事件。它具有 近期遭到移除 我们正计划彻底 。Capability Requested
:此事件作为临时解决方案引入, 为fuchsia.logger/LogSink
连接提供组件归因。开始时间 那么它也已用于向fuchsia.debugdata/Publisher
连接。本来不会花费太长时间 术语解决方案。由于事件系统仅用于特权组件, 通过组件事件构建这项功能是一种需要投入很少的方法。 大家知道,如果用例不断扩大,就有必要开发 更加标准化的解决方案。目前,我们计划移除 事件,在此期间将其列入许可名单: 归档人员、调试数据和测试管理器(针对调试数据)。Directory Ready
:此事件作为一种解决方案引入,用于提供out/diagnostics
目录,由组件向 Archivist 公开,用于 检查数据汇总。诊断团队计划设计 日志。鉴于需要在组件启动之前获取日志 那么这种方法就过时了。一种新的 向档案管理员提供检查和记录 VMO 的解决方案将是 其目的是保证日志在配置组件之前就可用 启动异步循环目前的建议是将此事件添加到 只有其拥有的用户(存档员),并努力将其彻底删除。
路由 CML 语法
使用
{
use: [
{
event_stream: [
"running",
"started",
"stopped",
],
from: "parent",
mode: "async",
path: "/my_stream"
},
]
}
该使用声明包含:
event_stream
:单个事件名称或事件名称列表。from
: capability 的来源。允许使用的值:父级或子级 参考。不允许使用框架或自身中的事件。path
:将传送事件流的路径。此操作为可选项。时间 给定的组件的传入命名空间将包含一个服务文件,该文件具有 组件管理器的fuchsia.component.EventStream
的指定名称 (请参阅使用)。如果未提供,组件可以使用EventSource
,用于在特定时间点开始处理事件。scope
:从父级使用事件时,scope
可用于引用 将事件缩小到某个子范围,否则该事件将带有 将来自父级的作用域mode
:默认为async
。如前所述,唯一的事件模式 异步。因此,只有列入许可名单的测试才可以使用模式 “同步”直到完全消除这些模式。该字段最终将 完全不需要filter
:目前只用于DiagnosticsReady
和CapabilityRequested
。如前所述,这些活动将被移除 不会提供关于过滤器的详细信息, 给非诊断开发者
您也可以使用来自不同来源的事件。在
在以下示例中,组件将获取包含 started
的单个事件流,
限定了 stopped
事件的范围,因为它是由其父级以及 started
提供的
当其子级 #child
启动时。
use: [
{
event_stream: [
{
name: [ "started", "stopped ],
from: "parent",
},
{
name: "started",
from: "framework",
scope: "#child",
}
]
}
]
优惠
{
offer: [
{
event_stream: "started",
from: [ "#child_a", "parent", ],
to: "#archivist",
as: "started_foo"
},
]
}
优惠声明包含:
event_stream
:单个事件名称或事件名称列表。scope
:当从框架提供事件时,作用域允许执行以下操作: 定义事件涉及的子项(或子项的数组)。如果 未指定范围,范围为 self,这意味着此事件与 组件本身。当某个事件由父级提供时,该范围允许 将事件缩小到子范围。from
: capability 的一个或多个来源。当多个来源 但前提是这些信息流被视为进行合并。允许使用的值:framework、 父引用或子引用不允许自行提供建议。to
:向其提供该功能的子引用。as
:功能的目标名称(重命名)。只能提供 在指定了单个事件流名称时触发。
公开
{
expose: [
{
event_stream: "started",
from: "framework",
scope: [ "#child", "#other_child" ],
as: "foo_started"
},
]
}
公开声明包含:
event_stream
:单个事件名称或事件名称列表。scope
:当从framework
公开事件时,范围是必需的, 允许定义发生该事件的子项(或子项的数组) 。from
: capability 的另一个来源。当多个来源 但前提是这些信息流被视为进行合并。允许使用的值:parent 或 子引用。不允许公开自己的事件。as
:功能的目标名称(重命名)。只能提供 在指定了单个事件流名称时触发。
EventSource
协议
EventSource
是允许组件实例订阅
事件流。它是 framework
中可供任何组件使用的内置功能
以使用明确路由至它们的事件。
静态事件流
静态事件流是通过
传入的 /events
目录。不同于运行时订阅
EventSource.Subscribe
,事件可以缓冲并触发
启动组件。
组件会在某个路径和组件管理器中声明它们 use
事件流
将在传入的命名空间中的指定路径(该路径为
由组件管理器提供的 fuchsia.component.EventStream
(请参阅
使用此部分)。
在执行 GetNext
调用之前,事件会在内部进行缓冲。容器的
此缓冲区将在实施过程中决定(可以是大型缓冲区,
下一个批次的大小上限或其他数据),并且会清楚显示
请参阅 FIDL API 和事件文档。如果缓冲数量
事件超过指定数量(由于使用者速度太慢)组件
管理程序会丢弃事件并关闭连接。然后,该组件
并在其准备好接收事件时重新连接。这是一种临时解决方法,
Zircon 中的流控制。我们希望防止频道溢出
那么反过来负责 EventStream
协议(
组件管理器,而不是组件管理器),我们也不希望使用过多的
在组件管理器中配置内存大小。一个行为良好的客户端,
以稳定的速率运行事件就无需担心丢失事件了。客户
如果事件处理速度过慢,则会收到有关丢弃事件的通知。这将
通过其接收的流中的事件发生。几个
另一种方法是:
- 同一类型的事件,具有一个错误负载,指示 该类型已被舍弃。
fuchsia.component.EventErrorPayload
下的一个特殊字段:dropped
。这个 字段将包含EventType
和指示 被舍弃的此类事件。- 协议中的 FIDL 事件, “drop”指定丢弃的数量和类型。此备选 那么就会引入另一个 DoS 媒介, 事件。
FIDL API
在撰写本文档时,有关使用事件的协议为
在 fuchsia.sys2/events.fidl
中定义。此提案引入了一些
目标是将协议和结构升级到 fuchsia.component
并在 SDK 中提供它们:
组件管理器提供 EventStream
而非组件
这意味着将 EventSource#Subscribe
更改为接受请求:
[Discoverable]
protocol EventSource {
Subscribe(vector<EventSubscription> events, request<EventStream> stream)
-> () error fuchsia.component.Error;
};
EventStream.GetNext
可批量提供事件
由于组件管理器现在提供协议,并对事件进行批处理,
在上一部分中,新的流协议会在
GetNext
,而不是单个事件。
protocol EventStream {
GetNext() -> vec<Event>;
};
已舍弃“TakeStaticEventStream
”
移除了 EventSource.TakeStaticEventStream
方法,改为将
将事件流直接传入到来的命名空间中,如前文所述
部分。
Event
和 EventResult
更严格
在撰写本文时,所有这些数据类型均为表。不过,
他们的数据已非常明确定义,我们预计不会向其添加更多数据。接收者
通过消除对选填字段的处理来改进工效学设计,
struct
和非灵活联合:
struct Event {
EventHeader header;
EventResult event_result;
};
union EventResult {
1: EventPayload payload;
2: EventError error;
};
实现
此设计中的大部分内容都已实现。还有一些内容需要 在 SDK 中提供此 API 之前,我们已先实现此 API。具体而言:
- 将以下事件列入许可名单:
directory ready
、running
和capability requested
。 - 在传入命名空间中放置一个静态事件流,并移除
EventStream.TakeStaticEventStream
方法结合使用。 - 自今天以来,我们安排了
events
的路线,并打算安排event streams
路线,因此 CML 最新情况 可以在路由中合并 - 从“根目录”上提供有关整个拓扑的事件添加到根目录
在事件路由声明中实现
scope
关键字,以允许 具有来自拓扑中子树的事件。 - 公开事件。在撰写此文档时,我们仅支持
offer
和use
。 - 在原始位置,事件现在是指单个组件,但 会提供给根域。路由后,可以 因此它们可以指多个组成部分。
性能
事件系统建立在现有的内部钩子系统之上, 因此组件管理器实现的性能影响 将事件分派给对其感兴趣的其他组件的做法几乎可以忽略不计。 事实上,从现在开始,如果将某个组件 只关注来自单个组件(而非整个子树)的事件, 您可以只为相应组件(而非整个)分派事件, 子树,从而减少涉及的系统调用数量。
工效学设计
此 RFC 改进了活动的工效学设计:
- 如果某个组件只关注单个组件事件,现在该事件可以是 以 CML 格式表示,而无需代码对其进行过滤。
- 所有组件现在都内置有
EventSource
协议 框架功能,并且不需要显式路由。 - 有一个 capability 的路由是
event stream
,而不是路由 与今天一样events
,消耗event streams
。 Event streams
可以在路由时合并,从而减少路由量 开发者需要用 CML 编写的语句。
向后兼容性
此更改不会破坏兼容性。就会有 在组件管理器级别(以及根、核心、引导领域)重构 而不是在客户端组件中此外,测试中还会有一些重构 今天使用事件(全部为树内事件),因为事件将与单个组件相关 。
安全性和隐私权注意事项
此提案将维护安全与隐私
审核。以前,EventSource
协议是显式路由的
因为事件关乎整个子树。现在,该协议是
功能和事件则与特定组件相关整体活动
子树以显式方式进行路由,并且可以对
确保非特权组件不会收到这些事件
测试
所有事件功能和语义都必须在组件中进行集成测试 。
文档
缺点、替代方案和未知问题
提供从组件到框架的 EventSink
功能
按照这种想法,组件会向RealmEventSink
框架。然后,框架会打开此功能,并将事件推送到
实时更新此提案缺少定义
组件将获取事件,并且没有明确的路径来限制此类范围
或能够静态验证组件是否未从
而相应拓扑中不应具有可见性的部分。在当前
那么就有可能以静态方式表达关于特定集合的
组件的数量(对于常规用例)或拓扑中的整个子树(对于
用于生产环境和测试中的诊断用例)。
用于公开和优惠路由声明的自身来源
来自 self
的“expose/offer
”活动将留作开放区域,供未来的设计工作使用
由组件可能会公开并分派的自定义事件组成
先验技术和参考资料
有关当前事件状态的文档可在 活动功能和 事件 FIDL 定义。