在程序执行中,经常会遇到一个大问题:执行超时而且无法退出。
有些超时现象不是仅仅通过线程的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