Grid 架构
Grid 被设计为一组组件,这些组件都在维护 Grid 中发挥作用。它看起来可能相当复杂,但希望本文档能够帮助消除任何困惑。
关键组件
Grid 的主要组件是
- 事件总线
- 用于在其他组件之间异步发送可能接收的消息。
- 新会话队列
- 维护尚未由分配器分配给节点的传入会话列表。
- 分配器
- 负责维护 Grid 中可以运行会话的可用位置的模型(称为“插槽”),并接受任何传入的新会话请求并将它们分配给一个插槽。
- 节点
- 运行一个WebDriver 会话。每个会话都分配到一个插槽,每个节点都有一个或多个插槽。
- 会话映射
- 维护会话 ID与运行会话的节点地址之间的映射。
- 路由器
- 充当 Grid 的前端。这是 Grid 中唯一可能暴露给更广泛的 Web 的部分(尽管我们强烈建议不要这样做)。它将传入的请求路由到新会话队列或运行会话的节点。
在讨论 Grid 时,还有一些其他有用的概念需要牢记
- 一个 插槽 是可以运行会话的地方。
- 每个插槽都有一个 刻板印象。这是新会话会话请求必须匹配的最小功能集,然后分配器才会将该请求发送到拥有该插槽的节点。
- Grid 模型 是分配器如何跟踪 Grid 的状态。顾名思义,有时这可能会与现实不同步(可能是因为分配器才刚刚启动)。它优先于查询每个节点,以便分配器可以快速将一个插槽分配给新会话请求。
同步和异步调用
Grid 中使用了两种主要的通信机制
- 通过 HTTP 请求的同步 “REST-ish” JSON。
- 发送到事件总线的异步事件。
我们如何选择使用哪种通信机制?毕竟,我们可以以基于事件的方式对整个 Grid 进行建模,并且它也可以正常工作。
答案是,如果执行的操作是同步的(例如,大多数 WebDriver 调用),或者如果丢失响应会产生问题,则 Grid 使用同步调用。相反,如果我们想向任何感兴趣的人广播信息,或者如果丢失响应无关紧要,那么我们更喜欢使用事件总线。
一个有趣的事情是,异步调用比同步调用与它们的侦听器更加解耦。
组件之间的启动顺序和依赖关系
虽然 Grid 设计为允许组件以任何顺序启动,但从概念上讲,组件的启动顺序是
- 事件总线和会话映射首先启动。这些没有其他依赖项,甚至彼此之间都没有,因此可以安全地并行启动。
- 接下来启动会话队列。
- 现在可以启动分配器了。这将定期连接到会话队列并轮询作业,尽管此轮询可能由事件(将新会话添加到队列中)或定期启动。
- 可以启动路由器。新会话请求将被定向到会话队列,分配器将尝试找到一个插槽来运行会话。
- 我们现在可以启动一个节点了。有关如何将节点注册到 Grid 的详细信息,请参见下文。注册完成后,Grid 即可提供流量。
你可以这样描绘组件之间的依赖关系,其中 “✅” 表示组件之间存在同步依赖关系。
事件总线 | 分配器 | 节点 | 路由器 | 会话映射 | 会话队列 | |
---|---|---|---|---|---|---|
事件总线 | X | |||||
分配器 | ✅ | X | ✅ | ✅ | ||
节点 | ✅ | X | ||||
路由器 | ✅ | X | ✅ | |||
会话映射 | X | |||||
会话队列 | ✅ | X |
节点注册
将新节点注册到 Grid 的过程是轻量级的。
- 当节点启动时,它应该定期发出“心跳”事件。此心跳包含节点状态。
- 分配器侦听心跳事件。当它看到一个时,它会尝试
GET
节点的/status
端点。正是通过此信息设置 Grid。
分配器将使用相同的 /status
端点定期检查节点,但即使在启动后,节点也应继续发送心跳事件,以便没有 Grid 状态持久存储的分配器可以重新启动,并且(最终)将是最新的和正确的。
节点状态对象
节点状态是一个 JSON blob,包含以下字段
名称 | 类型 | 描述 |
---|---|---|
可用性 | 字符串 | 一个字符串,可以是 up 、draining 或 down 之一。重要的是 draining ,它表示不应将新会话发送到节点,并且一旦其上的最后一个会话关闭,节点将退出或重新启动。 |
externalUrl | 字符串 | Grid 中其他组件应连接到的 URI。 |
lastSessionCreated | 整数 | 上次在此节点上创建会话的纪元时间戳。如果所有其他条件都相同,分配器将尝试将新会话发送到空闲时间最长的节点。 |
maxSessionCount | 整数 | 尽管可以通过计算可用插槽的数量来推断会话计数,但此整数值用于确定在节点被视为“已满”之前应同时运行的最大会话数。 |
nodeId | 字符串 | 用于标识此节点实例的 UUID。 |
osInfo | 对象 | 一个包含 arch 、name 和 version 字段的对象。这由 Grid UI 和 GraphQL 查询使用。 |
slots | 数组 | 一个插槽对象数组(如下所述) |
version | 字符串 | 节点版本(对于 Selenium,这将与 Selenium 版本号匹配) |
建议在所有字段中都放置值。
插槽对象
插槽对象表示节点中的单个插槽。“插槽”是单个会话可以运行的地方。节点可能具有比其可以同时运行的更多插槽。例如,一个节点可能能够运行多达 10 个会话,但它们可以是 Chrome、Edge 或 Firefox 的任意组合;在这种情况下,该节点将指示“最大会话数”为 10,然后还说它有 10 个 Chrome 插槽、10 个 Edge 插槽和 10 个 Firefox 插槽。
名称 | 类型 | 描述 |
---|---|---|
id | 字符串 | 用于引用插槽的 UUID |
lastStarted | 字符串 | 上次插槽启动会话的时间,采用 ISO-8601 格式 |
stereotype | 对象 | 此插槽将匹配的最小功能集。一个最小的示例是 {"browserName": "firefox"} |
session | 对象 | 会话对象(见下文) |
会话对象
这表示插槽中正在运行的会话
名称 | 类型 | 描述 |
---|---|---|
capabilities | 对象 | 会话提供的实际功能。将与新会话命令的返回值匹配 |
startTime | 字符串 | 会话的开始时间,采用 ISO-8601 格式 |
stereotype | 对象 | 此插槽将匹配的最小功能集。一个最小的示例是 {"browserName": "firefox"} |
uri | 字符串 | 节点用于与会话通信的 URI |