[python] 方法超时处理的修饰器

在程序执行中,经常会遇到一个大问题:执行超时而且无法退出。

有些超时现象不是仅仅通过线程的interrupt能够搞定的,比如:网络传输路由中MTU值的不一致,会导致你对目标服务器的请求挂起,比如一个http get,或者是一个数据库连接的发起动作,这时候,基于block io的程序设置的timeout是无效的。

再比如非网络情况的例子:数据读取的数量太大,在网络传输过慢的情况下,也会等待很久。这时候,是找不到一个能够break point去终止执行的。

解决办法的一种是:ps + kill -9

这就要求:目标方法(有时间限制的)在一个独立的进程中执行,超时将该进程杀死。

当然,尤其要注意的是,这个操作是很暴力的,没有对资源进行释放,以及对已进行的数据操作进行回滚。

因此,在使用这种超时策略的时候,要充分考虑到方法执行的入点和出点,以及炒作中断所造成的影响以及修复。

这里其实要处理到的问题,跟实时系统一样的,就是:可预测的——在可预计的时间内给出结果(正确或者失败)。实时系统的重点不在于速度而在于可预测(精度和限度)。

可以很明确的说,java现有的版本是不支持这种情况的。realtime java也已经关闭好久了。不在JVM的实现上做文章,是处理不了这个问题的。

当然,subprocess的思路java也有,但是比较简陋,数据的交换需要通过stdin/stdout,相较之下,python通过共享内存在进程间通讯的方式要简单和优雅许多。

因此,以下用python实现了一个 限制执行时间、进行重试的 方法工具。


import multiprocessing

class TaskletTimeoutError(Exception) : 
    pass

class tasklet(object) :

    def __init__(self, timeout = 60, maxretry = 1) :
        self.timeout = timeout
        self.maxretry = maxretry

    def __call__(self, original_func) :
        decorator_self = self

        def wrappee(*args, **kwargs) :

            count = decorator_self.maxretry
            q = multiprocessing.Queue()
            q.put(None)

            real_args = args
            real_kwargs = kwargs

            def __run(func, real_args, real_kwargs) :
                result = original_func(*real_args, **real_kwargs)
                q.get()
                q.put(result)

            while count > 0 :
                proc_args = (original_func, real_args, real_kwargs)
                proc_task = multiprocessing.Process(target = __run, args = proc_args)
                proc_task.start()

                proc_task.join(decorator_self.timeout)
                if proc_task.is_alive() :
                    proc_task.terminate()
                    count -= 1
                    continue

                return q.get()

            raise TaskletTimeoutError(u'timeout : %d seconds X %d retry times.' \
                    % (decorator_self.timeout, decorator_self.maxretry))

        return wrappee


@tasklet(timeout = 1, maxretry = 2)
def haha(num) :
    import time
    time.sleep(5)
    print 'haha'
    return '-' * num

try :
    print haha(20)
except TaskletTimeoutError, e :
    print e


你可能感兴趣的:(杂记)