依赖
- Python3.7以上,需要使用asyncio这个库。
- airflow 版本需要在2.2以上。
- 需要启动triggerer这个airflow进程。
使用
新加自己的DeferrableOperators,一般要经过三步:
- 还是继承原来的BaseSensorOperator。
from airflow.sensors.base import BaseSensorOperator
class ExecutionDateTimeSensorAsync(BaseSensorOperator):
def __init__(self, *, target_time, **kwargs):
super().__init__(**kwargs)
self.daily_target_time = target_time
def init_real_execution_datetime(self, context):
time_list = self.daily_target_time.split(':')
if len(time_list) != 2:
raise
hour = int(time_list[0])
minute = int(time_list[1])
utc_target_datetime = datetime.strptime(self.daily_target_time, "%H:%M")
china_target_datetime = utc_target_datetime.replace(tzinfo=dateutil.tz.gettz('Asia/Shanghai'))
execution_date_str = (context['execution_date'] + timedelta(days=1)).strftime("%Y-%m-%d")
real_date_str = execution_date_str + ' ' + self.daily_target_time
real_execution_datetime = datetime.strptime(real_date_str, "%Y-%m-%d %H:%M")
def execute(self, context):
self.init_real_execution_datetime(context)
self.defer(
trigger=ExecutionDateTimeTrigger(moment=self.real_execution_datetime),
method_name="execute_complate"
)
def execute_complete(self, context, event=None):
# 可以做一些额外的工作,发邮件、告警之类,return之后就会把这个Sensor Mark Success了。
return
- 新加一个自己的Trigger
最恶心人的地方是加Trigger,需要把新加的trigger放到一个系统路径下,serialize这个函数返回的元组的第一个参数必须填写这个路径,trigger这个进程才能找到trigger的定义去实例化这个trigger。
from airflow.triggers.base import BaseTrigger, TriggerEvent
import pytz
utc = pytz.UTC
class ExecutionTimeTrigger(BaseTrigger):
def __init__(self, moment):
super().__init__()
self.moment = moment.replace(tzinfo=utc)
def serialize(self):
return ("airflow.triggers.temporal.ExecutionTimeTrigger", {"moment": self.moment})
async def run(self):
self.log.info('--- {} ---'.format(self.moment))
while (self.moment > datetime.datetime.now().replace(tzinfo=utc)):
await asyncio.sleep(1)
yield TriggerEvent(self.moment)
- airflow启动triggerer这个进程
airflow triggerer
效果
如果使用原生的TimeSensor之类的Sensor,如果这个TimeSensor前置任务已经好了的话,状态马上就会被置成SCHEDULEABLE并且被executor弄成RUNNING状态,占用worker进程资源。
在使用原生的Sensor,并行150个任务的时候,会占用13G左右的内存,并且消耗CPU在无意义的轮询中。
使用Deferrable Operators和Trigger之后,Triggerer这个进程会帮我们做掉这种无意义的轮询。当Trigger里面的run函数真正yield的时候就是这个trigger完成的时候,会把这个任务置成Success状态,跟普通的Sensor行为一致。之后所有的这种时间等待、文件等待任务都可以使用新的Deferrable Operators来完成,但是需要写新的Trigger逻辑。
用新的Deferrable Operators之后,额外使用的内存只有0.2G左右。节省了很多worker、内存、cpu资源。