并行执行¶
1.3 新版功能.
默认情况下,Fabric 会默认 顺序 执行所有任务(详细信息参见 Execution strategy ),这篇文档将介绍 Fabric 如何在多个主机上 并行 执行任务,包括 Fabric 参数设置、任务独立的装饰器,以及命令行全局控制。
它是如何运转的¶
由于 Fabric 1.x 并不是完全线程安全(以及为了更加通用,任务函数之间并不会产生交互),该功能的实现是基于 Python multiprocessing 模块,它会为每一个主机和任务组合创建一个线程,同时提供了一个(可选的)弹窗用于阻止创建过多的进程。
举个例子,假设你正打算更新数台服务器上的 Web 应用代码,所有服务的代码都更新后开始重启服务器(这样代码更新失败的时候比较容易回滚)。你可能会写出下面这样的代码:
from fabric.api import *
def update():
with cd("/srv/django/myapp"):
run("git pull")
def reload():
sudo("service apache2 reload")
在三台服务器上并行执行,就像这样:
$ fab -H web1,web2,web3 update reload
刚常见的情况是没有启动任何并行执行参数,Fabric 将会按顺序在服务器上执行:
在
web1
上更新
在
web2
上更新
在
web3
上更新
在
web1
上重新加载配置
在
web2
上重新加载配置
在
web3
上重新加载配置
如果激活并行执行(通过 -P
——下面会详细介绍)它将变成这样:
在
web1
、web3
和web3
上更新
在
web1
、web2
和web3
上重新加载配置
。
这样做的好处非常明显——如果 update
花费 5 秒 reload
花费 2 秒顺序执行总共会花费 (5+2)*3 = 21 秒,而并行执行只需要它的 1/3,也就是 (5+2) = 7 秒。
如何使用¶
装饰器¶
由于并行执行影响的最小单位是任务,所以功能的启用或禁用也是以任务为单位使用 parallel
或 serial
装饰器。以下面这个 fabfile 为例:
from fabric.api import *
@parallel
def runs_in_parallel():
pass
def runs_serially():
pass
如果这样执行:
$ fab -H host1,host2,host3 runs_in_parallel runs_serially
将会按照这样的流程执行:
runs_in_parallel
运行在host1
、host2
和host3
上runs_serially
运行在host1
上runs_serially
运行在host2
上runs_serially
运行在host3
上
命令行参数¶
你也可以使用命令行选项 -P
或者环境变量 env.parallel <env-parallel>强制所有任务并行执行。不过被装饰器 `~fabric.decorators.serial 封装的任务会忽略该设置,仍旧保持顺序执行。
例如,下面的 fabfile 会产生和上面同样的执行顺序:
from fabric.api import *
def runs_in_parallel():
pass
@serial
def runs_serially():
pass
在这样调用时:
$ fab -H host1,host2,host3 -P runs_in_parallel runs_serially
和上面一样,runs_in_parallel
将会并行执行,runs_serially
顺序执行。
bubble 大小¶
主机列表很大时,用户的机器可能会因为并发运行了太多的 Fabric 进程而被压垮,因此,你可能会选择 moving bubble 方法来限制 Fabric 并发执行的活跃进程数。
默认情况下没有使用 bubble 限制,所有主机都运行在并发池中。你可以在任务级别指定 parallel
的关键字参数 pool_size
来覆盖该设置,或者使用选项 -z
全局设置。
例如同时在 5 个主机上运行:
from fabric.api import *
@parallel(pool_size=5)
def heavy_task():
# lots of heavy local lifting or lots of IO here
或者不使用关键字参数 pool_size
:
$ fab -P -z 5 heavy_task