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解决鸡兔同笼问题的方法
Dec 20 Python
简单的编程0基础下Python入门指引
Apr 01 Python
深入理解Python中的super()方法
Nov 20 Python
matplotlib绘图实例演示标记路径
Jan 23 Python
对python抓取需要登录网站数据的方法详解
May 21 Python
异步任务队列Celery在Django中的使用方法
Jun 07 Python
python判断文件是否存在,不存在就创建一个的实例
Feb 18 Python
使用pytorch实现可视化中间层的结果
Dec 30 Python
pycharm激活码快速激活及使用步骤
Mar 12 Python
PyCharm 2020 激活到 2100 年的教程
Mar 25 Python
Python flask路由间传递变量实例详解
Jun 03 Python
keras 读取多标签图像数据方式
Jun 12 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的crc32函数使用时需要注意的问题(不然就是坑)
2015/04/21 PHP
学习php设计模式 php实现命令模式(command)
2015/12/08 PHP
Yii2实现跨mysql数据库关联查询排序功能代码
2017/02/10 PHP
php生成0~1随机小数的方法(必看)
2017/04/05 PHP
PHP设计模式之观察者模式定义与用法示例
2018/08/04 PHP
Laravel框架实现的上传图片到七牛功能详解
2019/09/06 PHP
contains和compareDocumentPosition 方法来确定是否HTML节点间的关系
2011/09/13 Javascript
js 判断一个元素是否在页面中存在
2012/12/27 Javascript
遮罩层点击按钮弹出并且具有拖动和关闭效果(两种方法)
2015/08/20 Javascript
基于jQuery实现自动轮播旋转木马特效
2015/11/02 Javascript
很棒的js选项卡切换效果
2016/07/15 Javascript
JS简单实现获取元素的封装操作示例
2017/04/07 Javascript
一个可复用的vue分页组件
2017/05/15 Javascript
vue组件Prop传递数据的实现示例
2017/08/17 Javascript
初探JavaScript 面向对象(推荐)
2017/09/03 Javascript
BetterScroll 在移动端滚动场景的应用
2017/09/18 Javascript
react中的ajax封装实例详解
2017/10/17 Javascript
使用layui 渲染table数据表格的实例代码
2018/08/19 Javascript
JS前端基于canvas给图片添加水印
2020/11/11 Javascript
[00:43]DOTA2小紫本全民票选福利PA至宝全方位展示
2014/11/25 DOTA
基于python的七种经典排序算法(推荐)
2016/12/08 Python
浅谈python中列表、字符串、字典的常用操作
2017/09/19 Python
Python cookbook(数据结构与算法)从序列中移除重复项且保持元素间顺序不变的方法
2018/03/13 Python
django DRF图片路径问题的解决方法
2018/09/10 Python
python3的输入方式及多组输入方法
2018/10/17 Python
基于Pycharm加载多个项目过程图解
2020/01/19 Python
python读取csv文件指定行的2种方法详解
2020/02/13 Python
python logging通过json文件配置的步骤
2020/04/27 Python
入党转预备思想汇报
2014/01/07 职场文书
最新会计专业求职信范文
2014/01/28 职场文书
会计核算科岗位职责
2014/03/19 职场文书
交通安全责任书范本
2014/07/24 职场文书
医院党建工作总结2015
2015/05/26 职场文书
论语读书笔记
2015/06/26 职场文书
远程教育集中轮训基层干部培训班学习心得体会
2016/01/09 职场文书
2016年青少年禁毒宣传教育活动总结(学校)
2016/04/05 职场文书