多重工作器(实验性功能)

报告问题 查看源代码

本页面介绍了多路复用工作器、如何编写与多路复用兼容的规则,以及针对特定限制的解决方法。

多重工作器允许 Bazel 使用单个工作器进程处理多个请求。对于多线程工作器,Bazel 可以使用更少的资源来实现相同或更高的性能。例如,Bazel 可以让四个多路复用工作器与同一个工作器进程进行通信,然后这些工作器进程可以并行处理请求,而无需每个工作器一个工作器进程。对于 Java 和 Scala 等语言,这样可以节省 JVM 预热时间和 JIT 编译时间,并且通常允许在同一类型的所有工作器之间使用一个共享缓存。

概览

Bazel 服务器和工作器进程之间具有两层。对于可以并行运行进程的某些助记符,Bazel 会从工作器池获取 WorkerProxyWorkerProxy 将请求连同 request_id 一起按顺序转发到工作器进程,工作器进程会处理请求并将响应发送到 WorkerMultiplexer。当 WorkerMultiplexer 收到响应时,它会解析 request_id,然后将响应转发回正确的 WorkerProxy。与非多路复用工作器一样,所有通信都通过标准输入/输出完成,但该工具不能仅使用 stderr 来实现用户可见输出(请参阅下文)。

每个 worker 都有一个密钥。Bazel 使用密钥的哈希代码(由环境变量、执行根和助记符组成)来确定使用哪个 WorkerMultiplexer。如果具有相同的哈希代码,WorkerProxy 将与相同的 WorkerMultiplexer 通信。因此,假设环境变量和执行根在一次 Bazel 调用中相同,那么每个唯一的助记符只能有一个 WorkerMultiplexer 和一个工作器进程。工作器总数(包括常规工作器和 WorkerProxy)仍受 --worker_max_instances 的限制。

编写与多路复用兼容的规则

规则的工作器进程应该是多线程的,以便利用多路复用工作器。Protobuf 允许规则集解析单个请求,即使数据流中可能堆积多个请求也是如此。每当工作器进程从流解析请求时,都应在新线程中处理该请求。由于不同的线程可以完成并同时写入流,因此工作器进程需要确保以原子方式写入响应(消息不重叠)。响应必须包含它们所处理的请求的 request_id

处理多路复用输出

与单工工作器相比,多路复用工作器需要更加谨慎地处理其输出。发送到 stderr 的所有内容都将进入到一个与同一类型的所有 WorkerProxy 共享的单个日志文件中,在并发请求之间随机交错。虽然将 stdout 重定向到 stderr 是个好主意,但请勿将该输出收集到 WorkResponseoutput 字段中,因为这可能会导致用户被损坏的输出片段。如果您的工具仅将面向用户的输出发送到 stdoutstderr,则需要先更改该行为,然后才能启用多路复用工作器。

启用多路复用工作器

默认情况下,多重工作器处于停用状态。规则集可以通过在操作的 execution_requirements 中使用 supports-multiplex-workers 标记来开启多重工作器(就像 supports-workers 标记启用常规工作器一样)。与使用常规工作器时一样,您需要在规则集级别(例如 --strategy=[some_mnemonic]=worker)或通常在策略集级别(例如 --dynamic_local_strategy=worker,standalone)指定工作器策略。无需额外的标志,supports-multiplex-workers 优先于 supports-workers(如果两者都设置了)。您可以通过传递 --noworker_multiplex 在全局范围内关闭多路复用工作器。

建议规则集尽可能使用多路复用工作器,以降低内存压力并提高性能。不过,除非实现了多路复用沙盒,否则多路复用工作器目前与动态执行不兼容。若尝试通过动态执行来运行非沙盒化的多路复用工作器,系统会改为以静默方式使用沙盒化的单工工作器。

多重沙盒

您可以通过在 worker 实现中添加对多重工作器的显式支持来对其进行沙盒化。虽然可以通过在各自的沙盒中运行每个工作器进程来完成单工工作器沙盒,但多路复用工作器会在多个并行请求之间共享进程工作目录。要允许对多路复用工作器采用沙盒机制,工作器必须支持从每个请求中指定的子目录中读取数据以及向其中写入数据,而不是直接在其工作目录中执行读写操作。

为了支持多路复用沙盒,工作器必须使用 WorkRequest 中的 sandbox_dir 字段,并将其用作所有文件读写的前缀。虽然 argumentsinputs 字段与未经过沙盒化的请求相比保持不变,但实际输入是相对于 sandbox_dir 而言的。工作器必须转换在 argumentsinputs 中找到的文件路径,以便从此修改后的路径读取数据,并且还必须写入相对于 sandbox_dir 的所有输出。这包括“.”等路径,以及参数中指定的文件(如 "argfile" 参数)中的路径。

当工作器支持多重沙盒时,规则集可以通过向操作的 execution_requirements 添加 supports-multiplex-sandboxing 来声明此项支持。然后,如果传递 --experimental_worker_multiplex_sandboxing 标志或者工作器用于动态执行,则 Bazel 将使用多重沙盒。

沙盒化多重工作器的工作器文件仍然是相对于工作器进程的工作目录而言的。因此,如果某个文件既用于运行 worker 又用作输入,则必须在 flagfile 参数以及 toolsexecutablerunfiles 中同时将其指定为输入。