任务等待时长报警功能

156 阅读2分钟

需求:

1.用户使用AI转录时首次任务的等待时长

2.如果超过30s等待时长则需要进行飞书报警提示

实现:

1.使用的Keystone 6搭建的一个后台管理系统,第一步连接数据库使用的knex映射mysql来查询
import knex, { Knex } from "knex";
​
const getConnectionConfig = (db: string): Knex.StaticConnectionConfig | Knex.ConnectionConfigProvider => {
  return {
    host: 'xxxxxxxxxx',
    port: 3306,
    user: 'xxxx',
    password: 'xxxx',
    database: db
  }
}
​
const connShire = knex({
  client: 'mysql2',
  connection: getConnectionConfig('shire'),
});
​
const connManager = knex({
  client: 'mysql2',
  connection: getConnectionConfig('manger')
});
​
export {
  connManager, connShire
}
2.查询逻辑
  • 以当前时间为起点对前24小时内创建的用户作为新用户
  • 来查询它的第一条转录task任务的id
  • 拿到id后查询任务表对应的任务状态是否为wail等待并获取它的时长
  • 返回用户的id和时长 如果大于30s 则进行报警 接口如下↓↓↓↓↓↓
import { connManager } from "./connections";
​
interface WaitParams {
    twentyFourHoursAgo?: Date;
}
export async function WaitTasks(params: WaitParams) {
​
  const userTask = connManager('asr_user_tasks');
  const taskSteps = connManager('asr_task_steps');
​
  let twentyFourHoursAgo = params.twentyFourHoursAgo ?? new Date(new Date().getTime() - 24 * 60 * 60 * 1000);
​
  // 查找每个用户在当前时间前24小时内创建的最小 task_id
  const waitTaskList = await userTask
  .select('user_id')
  .min('task_id as first_task_id')
  .where('created_at', '>=', twentyFourHoursAgo)
  .groupBy('user_id')
  .then(results => {
    const firstTaskIds = results.map(row=>row.first_task_id);
    return taskSteps.select('task_id','cost','created_at').whereIn('task_id',firstTaskIds).andWhere('step','wait')
  })
  .then(taskStepsResults=>{
    const alertTasks:Array<{task_id:string,step_cost:string,created_at:string}> = [];
    taskStepsResults.forEach(step=>{
      if(step.cost>30){
        
        alertTasks.push({ task_id: step.task_id, step_cost: step.cost,created_at: step.created_at  });
      }
    });
    return alertTasks;
})
  .catch(err => {
    console.error(err); 
  })
    
​
  return waitTaskList;
}
​
并在routers下创建接口
  app.get('/api/clipto/xxx/xxx-xxx-xxx', async (req, res, next) => {
    try {
      const twentyFourHoursAgo = req.query.date ? new Date(req.query.date as string) : undefined
      const waitTaskList  = await WaitTasks({twentyFourHoursAgo});
      res.status(200).json(waitTaskList);
    } catch (e) {
      res.status(500).send(e);
    }
  });
​
3.获取到报警任务列表并定义好接口后,在服务启动时进行定时器轮询 使用的node-schedule
  • 启动后运行定时器 对报警的任务进行去重, 每1分钟获取一次 , 使用set存储 每天凌晨清除缓存 避免内存堵塞 服务器崩溃
//scheduledTtask.ts
import schedule from 'node-schedule';
import { WaitTasks } from './waitTask'; 
​
const interval = '*/1 * * * *';
const sentAlerts = new Set(); 
​
//每天凌晨清除set缓存
schedule.scheduleJob('0 0 */1 * *', () => {
  sentAlerts.clear();
  console.log('已清空已发送集合');
});
​
async function sendFeishuAlert(task: { task_id: string, step_cost: string }) {
  const alertKey = `${task.task_id}-${task.step_cost}`;
  if (sentAlerts.has(alertKey)) {
    return;
  }
  const webhookUrl = 'https://www.feishu.cn/flow/api/xxxxxx/xxxxxxx'; 
  const message = {
    errorMsg: `Task ${task.task_id} has a wait time of ${task.step_cost} seconds. Triggering alert.`,
  };
​
  try {
    await fetch(webhookUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(message),
    });
    sentAlerts.add(alertKey);
  } catch (error) {
    console.error('Error sending alert to Feishu:', error);
  }
}
​
// 定义轮询函数
async function pollWaitTasks() {
  try {
    const waitTaskList = await WaitTasks({ twentyFourHoursAgo: undefined });
​
    if (waitTaskList) {
      waitTaskList.forEach(task => {
        if (Number(task.step_cost) > 30) {
          sendFeishuAlert(task);
        }
      });
    }
  } catch (e) {
    console.error('Error:', e);
  }
}
//启动
schedule.scheduleJob(interval, pollWaitTasks);
​
//在keystone.ts中引入即可

image-20240611201054334.png