简单队列系统RQ-深入理解(四)

380 阅读3分钟

Python高阶使用

Python是语法糖很多的高级语言,而且很多是Python语言独有的。RQ作为流行的分布式任务框架,也使用了Python的很多语法糖,通过阅读源码帮助理解如何更好的使用这些语法糖以及魔术方法。

propery

  • 描述:@property 语法糖提供了比 property() 函数更简洁直观的写法。被 @property 装饰的方法是获取属性值的方法,被装饰方法的名字会被用做 属性名。被 @属性名.setter 装饰的方法是设置属性值的方法。被 @属性名.deleter 装饰的方法是删除属性值的方法。
## Job

    @property
    def func_name(self):
        return self._func_name

    @property
    def func(self):
        func_name = self.func_name
        if func_name is None:
            return None

        module_name, func_name = func_name.rsplit('.', 1)
        module = importlib.import_module(module_name)
        return getattr(module, func_name)

    @property
    def args(self):
        return self._args

    @property
    def kwargs(self):
        return self._kwargs
  • 总结:@property用于封装复杂的细节,自定义获取属性,设置属性,删除属性操作行为。

装饰器

装饰器除了用于方法还可以用于类,对方法进行装饰和对类进行装饰。

@total_ordering
class Queue(object):
    redis_queue_namespace_prefix = 'rq:queue:'


def total_ordering(cls):
    """Class decorator that fills in missing ordering methods"""
    # Find user-defined comparisons (not those inherited from object).
    roots = {op for op in _convert if getattr(cls, op, None) is not getattr(object, op, None)}
    if not roots:
        raise ValueError('must define at least one ordering operation: < > <= >=')
    root = max(roots)       # prefer __lt__ to __le__ to __gt__ to __ge__
    for opname, opfunc in _convert[root]:
        if opname not in roots:
            opfunc.__name__ = opname
            setattr(cls, opname, opfunc)
    return cls

上下文管理器

上下文管理器是Python特有的,主要是为了简化代码操作,使得代码变得简洁便于维护,减少冗余代码,复用性更强。

## 原始写法
try:
    VAR = XXXXX
except xxx as e:
    PASS
finally:
    DO SOMETHING

## 上下文管理器写法
with EXPR as VAR:
    BLOCK

这里就是一个标准的上下文管理器的使用逻辑,稍微解释一下其中的运行逻辑:

(1)执行EXPR语句,获取上下文管理器(Context Manager)

(2)调用上下文管理器中的__enter__方法,该方法执行一些预处理工作。

(3)这里的as VAR可以省略,如果不省略,则将__enter__方法的返回值赋值给VAR。

(4)执行代码块BLOCK,这里的VAR可以当做普通变量使用。

(5)最后调用上下文管理器中的的__exit__方法。

(6)exit__方法有三个参数:exc_type, exc_val, exc_tb。如果代码块BLOCK发生异常并退出,那么分别对应异常的type、value 和 traceback。否则三个参数全为None。如果__exit()方法返回值为false,则异常会被重新抛出;如果其返回值为true,则视为异常已经被处理,程序继续执行。

class death_pentalty_after(object):
    def __init__(self, timeout):
        self._timeout = timeout

    def __enter__(self):
        self.setup_death_penalty()

    def __exit__(self, type, value, traceback):
        # Always cancel immediately, since we're done
        try:
            self.cancel_death_penalty()
        except JobTimeoutException:
            pass
        return False

    def handle_death_penalty(self, signum, frame):
        raise JobTimeoutException('Job exceeded maximum timeout '
                'value (%d seconds).' % self._timeout)

    def setup_death_penalty(self):
        signal.signal(signal.SIGALRM, self.handle_death_penalty)
        signal.alarm(self._timeout)

    def cancel_death_penalty(self):
        signal.alarm(0)
        signal.signal(signal.SIGALRM, signal.SIG_DFL)

RQ中使用上下文管理器用于作业的超时管理,配合signal模块完成作业的超时时间控制,开始时注册信号处理程序,设置定时信号,结束时取消定时信号并忽略信号,下面是具体使用的代码。

def perform_job(self, job):
    """Performs the actual work of a job.  Will/should only be called
    inside the work horse's process.
    """
    self.procline('Processing %s from %s since %s' % (
        job.func_name,
        job.origin, time.time()))

    try:
        with death_pentalty_after(job.timeout or 180):
            rv = job.perform()
    except Exception as e:
        fq = self.failed_queue
        self.log.exception(red(str(e)))
        self.log.warning('Moving job to %s queue.' % fq.name)

        fq.quarantine(job, exc_info=traceback.format_exc())
        return False

    if rv is None:
        self.log.info('Job OK')
    else:
        self.log.info('Job OK, result = %s' % (yellow(unicode(rv)),))

    if rv is not None:
        p = self.connection.pipeline()
        p.hset(job.key, 'result', dumps(rv))
        p.expire(job.key, self.rv_ttl)
        p.execute()
    else:
        # Cleanup immediately
        job.delete()