在某個Flask項目在做后端接口時需要設置超時響應,因為接口中使用爬蟲請求了多個網站,響應時間時長時短。
需要設置一個最大響應時間,時間內如果接口爬蟲沒跑完,直接返回請求超時。
方法1:使用線程控制
import requests, datetime, time
import threading
class MyThread(threading.Thread):
def __init__(self, target, args=()):
"""
why: 因為threading類沒有返回值,因此在此處重新定義MyThread類,使線程擁有返回值
此方法來源 https://www.cnblogs.com/hujq1029/p/7219163.html?utm_source=itdadao&utm_medium=referral
"""
super(MyThread, self).__init__()
self.func = target
self.args = args
def run(self):
# 接受返回值
self.result = self.func(*self.args)
def get_result(self):
# 線程不結束,返回值為None
try:
return self.result
except Exception:
return None
# 為了限制真實請求時間或函數執行時間的裝飾器
def limit_decor(limit_time):
"""
:param limit_time: 設置最大允許執行時長,單位:秒
:return: 未超時返回被裝飾函數返回值,超時則返回 None
"""
def functions(func):
# 執行操作
def run(*params):
thre_func = MyThread(target=func, args=params)
# 主線程結束(超出時長),則線程方法結束
thre_func.setDaemon(True)
thre_func.start()
# 計算分段沉睡次數
sleep_num = int(limit_time // 1)
sleep_nums = round(limit_time % 1, 1)
# 多次短暫沉睡並嘗試獲取返回值
for i in range(sleep_num):
time.sleep(1)
infor = thre_func.get_result()
if infor:
return infor
time.sleep(sleep_nums)
# 最終返回值(不論線程是否已結束)
if thre_func.get_result():
return thre_func.get_result()
else:
return"請求超時" #超時返回 可以自定義
return run
return functions
#接口函數
def a1():
print("開始請求接口")
#這里把邏輯封裝成一個函數,使用線程調用
a_theadiing = MyThread(target=a2)
a_theadiing.start()
a_theadiing.join()
#返回結果
a = a_theadiing.get_result()
print("請求完成")
return a
@limit_decor(3) #超時設置為3s 2s邏輯未執行完畢返回接口超時
def a2():
print("開始執行")
time.sleep(2)
print("執行完成")
a=2
return a
# 程序入口 未超時返回a的值 超時返回請求超時
if __name__ == '__main__':
a = a1() #調用接口(這里把函數a1看做一個接口)
print(a)
超時設置3s,線程調用函數運行2s,這里返回a的值2。
方法2:使用信號模塊signal(只能在unix系統使用)
signal負責在Python程序內部處理信號,典型的操作包括預設信號處理函數,暫停並等待信號,以及定時發出SIGALRM等。
要注意,signal包主要是針對UNIX平台(比如Linux, MAC OS),而Windows內核中由於對信號機制的支持不充分,所以在Windows上的Python不能發揮信號系統的功能。
信號是進程之間通訊的方式,是一種軟件中斷。一個進程一旦接收到信號就會打斷原來的程序執行流程來處理信號。
def set_timeout(num):
def wrap(func):
def handle(signum, frame): # 收到信號 SIGALRM 后的回調函數,第一個參數是信號的數字,第二個參數是the interrupted stack frame.
raise RuntimeError
def to_do(*args):
try:
signal.signal(signal.SIGALRM, handle) # 設置信號和回調函數
signal.alarm(num) # 設置 num 秒的鬧鍾
print('start alarm signal.')
r = func(*args)
print('close alarm signal.')
signal.alarm(0) # 關閉鬧鍾
return r
except RuntimeError as e:
return "超時啦"
return to_do
return wrap
@set_timeout(2) # 限時 2 秒超時
def connect(): # 要執行的函數
time.sleep(3) # 函數執行時間,寫大於2的值,可測試超時
return "完成"
if __name__ == '__main__':
a = connect()

