Django異步任務線程池實現(xiàn)原理
這篇文章主要介紹了Django異步任務線程池實現(xiàn)原理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
當數(shù)據(jù)庫數(shù)據(jù)量很大時(百萬級),許多批量數(shù)據(jù)修改請求的響應會非常慢,一些不需要即時響應的任務可以放到后臺的異步線程中完成,發(fā)起異步任務的請求就可以立即響應
選擇用線程池的原因是:線程比進程更為可控。不像子進程,子線程會在所屬進程結束時立即結束。線程可共享內存。
請求任務異步處理的原理
使用python manage.py runserver模式啟動的Django應用只有一個進程,對于每個請求,主線程會開啟一個子線程來處理請求。請求子線程向主線程申請一個新線程,然后把耗時的任務交給新線程,自身立即響應,這就是請求任務異步處理的原理。
可視化線程池
如果想要管理這批異步線程,知道他們是否在運行中,可以使用線程池(ThreadPoolExecutor)。
線程池會先啟動若干數(shù)量的線程,并讓這些線程都處于睡眠狀態(tài),當向線程池submit一個任務后,會喚醒線程池中的某一個睡眠線程,讓它來處理這個任務,當處理完這個任務,線程又處于睡眠狀態(tài)。
submit任務后會返回一個期程(future),這個對象可以查看線程池中執(zhí)行此任務的線程是否仍在處理中
因此可以構建一個全局可視化線程池:
from concurrent.futures.thread import ThreadPoolExecutor
class ThreadPool(object):
def __init__(self):
# 線程池
self.executor = ThreadPoolExecutor(20)
# 用于存儲每個項目批量任務的期程
self.future_dict = {}
# 檢查某個項目是否有正在運行的批量任務
def is_project_thread_running(self, project_id):
future = self.future_dict.get(project_id, None)
if future and future.running():
# 存在正在運行的批量任務
return True
return False
# 展示所有的異步任務
def check_future(self):
data = {}
for project_id, future in self.future_dict.items():
data[project_id] = future.running()
return data
def __del__(self):
self.executor.shutdown()
# 主線程中的全局線程池
# global_thread_pool的生命周期是Django主線程運行的生命周期
global_thread_pool = ThreadPool()
使用:
# 檢查異步任務 if global_thread_pool.is_project_thread_running(project_id): raise exceptions.ValidationError(detail='存在正在處理的批量任務,請稍后重試') # 提交一個異步任務 future = global_thread_pool.executor.submit(self.batch_thread, project_id) global_thread_pool.future_dict[project_id] = future # 查看所有異步任務 @login_required def check_future(request): data = global_thread_pool.check_future() return HttpResponse(status=status.HTTP_200_OK, content=json.dumps(data))
串行執(zhí)行
使用線程鎖
在全局線程池中初始化線程鎖
class ThreadPool(object):
def __init__(self):
self.executor = ThreadPoolExecutor(20)
self.future_dict = {}
self.lock = threading.Lock()
然后執(zhí)行線程前需要獲取鎖并再執(zhí)行結束后釋放鎖
def batch_thread(self):
global_thread_pool.lock.acquire()
try:
...
global_thread_pool.lock.release()
except Exception:
trace_log = traceback.format_exc()
logger.error('異步任務執(zhí)行失敗:\n %s' % trace_log)
global_thread_pool.lock.release()
需要捕捉異常預防子線程出錯而無法釋放鎖的情況
異步線程任務執(zhí)行前先檢查數(shù)據(jù)庫連接是否可用,然后關掉不可用連接
由于django的數(shù)據(jù)庫連接是保存到線程本地變量中的,通過ThreadPoolExecutor創(chuàng)建的線程會保存各自的數(shù)據(jù)庫連接。
當連接被保存的時間超過mysql連接的最大超時時間,連接失效,但不會被線程釋放。
之后再調起線程執(zhí)行涉及到數(shù)據(jù)庫操作的異步任務時,會用到失效的數(shù)據(jù)庫連接,導致報錯“MySQL server has gone away”。
解決方案是在線程池的所有異步任務執(zhí)行前先檢查數(shù)據(jù)庫連接是否可用,然后關掉不可用連接
def batch_thread(self):
for conn in connections.all():
conn.close_if_unusable_or_obsolete()
...
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Python使用微信itchat接口實現(xiàn)查看自己微信的信息功能詳解
這篇文章主要介紹了Python使用微信itchat接口實現(xiàn)查看自己微信的信息功能,結合實例形式分析了Python微信itchat模塊常見功能與操作技巧,需要的朋友可以參考下2019-08-08
Python2和Python3.6環(huán)境解決共存問題
這篇文章主要介紹了Python2和Python3.6環(huán)境解決共存問題,需要的朋友可以參考下2018-11-11
Python?matplotlib實現(xiàn)多子圖布局
多子圖布局是指在一個圖像中同時顯示多個子圖,每個子圖可以是獨立的圖形或者是相互關聯(lián)的圖形,下面我們就來了解下matplotlib是如何實現(xiàn)多子圖布局的吧2023-12-12
Python快速轉換numpy數(shù)組中Nan和Inf的方法實例說明
今天小編就為大家分享一篇關于Python快速轉換numpy數(shù)組中Nan和Inf的方法實例說明,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-02-02

