• 场景描述: 写了个构造数据的自动化脚本,准备集成到web上。 前端form表单提交构造数据所需的入参。实际操作时,前端点击保存后,由于构造数据的脚本要执行很久,导致前端的页面驻留太久。于是准备使用异步的方法解决这个问题。
  • 需求:前端给个form表单填写参数,保存后数据存入DB(一条任务数据),状态默认为未处理;调用异步方法,执行脚本,脚本执行成功后修改任务状态。
  • 技术栈 celery
  • urls.py
from django.conf.urls import url
from web.views import account,project

# 增加name属性是为了方便反向解析url
urlpatterns = [
    # 小工具
    url(r'^home/create_sim/$', project.CreateSim.as_view(), name="create_sim"),
]
  • views.py
class CreateSim(View):

    def get(self, request):
        form = CreateSimModelForm()
        # 去数据库获取任务列表传给前端{'form': form,'list':list}
        # 待实现
        return render(request, 'web/create_sim.html', {'form': form})

    def post(self, request):
        form = CreateSimModelForm(request, request.POST)
        if form.is_valid():
			# task_id  不是从request中获取的,所以要单独写入值
            form.instance.task_id = ''.join(str(uuid4()).split('-'))
            form_content = form.save()
            # 此处应该要将我的脚本中的内容放到这里来,但是执行太久,会导致前端一直停留在数据提交页面
            # 异步处理可以上场了。身为一个测试,当然知道异步处理的存在了,只是不知道如何实现。。。
            # def create_sim_task(operator, operator_name, operator_account, create_num, apn, product, offer)
            # 异步任务函数的调用方法.delay是将task任务放入到celery的队列里,函数需要的参数都放在delay()里
            asynchronous_tasks = create_sim_task.delay(form_content.get_operator_display(),
                                                       form_content.operator_name,
                                                       form_content.operator_account,
                                                       form_content.create_num,
                                                       form_content.apn,
                                                       form_content.product,
                                                       form_content.offer,
                                                       form_content.task_id,
                                                       )


            return JsonResponse({'status': True})

        return JsonResponse({'status': False, 'error': form.errors})
  • form.py
class CreateSimModelForm(Bootstrp, forms.ModelForm):
    class Meta:
        model = models.Task
        fields = ['operator', 'create_num', 'operator_name', 'operator_account', 'apn', 'offer', 'product']

    def __init__(self, request=None, *args, **kwargs):
    '''request实际没用到'''
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_operator(self):
        operator = self.cleaned_data.get('operator')
        if operator <= 0:
            raise ValidationError('运营商类型必须选择')
        return self.cleaned_data['operator']

    def clean_offer(self):
        operator = self.cleaned_data.get('operator')
        offer = self.cleaned_data.get('offer')
        if not offer and operator == 3:
            raise ValidationError('运行商类型为JT,offer不允许为空')

        return self.cleaned_data['offer']

    def clean_product(self):
    ''' 主要限制运营商为Syniverse时,product不允许为空'''
        operator = self.cleaned_data.get('operator')
        product = self.cleaned_data.get('product')
        if not product and operator == 5:
            raise ValidationError('运行商类型为Syniverse,product不允许为空')

        return self.cleaned_data['product']
  • models.py
class Task(models.Model):
    operator_choice = (
        (1, '请选择运营商类型'),
        (2, 'onelink'),
        (3, 'jt'),
        (4, 'jasper'),
        (5, 'syniverse'),
    )
    status_choice = (
        (1, '未处理'),
        (2, '处理中'),
        (3, '已完成'),

    )
    task_id = models.CharField(verbose_name='任务id', max_length=64)
    operator = models.SmallIntegerField(verbose_name='运营商', choices=operator_choice, default=1)
    create_num = models.SmallIntegerField(verbose_name='创建数量')
    operator_name = models.CharField(verbose_name='运营商名称', max_length=64)
    operator_account = models.CharField(verbose_name='运营商账户', max_length=64)
    apn = models.CharField(verbose_name='APN', max_length=64)
    product = models.CharField(verbose_name='product(Syniverse卡必填)', max_length=64, null=True, blank=True, default="")
    offer = models.CharField(verbose_name='offer(JT卡必填)', max_length=64, null=True, blank=True, default="")
    status = models.SmallIntegerField(verbose_name='任务状态', choices=status_choice, default=1)
  • 下面是重点了,主要是如何在django中使用celery
  • 先安装celery模块
  • 在django的项目目录中(跟app同级)建立一个包文件夹(就是一个文件夹里面包含一个空的__init__文件)
  • 新建task.py文件用于定义异步任务(函数)供其他模块调用。
    1. 先要初始化django的配置,参考wsgi.py文件中的写法
    2. 启动worker,进入项目根目录执行(如果不是在django项目中才需要执行步骤1),启动成功
    celery -A celery_tasks.task worker -l info

django异步接口demo django异步处理_python

import os
from celery import Celery


os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bugManagerSys.settings")
# 创建一个Celery实例backend 存放返回结果;broker 任务存放;'celery_tasks.tasks'随便定义一个名称即可,习惯性根据tasks.py文件路径写
celery_obj = Celery('celery_tasks.tasks', broker='redis://127.0.0.1:6379/1',backend="redis://127.0.0.1:6379/1")


# ImportSim 我写的脚本类
class ImportSim:
	def make_import_data(self):
		pass
		...
		
@celery_obj.task
def create_sim_task(operator, operator_name, operator_account, create_num, apn, product, offer,task_id):
    loggings.debug("开始处理异步任务")
    import_sim_url = "http://XXX:XX/admin/web/batchJobs/import/sims/data"
    inventory_sim_url = "http://XXX:XX/admin/web/boss/inventory/inboundRecords"
    oo = ImportSim(operator=operator)
    # 构造导入SIM卡信息的数据
    oo.make_import_data(num=create_num, operator_name=operator_name,
                        operator_acount=operator_account, apn=apn, product=product,
                        offer=offer)
    # 构造库存数据
    oo.make_inventory_import_data("单", "插拔卡三合一", "运营商提供")

    # 库存数据写入excel
    oo.write_excel(inventory=True)
    # 调用API接口,将库存数据上传到系统
    oo.import_excel(inventory_sim_url)
    # 构造测试桩数据
    oo.make_insert_sql()
    # 生成excel文件 ,会清理import data 所以必须在 make_inert_sql() 后调用write_excel(),将导入的SIM卡信息写入excel
    oo.write_excel()
    # 修改点 len(oo.onelink_insert_sql) > 0:
    # 定义线程池
    sum_sql_list = oo.jasper_insert_sql + oo.onelink_insert_sql + oo.syniverse_insert_sql + oo.jt_insert_sql
    with ThreadPoolExecutor(max_workers=20) as executor:
        tasks = [executor.submit(oo.insert_mock_db) for i in range(len(sum_sql_list))]
        wait(tasks, return_when=ALL_COMPLETED)
        print('all_cone')

    # 调用API接口将SIM卡的excel 文件上传
    # print('主线程有没有等?')
    res = oo.import_excel(import_sim_url)
    # obj = models.Task.objects.filter(task_id=task_id).first()
    # 准备在这里判断res的内容,如果返回的是成功,就修改数据库任务的状态为已完成,否则失败
    print(f"result ----->{res}")
    loggings.debug("处理异步任务完成")
    return res
  • 调用后,检查redis,在数据库1中存放了队列,数据库2中存放了返回值(注意返回值需要转换成json格式)

django异步接口demo django异步处理_数据_02

遗留问题:返回值如何处理。
1、异步任务生成的id要存起来
2、弄个定时任务,根据task_id去轮询任务表中状态为处理中的任务在redis中的返回值