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解析模块(ConfigParser)使用方法
Dec 10 Python
python实现数通设备端口监控示例
Apr 02 Python
Python中将字典转换为XML以及相关的命名空间解析
Oct 15 Python
Python基础教程之正则表达式基本语法以及re模块
Mar 25 Python
python 读取excel文件生成sql文件实例详解
May 12 Python
Python输出各行命令详解
Feb 01 Python
Pandas读写CSV文件的方法示例
Mar 27 Python
python 读写excel文件操作示例【附源码下载】
Jun 19 Python
tensorflow mnist 数据加载实现并画图效果
Feb 05 Python
django-xadmin根据当前登录用户动态设置表单字段默认值方式
Mar 13 Python
python 获取剪切板内容的两种方法
Nov 28 Python
ubuntu安装jupyter并设置远程访问的实现
Mar 31 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
基于Laravel实现的用户动态模块开发
2017/09/21 PHP
PHP异步进程助手async-helper
2018/02/05 PHP
PHP 爬取网页的主要方法
2018/07/13 PHP
PHP getName()函数讲解
2019/02/03 PHP
PHP中上传文件打印错误错误类型分析
2019/04/14 PHP
js判断一个元素是否为另一个元素的子元素的代码
2012/03/21 Javascript
js整数字符串转换为金额类型数据(示例代码)
2013/12/26 Javascript
jQuery子属性过滤选择器用法分析
2015/02/10 Javascript
jQuery遍历json中多个map的方法
2015/02/12 Javascript
JavaScript实现拖拽网页内元素的方法
2015/04/15 Javascript
jquery点击缩略图切换视频播放特效代码分享
2015/09/15 Javascript
JavaScript实现点击单元格改变背景色的方法
2016/02/12 Javascript
jQuery Mobile操作HTML5的常用函数总结
2016/05/17 Javascript
jquery获取table指定行和列的数据方法(当前选中行、列)
2016/11/07 Javascript
JavaScript的兼容性与调试技巧
2016/11/22 Javascript
js仿小米手机上下滑动效果
2017/02/05 Javascript
Vuerouter的beforeEach与afterEach钩子函数的区别
2018/12/26 Javascript
vue 子组件和父组件传值的示例
2020/09/11 Javascript
js中实现继承的五种方法
2021/01/25 Javascript
Tensorflow简单验证码识别应用
2017/05/25 Python
使用Python 正则匹配两个特定字符之间的字符方法
2018/12/24 Python
使用Python轻松完成垃圾分类(基于图像识别)
2019/07/09 Python
Python使用scrapy爬取阳光热线问政平台过程解析
2019/08/14 Python
pycharm中导入模块错误时提示Try to run this command from the system terminal
2020/03/26 Python
Python如何设置指定窗口为前台活动窗口
2020/08/12 Python
Python爬取微信小程序通用方法代码实例详解
2020/09/29 Python
如何基于Python爬虫爬取美团酒店信息
2020/11/03 Python
详解Open Folder as PyCharm Project怎么添加的方法
2020/12/29 Python
理工科学生的自我评价
2013/12/15 职场文书
物流专员岗位职责
2014/02/17 职场文书
党员民主评议个人总结
2014/10/20 职场文书
2014年林业工作总结
2014/12/05 职场文书
茶花女读书笔记
2015/06/29 职场文书
Spring Boot两种全局配置和两种注解的操作方法
2021/06/29 Java/Android
C#连接ORACLE出现乱码问题的解决方法
2021/10/05 Oracle
php解析非标准json、非规范json的方式实例
2022/05/10 PHP