本页介绍了多路复用工作器、如何编写与多路复用兼容的规则,以及针对某些限制的权宜解决方法。
借助多工人,Bazel 可以使用单个工作进程处理多个请求。对于多线程工作器,Bazel 可以使用更少的资源来实现相同或更好的性能。例如,Bazel 可以有四个多路复用工作器与同一个工作器进程通信,而不是每个工作器都有一个工作器进程,然后该进程可以并行处理请求。对于 Java 和 Scala 等语言,这可以节省 JVM 预热时间和 JIT 编译时间,并且通常允许在同一类型的所有工作器之间使用一个共享缓存。
概览
Bazel 服务器和工作器进程之间有两层。对于可以并行运行进程的某些助记符,Bazel 会从工作器池中获取 WorkerProxy
。WorkerProxy
会将请求连同 request_id
依次转发给 worker 进程,worker 进程会处理请求并将响应发送给 WorkerMultiplexer
。当 WorkerMultiplexer
收到响应时,它会解析 request_id
,然后将响应转发回正确的 WorkerProxy
。与非复用工作器一样,所有通信都是通过标准输入/输出完成的,但该工具不能仅使用 stderr
来生成可见的用户输出(见下文)。
每个工作器都有一个键。Bazel 使用键的哈希代码(由环境变量、执行根目录和助记符组成)来确定要使用的 WorkerMultiplexer
。如果 WorkerProxy
具有相同的哈希代码,则会与同一 WorkerMultiplexer
通信。因此,假设单个 Bazel 调用中的环境变量和执行根目录相同,则每个唯一的记号只能有一个 WorkerMultiplexer
和一个 worker 进程。工作器总数(包括常规工作器和 WorkerProxy
)仍受 --worker_max_instances
的限制。
编写可与多路复用兼容的规则
规则的工作器进程应采用多线程方式,以充分利用多工器。Protobuf 允许规则集解析单个请求,即使数据流中可能有多个请求堆积也是如此。每当工作器进程解析来自数据流的请求时,都应在新线程中处理该请求。由于不同的线程可能会同时完成并写入到流中,因此工作器进程需要确保响应以原子方式写入(消息不会重叠)。响应必须包含其所处理请求的 request_id
。
处理多路复用输出
与单工序工作器相比,多工序工作器在处理输出时需要更加小心。发送到 stderr
的所有内容都将进入一个日志文件,该日志文件由所有同类型的 WorkerProxy
共享,并在并发请求之间随机交错。虽然将 stdout
重定向到 stderr
是个好主意,但请勿将该输出收集到 WorkResponse
的 output
字段中,因为这可能会向用户显示经过混淆处理的输出片段。如果您的工具仅将面向用户的输出发送到 stdout
或 stderr
,则您需要先更改此行为,然后才能启用多路复用 worker。
启用多工器
多路复用工作器默认处于停用状态。规则集可以通过在操作的 execution_requirements
中使用 supports-multiplex-workers
标记来启用多路复用工作器(就像 supports-workers
标记启用常规工作器一样)。与使用常规工作器一样,您需要在规则集级别(例如 --strategy=[some_mnemonic]=worker
)或通常在策略级别(例如 --dynamic_local_strategy=worker,standalone
)指定工作器策略。无需其他标志,如果同时设置了 supports-multiplex-workers
和 supports-workers
,则 supports-multiplex-workers
优先于 supports-workers
。您可以通过传递 --noworker_multiplex
来全局关闭多工器。
建议规则集尽可能使用多工工作器,以减少内存压力并提高性能。不过,多路复用工作器目前不支持动态执行,除非它们实现了多路复用沙盒。尝试使用动态执行运行非沙盒化多工序工作器时,系统会改为静默使用沙盒化单工序工作器。
多重沙盒
通过在工作器实现中添加对多路复用工作器的明确支持,可以将多路复用工作器沙盒化。虽然单工序工作器沙盒化可以通过在各自的沙盒中运行每个工作器进程来实现,但多工序工作器会在多个并行请求之间共享进程工作目录。为了允许对多工工作器进行沙盒化处理,工作器必须支持从每个请求中指定的子目录读取和写入,而不是直接在其工作目录中读取和写入。
为了支持多重沙盒化,工作器必须使用 WorkRequest
中的 sandbox_dir
字段,并将其用作所有文件读写的前缀。虽然 arguments
和 inputs
字段与未沙盒化请求保持不变,但实际输入相对于 sandbox_dir
。worker 必须转换 arguments
和 inputs
中找到的文件路径,以便从此经过修改的路径读取,并且还必须相对于 sandbox_dir
写入所有输出。这包括“.”等路径,以及在参数中指定的文件中找到的路径(例如 “argfile” 参数)。
当 worker 支持多重沙盒化后,规则集可以通过向操作的 execution_requirements
添加 supports-multiplex-sandboxing
来声明此支持。然后,如果传递了 --experimental_worker_multiplex_sandboxing
标志,或者将 worker 与动态执行搭配使用,Bazel 将使用多重沙盒化。
沙盒化多路复用工作器的工作器文件仍然相对于工作器进程的工作目录。因此,如果某个文件既用于运行 worker 又用作输入,则必须在 flagfile 参数和 tools
、executable
或 runfiles
中将其同时指定为输入。