【问题标题】:Job Scheduler - YAML for writing job definition?Job Scheduler - 用于编写作业定义的 YAML?
【发布时间】:2018-03-05 17:22:58
【问题描述】:

在我们的旧作业调度软件(基于 crontab 构建)中,我们使用 apache 配置格式 (parser) 来编写作业定义,并且我们使用 perl config general 来解析配置文件。该软件是高度定制的,并且具有诸如在检查是否满足该命令的依赖性后在我的作业中运行命令、在命令失败时重新安排作业、支持自定义通知等功能。

我们现在计划用 python 重写这个软件,并考虑使用 YAML 代替 apache 配置来编写作业定义。 YAML 适合编写这样的动态配置吗?

工作定义示例(每天凌晨 2 点运行此工作,检查是否是星期二而不是印度的假期,如果是,请预订我的航班并发送通知):

// python function to check if it is tuesday
checkIfTuesdayAndNotHoliday()

<job> 
    calendar: indian

        <dependency: arbitrary_python_code: checkIfTuesdayAndNotHoliday()>
        <command>  
            check availability of flight
        </command>

        <success: notify: email: agrawall/>
        <failure: notify: email: ops>
        <command>
            some command to book my flight
        </command>
</job>

<crontab> 0 2 * * * </crontab>

我很难理解我应该使用什么格式来定义作业(YAML、Apache Config、XML、JSON 等)。请注意,此作业定义将在我的 python 脚本中转换为作业对象。

我们目前使用的 perl 中的 Apache 配置解析器 https://metacpan.org/source/TLINDEN/Config-General-2.63/General.pm#L769

我们计划在 python 中使用 Apache 配置解析器https://github.com/etingof/apacheconfig

【问题讨论】:

  • 您的 Job 对象的格式(类)是什么?
  • 解析器将此配置转换为字典,然后我们从字典构建作业对象
  • 所以,你可以把这个字典当作配置文件使用。
  • 你能给我们举个这个目录的例子吗?
  • 它只是一个 python 字典,{"python": //code as a string, "job": //job definition, "crontab":...}。这是我们在python中使用的解析器。 github.com/etingof/apacheconfig

标签: python yaml airflow job-scheduling luigi


【解决方案1】:

基于 Python 的配置文件至少在 Python 1.6 中以 distutils'setup.py 的形式存在(即 2000 年之前)。使用这种格式的主要缺点是很难以编程方式更新配置中的值。即使您只想制作一些额外的实用程序来分析这些文件,您甚至必须特别注意您可以在不执行代码的情况下导入这样的配置文件,而且也不需要通过导入来拉入各种依赖项。这可以通过使用if __name__ == '__main__': 来实现,或者更容易通过将配置信息作为文件中的数据结构来实现。

因此,如果更新文件永远不会成为问题,那么您可以使用基于 Python 的数据结构,并且这些数据结构非常易读。

XML 和 JSON 不是手动编辑的好格式。 XML 有很多 &lt;&gt; 可以在没有特殊工具的情况下轻松输入。 JSON 有很多双引号,难以阅读,但它也存在各种问题,因为 JSON 不允许在数组和对象中使用尾随逗号,从而导致人们编写如下对象:

{ 
    "a": 1
  , "b": 2
}

这可以防止您删除最后一行并忘记删除分隔键/值对的逗号,但 IMO 可读性不同。

另一方面,YAML 可以写得很可读,但是在编辑文件时必须考虑一些规则。在我的回答here 中,我展示了一些可以包含在 YAML 文件中的基本规则,编辑者在编辑时需要考虑这些规则。 YAML 可以被 Python 以外的其他语言读取(使用基于 Python 的配置文件很难做到这一点)。

您可以使用 YAML 标签(以及与这些标签关联的适当 Python 对象),因此您不必依赖从某个键值对中解释键来理解值的含义:

- !Job
  calendar: !Calendar indian
  dependency: !Arbitrary_python_code checkIfTuesdayAndNotHoliday()
  command: !CommandTester
     exec: !Exec check availability of flight
     success: !Commands
       - !Notify 
          email: agrawall
       - !Exec some command to book my flight
     failure: !Commands
       - !Notify 
           email: ops

(底部是与这些标签关联的类的部分示例实现)

当您使用 ruamel.yaml(免责声明:我是该软件包的作者)时,即使不丢失 cmets、密钥排序、标签,也可以通过编程方式更新 YAML。


我一直在参数化我的 Python 打包(我管理超过 100 个包,其中一些在 PyPI 上,其他仅用于特定客户端),通过从每个包的__init__.py 文件。我已经尝试插入 Python 的 JSON 子集,但最终开发了 PON(Python 对象表示法),它可以很容易地被 setup.py 解析,而无需在 AST 上导入带有小(100 行)扩展名的 __init__.py 文件literal_eval 包含在 Python 标准库中。

PON 可以在没有任何库的情况下使用(因为它是 Python 数据结构的子集,包括 dict、list、set、tuple 和基本类型,如整数、浮点数、布尔值、字符串、日期、日期时间。因为它基于AST 评估器,您可以在配置文件中进行计算 (secs_per_day = 24 * 60 * 60) 和其他评估。

PON 自述文件还更详细地描述了该格式相对于 YAML、JSON、INI、XML 的优点(和缺点)。

使用配置数据不需要 PON 包,仅当您想对 PON 数据进行编程往返(加载-编辑-转储)时才需要。


import sys
from ruamel.yaml import YAML, yaml_object

yaml = YAML()

@yaml_object(yaml)
class CommandTester:
    yaml_tag = u'!CommandTester'

    def __init__(self, exec=None, success=None, failure=None):
        self.exec = exec
        self.success = success
        self.failure = failure

    def __call__(self):
        if self.exec():
            self.success()
        else:
            self.failure()

@yaml_object(yaml)
class Commands:
    """a list of commands"""
    yaml_tag = u'!Commands'

    def __init__(self, commands):
        self._commands = commands  # list of commands to execute

    @classmethod
    def from_yaml(cls, constructor, node):
        for m in yaml.constructor.construct_yaml_seq(node):
            pass
        return cls(m)

    @classmethod
    def to_yaml(cls, representer, node):
        return representer.represent_sequence(cls.yaml_tag, node._commands)

    def __call__(self, verbose=0, stop_on_error=False):
        res = True
        for cmd in self._cmd:
            try:
                res = subprocess.check_output(cmd)
            except Exception as e:
                res = False
                if stop_on_error:
                    break
            return res

@yaml_object(yaml)
class Command(Commands):
    """a single command"""
    yaml_tag = u'!Exec'

    def __init__(self, command):
        Commands.__init__(self, [command])

    @classmethod
    def from_yaml(cls, constructor, node):
        return cls(node.value)

    @classmethod
    def to_yaml(cls, representer, node):
        return representer.represent_scalar(cls.yaml_tag, node._commands[0])


@yaml_object(yaml)
class Notifier:
    yaml_tag = u'!Notify'

with open("job.yaml") as fp:
    job = yaml.load(fp)

yaml.dump(job, sys.stdout)

【讨论】:

  • 感谢您的见解,但我仍然看不到从 apache 配置格式迁移到 yaml 甚至 python 配置的任何优势。 yaml 是否有高级语法支持(如一些条件逻辑和循环)?
  • @LokeshAgrawal 我用一个更具体的例子更新了我的答案。这是否有利当然部分取决于品味和您的习惯。
  • @LokeshAgrawal 您还应该注意,您要使用的 Apache 配置库还没有使用表达式标签。我不确定添加起来有多容易,但是使用标签和适当的对象,您至少可以像 Apache 在 YAML 中提供的一样多。
  • 所以您的底线建议是使用 PON 格式? PON/YAML 是否支持表达式标签?我也不能使用 jinja.pocoo.org 之类的东西以及 apache 配置格式并为其编写自定义解析器吗?
  • PON 不使用标签,但会评估表达式。在 YAML 中,您可以轻松地将任何表达式评估作为与标签关联的对象的一部分(在创建期间,惰性,任何您喜欢的方式)。
【解决方案2】:

新趋势是使用 Python 文件作为配置。这就是在 Django 和 Flask 中所做的。它是人类可读的,易于定义和更新,当然也可以直接转换为 Python 对象。

另请参阅“Pros and cons for different configuration formats?” 的已接受答案。

另见这篇文章“Configuration files in Python”

这是一个示例 (setting.py):

def check_if_tuesday_and_not_holiday():
    """check if it is tuesday and not holiday"""
    return True

JOB = {
    'calendar': 'indian',
    'dependency': {
        'arbitrary_python_code': check_if_tuesday_and_not_holiday  # callback
    },
    'command': 'check availability of flight',
    'success': {
        'notify': {
            'email': 'agrawall'
        },
        'command': 'some command to book my flight'
    },
    'failure': {
        'notify': {
            'email': 'ops'
        }
    }
}

CRONTAB = '0 2 * * *'

注意:我不确定你的配置文件是否看懂,所以我尽量把它适配到 Python...

【讨论】:

  • 这里的问题是有时候写作业的用户技术不太好,他们可能不懂如何用python写代码。因此,我更喜欢 YAML 或 Apache Config 类型的配置文件。
  • 这没什么用,因为它不能在作业对象内动态插入值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多