python队列异步处理任务实现

概要

最近工作中遇到了一项任务,将导出文件功能改成异步处理。目前情况是已完成,但是有一个巨大的bug困扰了我很长时间,仔细思考好像没办法解决。下面我将梳理我的开发流程,我是小白,大神勿喷。

异步任务处理设计

实现效果:前端发起导出任务请求后,后端生成任务id返回给前端,前端就可以继续其他操作,同时前端根据任务id每2秒中查询一次后端的任务是否完成,完成则获取处理结果

1. 前端设计

以下是一段导出相应时间内的数据表格请求设计。

sysExportLogs = () =>{
    let { start, end } = this.state;
    let task_id = '';
    systemAdminAPI.sysAdminExportLogsExcel(start, end, logType).then(res => {
      task_id = res.data.task_id;
      return systemAdminAPI.queryAsyncOperationExportExcel(task_id);
    }).then(res => {
      if (res.data.is_finished === true){
        location.href = 'sys/log/export-excel/?task_id=' + task_id;
      } else {
        this.timer = setInterval(() => {
          systemAdminAPI.queryAsyncOperationExportExcel(task_id).then(res => {
            if (res.data.is_finished === true){
              this.setState({isFinished: true});
              clearInterval(this.timer);
              location.href = 'sys/log/export-excel/?task_id=' + task_id;
            }
          }).catch(err => {
            if (this.state.isFinished === false){
              clearInterval(this.timer);
              toaster.danger(gettext('Failed to export. '));
            }
          });
        }, 1000);
      }
    });
  };

详解: 前端通过点击一个按钮,触动sysExportLogs函数,该函数先发送一个请求用来获取task_id,之后设置一个定时器,每秒发起一个请求,查询后端任务是否已完成,完成则清除定时器,未完成则持续发起请求查看完成状态。

2. 后端设计

class TaskManager(object):

    def __init__(self):
        self.tasks_map = {}
        self.task_results_map = {}
        self.tasks_queue = queue.Queue(10)
        self.current_task_info = {}
        self.threads = []

    def is_valid_task_id(self, task_id):
        return task_id in (self.tasks_map.keys() | self.task_results_map.keys())

    def add_one_task(self, session, tstart, tend, log_type):
        task_id = str(uuid.uuid4())
        task =(handleTaskFunc, (session, tstart, tend, log_type, task_id))

        self.tasks_queue.put(task_id)
        self.tasks_map[task_id] = task
        return task_id
    def add_two_task(self, session, tstart, tend, log_type):
        ........
        ........
        return task_id

    def query_status(self, task_id):
        task_result = self.task_results_map.pop(task_id, None)
        if task_result == 'success':
            return True, None
        if isinstance(task_result, str) and task_result.startswith('error_'):
            return True, task_result[6:]
        return False, None

    def threads_is_alive(self):
        info = {}
        for t in self.threads:
            info[t.name] = t.is_alive()
        return info

    def handle_task(self):
        while True:
            try:
                task_id = self.tasks_queue.get(timeout=2)
            except queue.Empty:
                continue
            except Exception as e:
                logger.error(e)
                continue
            task = self.tasks_map.get(task_id)
            if type(task) != tuple or len(task) < 1:
                continue
            if type(task[0]).__name__ != 'function':
                continue
            task_info = task_id + ' ' + str(task[0])
            try:
                self.current_task_info[task_id] = task_info
                logging.info('Run task: %s' % task_info)
                start_time = time.time()

                # run
                task[0](*task[1])
                self.task_results_map[task_id] = 'success'

                finish_time = time.time()
                logging.info('Run task success: %s cost %ds \n' % (task_info, int(finish_time - start_time)))
                self.current_task_info.pop(task_id, None)
            except Exception as e:
                self.task_results_map[task_id] = 'error_' + str(e.args[0])
                logger.exception(e)
                logger.error('Failed to handle task %s, error: %s \n' % (task_info, e))
                self.current_task_info.pop(task_id, None)
            finally:
                self.tasks_map.pop(task_id, None)

    def run(self):
        thread_num = 3
        for i in range(thread_num):
            t_name = 'TaskManager Thread-' + str(i)
            t = threading.Thread(target=self.handle_task, name=t_name)
            self.threads.append(t)
            t.setDaemon(True)
            t.start()

	task_manager = TaskManager()

详解: 以上代码,设计了一个处理异步任务的类,在类中初始化了一个任务队列以及一些配合使用的映射。通过实例化这个类对象,运行线程run,持续对这个任务队列进行监听,当任务队列中存在任务时,对其进行后续操作,若没有任务,则持续进行监听。

小结

以上仅是大体的思路,在实现这个功能的时候,有一个致命的一点(对于我来说),我设计的类,在一个包中,里面的添加任务也在同一个包中,而启动监听却在另一个包中,这样会导致所实例化的对象并不是同一个,目前来说,无解。

问题:如何在两个不同的包中共享同一个实例化对象!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值