Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

周期任务运营优化 #7364

Closed
normal-wls opened this issue Feb 26, 2024 · 5 comments · Fixed by #7399
Closed

周期任务运营优化 #7364

normal-wls opened this issue Feb 26, 2024 · 5 comments · Fixed by #7399
Assignees

Comments

@normal-wls
Copy link
Member

背景

目前的周期任务增长迅速,通过对环境数据的获取和分析,以及与用户沟通,发现有部分已经废弃的周期任务其实已经不需要了,但是用户不会去主动关闭,这造成了执行资源的浪费。

方案

为了解决这个问题,本方案主要设计两个场景:

  1. 项目在 cc 归档之后,标准运维的项目会一起归档,但是不会级联关闭开启的周期任务。
  2. 对于开启的周期任务,定期进行扫描和整理,并给对应的负责人发送通知进行确认。

实现

  1. 启动一个新的周期任务,用于上述两个场景的扫描
  2. cc 归档场景:扫描所有已归档项目,然后匹配开启的周期任务后自动关闭
  3. 定期通知场景:
    1. 扫描所有开启的周期任务,按更新人/创建人维度进行聚合,并通过通知渠道进行通知,并提供确认链接。
    2. 需要新建通知历史记录表,用于记录和更新历史通知,并在用户确认后更新通知周期。
    3. 暂定通知周期:当存在开启周期任务更新/创建超过一个月且用户距上次确认时间超过一个月时,会聚合该用户所有开启的周期任务并通知。
    4. 通知实现:通过 mq 进行任务分发
@ecoli666
Copy link
Collaborator

@normal-wls 发送频率和延期时长,需要做成可配置

@luca5slose
Copy link

参考内置通知执行人逻辑,纯后台实现,不使用前台的通知配置

@lTimej
Copy link
Collaborator

lTimej commented Mar 21, 2024

开发方案:

1.级联关闭开启的周期任务

在原有定期扫描cc项目上,将已经的归档的项目对应的周期任务关闭

def sync_projects_from_cmdb(username, use_cache=True):
    ......
    Project.objects.update_business_project_status(
        archived_cc_ids=archived_biz_cc_ids | deleted_biz_cc_ids, active_cc_ids=active_biz_cc_ids
    )
    try:
        archived_cc_ids = archived_biz_cc_ids | deleted_biz_cc_ids
        period_tasks = PeriodicTask.objects.filter(project__id__in=archived_cc_ids).all()
        # 把cc归档的业务下的周期任务关闭
        for task in period_tasks:
            task.set_enabled(False)
    except Exception as e:
        logger.warning("[shutdown period_task] shutdown peroid_tash failed due to: {}".format(e))

2.定期通知

@periodic_task(run_every=TzAwareCrontab(**settings.EXPIRED_SESSION_PERIOD_TASK_SCAN))
def scan_period_task():
    tasks = PeriodicTask.objects.filter(task__celery_task__enabled=True).all()
    title = "周期任务关闭通知"
    for task in tasks:
        # 根据更新人/创建人 、更新时间/创建时间聚合
        notify_time = task.edit_time if task.editor else task.create_time
        notifier = task.editor if task.editor else task.creator
        curr_time = datetime.datetime.now()
        last_month_time = curr_time + dateutil.relativedelta.relativedelta(months=-int(settings.PERIOD_TASK_TIMES))
        record = NoticeHistoryRecord.objects.filter(task=task,notifier=notifier).first()
        # 把通知确认链接放在邮件内容里
        content = settings.BK_SOPS_HOST + f"/periodictask/api/shutdown/confirm/{task.project.id}/{task.id}/"
        receiver = task.editor if task.editor else task.creator
        try:
            if not record:
                # 超过一个月的周期任务给相关人发送通知
                if notify_time.timestamp() < last_month_time.timestamp():
                    send_message("admin",["email"],receiver,title,content)
            else:
                record_time = record.notify_time
                # 距上次确认时间超过一个月
                if record_time.timestamp() < last_month_time.timestamp():
                    send_message("admin",["email"],receiver,title,content)
        except Exception as e:
            logger.exception(f"send period task notify error: {e}")
#  确认接口       
@require_GET
@iam_intercept(ShutdownConfirmInterceptor())
def periodtask_shutdown_confirm(request, project_id, task_id):
    task = PeriodicTask.objects.get(id=task_id)
    defaults = {
        "notifier": task.editor if task.editor else task.creator,
        "notify_time": datetime.datetime.now()
    }
    try:
        # 更新通知人和通知时间
        NoticeHistoryRecord.objects.update_or_create(task=task,defaults=defaults)
    except Exception as e:
        return JsonResponse(
            {"result": False, "message": str(e), "data": None, "code": err_code.REQUEST_PARAM_INVALID.code}
        )

    return JsonResponse({"result": True, "message": "success", "data": None, "code": err_code.SUCCESS.code})   


#  通知历史记录表
class NoticeHistoryRecord(models.Model):
    task = models.ForeignKey(PeriodicTask, verbose_name=_("周期任务"), on_delete=models.CASCADE)
    notify_time = models.DateTimeField(verbose_name=_("通知周期"), auto_now_add=True)
    notifier = models.CharField(max_length=64, verbose_name=_("通知人"))

@normal-wls
Copy link
Member Author

  1. 可能存在 n+1 的问题,而且是同步关闭,可以考虑这里是否做异步关闭处理
period_tasks = PeriodicTask.objects.filter(project__id__in=archived_cc_ids).all()
# 把cc归档的业务下的周期任务关闭
for task in period_tasks:
    task.set_enabled(False)
  1. send_message建议也改成异步
  2. NoticeHistoryRecord
    之前的一些外键实现不太好,建议用 task_id 代替 task
  3. 通知的网址 是否需要返回一个简单页面,还是 json 即可,periodtask_shutdown_confirm 是否需要登录+权限校验,还是说通过加密生成 url,然后在访问时直接解密获取 task_id(参考 callback_url)的构造。(需要产品确认)

@luca5slose
Copy link

@lTimej 通知模板参考
image

lTimej added a commit to lTimej/bk-sops that referenced this issue Mar 26, 2024
@lTimej lTimej linked a pull request Mar 26, 2024 that will close this issue
lTimej added a commit to lTimej/bk-sops that referenced this issue Apr 9, 2024
lTimej added a commit to lTimej/bk-sops that referenced this issue Apr 15, 2024
@lTimej lTimej assigned lTimej and unassigned ecoli666 Apr 15, 2024
lTimej added a commit to lTimej/bk-sops that referenced this issue Apr 15, 2024
normal-wls pushed a commit that referenced this issue Apr 15, 2024
* perf: 周期任务运营优化 #7364

* perf: 周期任务运营优化 #7364

* perf: 周期任务,所有开启的周期任务一起排到前列 #7405

* perf: 周期任务运营优化 #7364

* perf: 周期任务运营优化 #7364
normal-wls added a commit that referenced this issue Apr 17, 2024
…on_master

perf: 周期任务消息通知模版信息优化 #7364
normal-wls pushed a commit that referenced this issue Apr 19, 2024
* fix: 修复周期扫描发送周期任务消息通知 #7364

* fix: 修复周期扫描发送周期任务消息通知 #7364
normal-wls pushed a commit that referenced this issue Apr 19, 2024
* fix: 修复周期扫描发送周期任务消息通知 #7364

* fix: 修复周期扫描发送周期任务消息通知 #7364
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants