背景
需求背景:
最近收到一个需求,需要为某个接口实现超时检测功能。由于计算量和服务问题,该接口调用返回的时间可能出现过长的情况。需求方希望设定一个超时参数,当函数调用时间超时则返回一个超时的json数据,未超时则正常返回数据。
最终我使用了多线程 + 队列Queue对需求进行了实现,故总结一下本次实现的方法和思路。
原始代码模拟
使用如下代码最简单地还原一下原始接口情景,该代码下,调用task()方法可能存在随机的5~20秒延时才能返回:
import time
import random
# 该接口调用时间可能为5秒到20秒,希望能有一个接口超时检测功能。
# 若延迟在10秒内,则返回正常信息;若延迟大于10秒,则立即返回一个超时的信息。
def task():
print("start task...")
start = time.time()
# 模拟任务随机耗时
time.sleep(random.randint(5, 20))
end = time.time()
print("该次接口调用耗时:{}s".format(end-start))
return {"status": "succeed"}
if __name__ == '__main__':
result = task()
print("返回结果为:{}".format(result))
结果示例,返回结果的时间可能小于10秒也可能大于十秒:
# 结果1
start task...
该次接口调用耗时:16.003031253814697s
返回结果为:{'status': 'succeed'}
# 结果2
start task...
该次接口调用耗时:7.378031253887643s
返回结果为:{'status': 'succeed'}
代码改造,超时监听
在主函数中,实例化一个队列并开启一个多线程任务,使用额外线程运行原有函数中的耗时代码部分,并将最终结果添加到主函数中的队列中。在主线程内,使用get阻塞获取队列中的结果,并传入超时参数,若在超时限制内获取到了结果,则正常返回;若触发了超时异常,则返回超时结果。无论怎样,主线程都将在规定Timeout内进行返回。
代码示例如下:
# 将耗时部分抽离为单独的函数,并接受一个队列参数,该队列为主函数中实例化的队列对象,将最终结果put到该队列中
def time_consuming_code(q):
start = time.time()
time.sleep(random.randint(5, 20))
res = {"status": "succeed"}
q.put(res)
end = time.time()
print("耗时部分后台线程耗时:{}".format(end-start))
def task(timeout=10):
print("start task...")
start = time.time()
# 实例化一个队列
q = Queue()
# 开启新线程运行耗时部分代码
t = Thread(target=time_consuming_code, args=(q,))
# 开启主线程守护,主线程结束则关闭子线程
t.daemon = True
t.start()
# 阻塞超时时间获取队列中的结果,若时间限制内未获取,则捕获异常,返回超时json信息
try:
res = q.get(timeout=timeout)
except Empty:
res = {"status": "timeout"}
end = time.time()
print("该次接口调用耗时:{}s".format(end - start))
return res
if __name__ == '__main__':
result = task()
print("返回结果为:{}".format(result))
输出示例:
主接口耗时最多只会是10秒:
# 正常返回结果:
start task...
耗时部分后台线程耗时:8.000471115112305
该次接口调用耗时:8.000840902328491s
返回结果为:{'status': 'succeed'}
#超时返回结果,超时后耗时任务的子线程被强制关闭,因此看不到子线程耗时输出:
start task...
该次接口调用耗时:10.005242109298706s
返回结果为:{'status': 'timeout'}
总结
以上便是该次利用队列的阻塞获取+多线程实现的超时检测代码demo,你可以将timeout参数单独剥离为配置设置,控制超时设定。
希望能对看了文章的你提供思路。
网友评论