本节介绍husky的架构,其中包括前端和后端。
在PyHusky前端,几乎所有的运算符如map,flat_map和reduce 等都属于DAG的节点,定义了对PyHuskyList中objects的转换。例如, b = a.map(func)
将从PyHuskyList a 返回一个新的PyHuskyList b。另外一些操作,如cache(),count(),collect()都只是对现有PyHuskyList操作,他们并不在DAG中创建新的节点。
例如,代码wc.py:
a = ["hello", "world", "hello", "husky"] words = ph.env.parallelize(a) wc = words.map(lambda x:(x,1)).reduce_by_key(lambda x,y:x+y).collect()
会创建如下的DAG:
words (parallelize_py) | tmp1 (map_py) | tmp2 (reduce_by_key_py) (collect_py)
PyHusky采取lazy evaluation。,只有Actions才会真正触发计算。 Actions 包括 reduce(func)
,cache()
,uncache()
,write_to_hdfs(url)
,count()
和 collect()
。
所有的 Actions 调用在frontend/scheduler.py compute()
或者 compute_collect()
方法以触发计算。scheduler首先序列化DAG,然后发送DAG到master并等待结果。
The Pyhusky backend consists of three parts: master who splits and distributes the DAG, C++ backend and python backend. Pyhusky后端由三部分组成:Master(分裂并分配DAG),C ++后端和Python后端。
Master负责从PyHusky前端接收DAG,分裂DAG,并分配分裂后的DAG到不同机器的Daemon上。
Master从PyHusky前端接收DAG,分裂DAG为多个任务,然后存储这些任务。
Daemons向Master请求新任务。如果有待完成的任务,Master就会分配该任务给该Daemon。
当某个Daemon完成一个任务后,它就会通知Master。Master会收集结果并跟踪其进展。
每台机器都运行一个Daemon进程。每一个Daemon进程有两层,即进程层和线程层。我们分别命名为DaemonDriver和WorkerDriver。
DaemonDriver向Master请求任务。当它接收到一个新的会话时,它使用husky元进行全局的初始化,并生成线程(WorkerDrivers)。当它接收到一个任务时,它将任务分配到WorkerDrivers并等待WorkerDrivers返回的结果。
WorkerDriver从DaemonDriver接收到任务。会话开始时,它会创建一个Python进程(每个WorkerDriver对应一个python进程)。它还指派任务给python进程,然后等待该进程的处理结果,并做相应的操作。
PyHusky Backend is responsible for handling PyHusky operators (e.g. to execute lambda on tuples). It receives DAGs from WorkerDriver and traverses the DAGs. It mainly contains three parts invoking developer-defined functions PyHusky后端从WorkerDriver接收并遍历DAGs,负责处理PyHusky的操作(例如,根据lambda处理每个元组)。后端主要任务是调用开发者定义的三个函数(prefunc()
,func()
,和postfunc()
)。
1.接收到一个DAG后,PyHusky后端会遍历该DAG,并给每个遍历到的操作运用prefunc()。 Prefunc()主要是做一些初始化的工作。例如,对于map_py操作,prefunc()需要反序列化其lambda以便后面的操作可使用这个lambda。
遍历DAG之后,对于各数据分片中的每个元组,PyHusky后端再次遍历DAG并运用func(),使得多个操作可以被同时执行(也就是pipeline,流水线处理)。例如,对于DAG load_py-> map_py-> flat_map_py-> count_py,PyHusky将各个数据分区的每个元组以流的形式通过DAG,以避免因缓存中间结果而占用大量内存。
处理完所有元组后,类似步骤1,PyHusky后端再一次遍历DAG,为每个操作运用postfunc(),做一些定稿工作。例如,对于write_to_hdfs_py操作,postfunc()实际上是发送输出到C++后端,然后C++后端将输出写入到HDFS。