Django异步任务线程池实现原理


Posted in Python onDecember 17, 2019

这篇文章主要介绍了Django异步任务线程池实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

当数据库数据量很大时(百万级),许多批量数据修改请求的响应会非常慢,一些不需要即时响应的任务可以放到后台的异步线程中完成,发起异步任务的请求就可以立即响应

选择用线程池的原因是:线程比进程更为可控。不像子进程,子线程会在所属进程结束时立即结束。线程可共享内存。

请求任务异步处理的原理

使用python manage.py runserver模式启动的Django应用只有一个进程,对于每个请求,主线程会开启一个子线程来处理请求。请求子线程向主线程申请一个新线程,然后把耗时的任务交给新线程,自身立即响应,这就是请求任务异步处理的原理。

可视化线程池

如果想要管理这批异步线程,知道他们是否在运行中,可以使用线程池(ThreadPoolExecutor)。

线程池会先启动若干数量的线程,并让这些线程都处于睡眠状态,当向线程池submit一个任务后,会唤醒线程池中的某一个睡眠线程,让它来处理这个任务,当处理完这个任务,线程又处于睡眠状态。

submit任务后会返回一个期程(future),这个对象可以查看线程池中执行此任务的线程是否仍在处理中

因此可以构建一个全局可视化线程池:

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))

串行执行

使用线程锁

在全局线程池中初始化线程锁

class ThreadPool(object):
  def __init__(self):
    self.executor = ThreadPoolExecutor(20)
    self.future_dict = {}
    self.lock = threading.Lock()

然后执行线程前需要获取锁并再执行结束后释放锁

def batch_thread(self):
  global_thread_pool.lock.acquire()
  try:
    ...
    global_thread_pool.lock.release()
  except Exception:
    trace_log = traceback.format_exc()
    logger.error('异步任务执行失败:\n %s' % trace_log)
    global_thread_pool.lock.release()

需要捕捉异常预防子线程出错而无法释放锁的情况

异步线程任务执行前先检查数据库连接是否可用,然后关掉不可用连接

由于django的数据库连接是保存到线程本地变量中的,通过ThreadPoolExecutor创建的线程会保存各自的数据库连接。

当连接被保存的时间超过mysql连接的最大超时时间,连接失效,但不会被线程释放。

之后再调起线程执行涉及到数据库操作的异步任务时,会用到失效的数据库连接,导致报错“MySQL server has gone away”。

解决方案是在线程池的所有异步任务执行前先检查数据库连接是否可用,然后关掉不可用连接

def batch_thread(self):
  for conn in connections.all():
    conn.close_if_unusable_or_obsolete()
  ...

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python str与repr的区别
Mar 23 Python
Python入门篇之字符串
Oct 17 Python
python查询mysql中文乱码问题
Nov 09 Python
Python打印“菱形”星号代码方法
Feb 05 Python
Python KMeans聚类问题分析
Feb 23 Python
PowerBI和Python关于数据分析的对比
Jul 11 Python
python实现简单图书管理系统
Nov 22 Python
Python使用turtle库绘制小猪佩奇(实例代码)
Jan 16 Python
python的help函数如何使用
Jun 11 Python
Python常用base64 md5 aes des crc32加密解密方法汇总
Nov 06 Python
python用字节处理文件实例讲解
Apr 13 Python
Python函数对象与闭包函数
Apr 13 Python
python 求10个数的平均数实例
Dec 16 #Python
python 经典数字滤波实例
Dec 16 #Python
Python实现把类当做字典来访问
Dec 16 #Python
python中p-value的实现方式
Dec 16 #Python
基于python读取.mat文件并取出信息
Dec 16 #Python
python基于plotly实现画饼状图代码实例
Dec 16 #Python
python 实现让字典的value 成为列表
Dec 16 #Python
You might like
php中强制下载文件的代码(解决了IE下中文文件名乱码问题)
2011/05/09 PHP
PHP错误Parse error: syntax error, unexpected end of file in test.php on line 12解决方法
2014/06/23 PHP
PHP面向对象学习之parent::关键字
2017/01/18 PHP
php实现微信支付之企业付款
2018/05/30 PHP
JavaScript页面刷新与弹出窗口问题的解决方法
2010/03/02 Javascript
jquery中加载图片自适应大小主要实现代码
2013/08/23 Javascript
使用JavaScript获取电池状态的方法
2014/05/03 Javascript
Javascript 绘制 sin 曲线过程附图
2014/08/21 Javascript
微信小程序开发之录音机 音频播放 动画实例 (真机可用)
2016/12/08 Javascript
从零学习node.js之简易的网络爬虫(四)
2017/02/22 Javascript
vuejs响应用户事件(如点击事件)
2017/03/14 Javascript
nodejs调取微信收货地址的方法
2017/12/20 NodeJs
Openlayers+EasyUI Tree动态实现图层控制
2020/09/28 Javascript
解决ant design vue中树形控件defaultExpandAll设置无效的问题
2020/10/26 Javascript
JS addEventListener()和attachEvent()方法实现注册事件
2021/01/11 Javascript
[43:26]完美世界DOTA2联赛PWL S2 Forest vs Rebirth 第二场 11.20
2020/11/23 DOTA
用Python登录Gmail并发送Gmail邮件的教程
2015/04/17 Python
Python 26进制计算实现方法
2015/05/28 Python
用pandas中的DataFrame时选取行或列的方法
2018/07/11 Python
django 实现电子支付功能的示例代码
2018/07/25 Python
Python实现合并两个有序链表的方法示例
2019/01/31 Python
局域网内python socket实现windows与linux间的消息传送
2019/04/19 Python
使用python+whoosh实现全文检索
2019/12/09 Python
Python 列表的清空方式
2020/01/13 Python
python如何判断IP地址合法性
2020/04/05 Python
python批量生成身份证号到Excel的两种方法实例
2021/01/14 Python
matplotlib之pyplot模块之标题(title()和suptitle())
2021/02/22 Python
HTML5图片预览实例分享
2014/06/04 HTML / CSS
美国嘻哈首饰购物网站:Hip Hop Bling
2016/12/30 全球购物
Laura Mercier官网:彩妆大师罗拉玛斯亚的化妆品牌
2018/01/04 全球购物
什么是事务?事务有哪些性质?
2012/03/11 面试题
linux面试题参考答案(5)
2016/11/05 面试题
家长建议怎么写
2014/05/15 职场文书
2015年入党积极分子评语
2015/03/26 职场文书
新手必备Python开发环境搭建教程
2021/05/28 Python
Python绘画好看的星空图
2022/03/17 Python