克服并发与容错:分布式任务调度系统实践

5万字长文

趁还没火赶紧收藏,比知识星球的项目质量高还不付费,你去哪儿找?

基于spring环境开发,拒绝CRUD,让你体验后端的美


我们为什么需要任务定时调度系统?

从后端角度讲,任务定时调度系统广泛应用于数据备份、日志清理、系统维护等场景,能够显著提高系统的自动化程度和运行效率

后端场景

1. 数据备份与恢复

1.1 场景描述
  • 数据备份:定期备份数据库、文件系统等关键数据,防止数据丢失。
  • 数据恢复:在数据丢失或损坏时,能够快速恢复数据。
1.2 任务定时调度系统应用
  • 任务定义:定义备份任务,包括备份时间、备份路径、备份策略等。
  • 调度策略:使用Cron表达式或固定间隔,定期执行备份任务。
  • 执行器:执行备份命令,如mysqldumprsync等。
  • 监控与日志:记录备份任务的执行情况,监控备份成功率。

2. 日志清理与归档

2.1 场景描述
  • 日志清理:定期清理过期的日志文件,释放存储空间。
  • 日志归档:将日志文件归档到长期存储系统,便于后续分析。
2.2 任务定时调度系统应用
  • 任务定义:定义日志清理和归档任务,包括清理时间、归档路径、保留策略等。
  • 调度策略:使用Cron表达式或固定间隔,定期执行清理和归档任务。
  • 执行器:执行清理和归档命令,如rmmv等。
  • 监控与日志:记录清理和归档任务的执行情况,监控日志文件的存储使用情况。

3. 系统维护与更新

3.1 场景描述
  • 系统更新:定期更新系统组件或软件包,确保系统安全性和稳定性。
  • 资源监控:定期监控系统资源使用情况,如CPU、内存、磁盘等。
3.2 任务定时调度系统应用
  • 任务定义:定义系统更新和资源监控任务,包括更新时间、更新内容、监控指标等。
  • 调度策略:使用Cron表达式或固定间隔,定期执行系统更新和资源监控任务。
  • 执行器:执行系统更新和资源监控命令,如apt-get updatetop等。
  • 监控与日志:记录系统更新和资源监控任务的执行情况,监控系统资源的使用情况。

4. 批处理任务

4.1 场景描述
  • 批处理任务:在特定时间点或时间间隔内执行批处理任务,如数据清洗、数据转换、数据导入导出等。
4.2 任务定时调度系统应用
  • 任务定义:定义批处理任务,包括任务时间、任务类型、任务参数等。
  • 调度策略:使用Cron表达式或固定间隔,定期执行批处理任务。
  • 执行器:执行批处理命令,如调用数据处理API、执行Shell脚本等。
  • 监控与日志:记录批处理任务的执行情况,监控任务的执行效率和成功率。

5. 自动化测试

5.1 场景描述
  • 自动化测试:定期执行自动化测试任务,确保系统的功能和性能符合预期。
5.2 任务定时调度系统应用
  • 任务定义:定义自动化测试任务,包括测试时间、测试类型、测试用例等。
  • 调度策略:使用Cron表达式或固定间隔,定期执行自动化测试任务。
  • 执行器:执行测试命令,如调用测试框架(如JUnit、Selenium)执行测试用例。
  • 监控与日志:记录自动化测试任务的执行情况,监控测试结果和覆盖率。

在互联网业务中,任务定时调度系统同样扮演着至关重要的角色,它确保了各种自动化流程按时、按需执行,从而维持业务的正常运转和优化用户体验。

互联网业务

1. 业务自动化

  • 数据同步与备份: 定时任务可以用于跨数据库或跨系统的数据同步,确保数据的一致性,以及进行定期的数据备份,防止数据丢失。

  • 缓存更新: 对于一些不经常变动但访问频繁的数据,可以设置定时任务定期更新缓存,提高系统响应速度。

  • 报表生成: 每天、每周或每月生成业务报表,用于分析和决策支持。

2. 用户体验优化

  • 定时推送: 如新闻应用的每日新闻摘要推送,或电商平台的促销活动提醒。

  • 会员服务: 自动续费、会员等级调整等都可以通过定时任务实现。

  • 内容更新: 如社交媒体内容的定时发布,保持平台内容的活跃度。

3. 系统维护与优化

  • 日志清理: 定期清理旧的日志文件,释放存储空间。

  • 性能监控: 定期运行性能检测任务,提前发现并解决潜在问题。

  • 索引重建: 在搜索引擎服务中,定时重建索引以提升搜索效率。

4. 业务流程

  • 订单处理: 如订单自动取消、自动确认收货等。

  • 支付结算: 定时对账、结算给商家或用户的账户。

  • 库存管理: 定时检查库存,触发自动补货或预警。

5. 营销与运营

  • 活动管理: 自动开启或结束促销活动、限时抢购等。

  • 用户行为分析: 定期分析用户行为数据,为个性化推荐或营销策略提供数据支持。

6. 安全与合规

  • 安全扫描: 定期进行系统的安全检查,防范潜在的安全威胁。

  • 合规检查: 确保业务操作符合法律法规,如数据隐私保护的定期审计。

7. 用户行为分析

7.1 场景描述
  • 用户行为分析:定期分析用户行为数据,生成用户画像、推荐模型等。
7.2 任务定时调度系统应用
  • 任务定义:定义用户行为分析任务,包括分析时间、分析指标、输出结果等。
  • 调度策略:使用Cron表达式或固定间隔,定期执行用户行为分析任务。
  • 执行器:执行分析命令,如调用数据分析工具(如Spark、Hadoop)进行数据处理。
  • 监控与日志:记录用户行为分析任务的执行情况,监控分析结果的准确性和时效性。

为什么要变成分布式的?

问就是高可用,可扩展,高容错


前言

从后端的角度来看,定时任务是指在预定的时间点或时间间隔内自动执行的特定任务。这些任务通常由服务器或分布式系统中的某个组件触发,并由后端服务负责执行。以下是从后端角度详细介绍定时任务的各个方面:

1. 定时任务的定义与管理

1.1 任务定义
  • 任务名称:任务的唯一标识符,用于区分不同的任务。
  • 任务描述:任务的简要描述,帮助理解任务的目的和功能。
  • 执行时间:任务的执行时间,可以是具体的日期时间、Cron表达式或固定的时间间隔。
  • 执行命令:任务执行的具体命令或脚本,可以是Shell命令、Python脚本、Java程序等。
  • 参数:任务执行时所需的参数,可以是命令行参数、环境变量等。
  • 重试策略:任务执行失败时的重试策略,包括最大重试次数和重试间隔。
  • 超时时间:任务执行的最大允许时间,超过该时间任务将被终止。
1.2 任务管理
  • 任务注册:通过API或UI界面将任务注册到系统中。
  • 任务修改:允许用户修改已注册任务的定义。
  • 任务删除:允许用户删除不再需要的任务。
  • 任务查询:允许用户查询任务的状态、执行历史等信息。

2. 调度器

2.1 调度策略
  • Cron表达式:支持标准的Cron表达式,用于定义任务的执行时间。
  • 固定间隔:支持以固定时间间隔执行任务。
  • 依赖任务:任务可以依赖其他任务的完成情况,只有当依赖任务成功执行后,当前任务才会被调度。
2.2 调度算法
  • 优先级调度:根据任务的优先级进行调度。
  • 负载均衡:根据执行器的负载情况,将任务分配到负载较低的执行器上。
2.3 调度记录
  • 调度时间:记录任务的调度时间。
  • 调度节点:记录任务被调度的节点。
  • 调度状态:记录任务的调度状态,如已调度、未调度、调度失败等。

3. 执行器

3.1 任务执行
  • 任务获取:执行器从消息队列或调度器获取待执行的任务。
  • 任务执行:执行器执行任务的命令或脚本。
  • 执行结果:执行结果(成功或失败)通过消息队列或直接返回给调度器。
3.2 容错机制
  • 任务重试:如果任务执行失败,根据重试策略进行重试。
  • 任务超时:如果任务执行时间超过预设的超时时间,执行器会终止任务并标记为失败。

4. 存储层

4.1 数据存储
  • 任务定义存储:存储任务的定义信息。
  • 调度记录存储:记录任务的调度历史。
  • 执行结果存储:存储任务的执行结果,包括成功、失败、重试次数等信息。
4.2 存储选择
  • 关系型数据库:如MySQL、PostgreSQL,适合存储结构化数据。
  • NoSQL数据库:如MongoDB、Redis,适合存储非结构化或半结构化数据。

5. 消息队列

5.1 消息队列选择
  • Kafka:适合高吞吐量的场景,支持分布式消息传递。
  • RabbitMQ:适合需要可靠消息传递的场景。
  • Redis Pub/Sub:适合轻量级、低延迟的场景。
5.2 消息传递
  • 任务调度消息:调度器将任务调度信息发送到消息队列。
  • 任务执行消息:执行器从消息队列中获取任务并执行。
  • 执行结果消息:执行结果(成功或失败)通过消息队列返回给调度器。

6. 监控与日志

6.1 监控
  • 系统状态监控:监控调度器、执行器、存储层等组件的状态。
  • 任务执行监控:监控任务的执行情况,包括执行时间、执行结果等。
6.2 日志
  • 任务执行日志:记录任务的执行过程,包括输入参数、执行命令、输出结果等。
  • 系统日志:记录系统的运行状态、错误信息等。

7. 安全性

7.1 身份验证
  • API身份验证:对任务定义与管理模块的API进行身份验证,防止未授权访问。
  • 用户认证:对用户进行身份认证,确保只有授权用户才能操作任务。
7.2 数据加密
  • 数据传输加密:在数据传输过程中使用SSL/TLS加密,确保数据的安全性。
  • 数据存储加密:对存储在数据库中的敏感数据进行加密。
7.3 访问控制
  • 权限管理:对不同用户或角色设置不同的访问权限,确保任务的安全管理。

具体设计与实现

设计一个分布式任务定时调度系统涉及多个层面的考虑,包括任务调度、任务分发、任务执行、状态监控、高可用性、容错机制和扩展性。一个健壮的分布式定时调度系统必须确保在多个节点环境中任务能够按时、高效且可靠地执行。

核心设计原则

  1. 分布式调度:系统需要支持在多个节点上分布式运行,并保证任务不会因为节点故障而丢失或重复执行。
  2. 任务的高可用性:调度系统要能够处理节点故障,确保任务按时执行,并提供故障恢复能力。
  3. 任务的负载均衡:需要将任务均衡分配给不同的节点,避免某些节点过载或空闲。
  4. 可扩展性:系统应能够方便地横向扩展,以应对任务和节点的增长。
  5. 可监控和可追踪性:系统应该对任务的调度、执行、结果、失败等状态提供全面的监控与日志。

系统的核心组件

  1. 任务定义与调度配置

    • 任务类型:定时任务、周期任务、触发任务等。任务可以以Cron表达式等格式定义调度时间。
    • 任务元数据存储:任务的元数据(任务ID、时间配置、任务状态等)可以存储在数据库中,如MySQL、PostgreSQL,或者使用分布式数据库如Etcd、ZooKeeper、Redis。
  2. 调度器(Scheduler)

    • 时间驱动的调度器:通过解析任务的时间表达式(如Cron)确定任务的执行时间。

    • 分布式锁:由于调度器可能有多个实例同时运行,使用分布式锁(如基于Redis、ZooKeeper)保证同一任务不会被多个调度器重复调度。最常见的分布式锁实现包括:

      • Redis分布式锁:使用Redis的SETNX命令确保同一时刻只有一个调度器可以调度某个任务。
      • ZooKeeper/Etcd:通过分布式一致性来实现锁。
  3. 任务分配与负载均衡

    • 任务分发器(Dispatcher) :调度器在调度任务时,需要将任务分发到执行节点。可以基于轮询随机分配、或任务权重等策略分配任务,常见的负载均衡策略包括:

      • 哈希取模:通过对任务ID哈希后取模来分配任务。
      • Consistent Hashing:一致性哈希可以保证任务分配的稳定性,当节点变动时,最少数量的任务会受到影响。
      • 基于节点负载的分配:调度系统监控节点的负载情况,将任务优先分配到负载较低的节点上。
  4. 任务执行器(Executor)

    • 任务执行:执行器从任务分发器处接收任务后,按照设定的时间进行执行。执行器需要具备一定的容错机制,确保任务不会因为瞬时的失败而永久失败。支持任务的重试机制。
    • 任务状态同步:任务执行过程中,执行器会向调度器反馈任务的执行状态(例如执行中执行成功执行失败),确保调度器可以实时掌握任务状态。
    • 任务日志记录:执行过程中,生成日志并记录执行过程中的异常情况。
  5. 任务状态管理与恢复机制

    • 任务状态:任务从创建调度分发执行中执行成功执行失败,需要有完整的状态流转。任务状态可存储在数据库中,调度器或执行器通过查询数据库的任务状态来判断任务的当前进展。
    • 任务恢复机制:当某个节点宕机时,系统应自动将任务重新分配给其他节点执行(利用分布式锁来确保同一任务不会被多个节点同时执行)。
  6. 监控与告警

    • 监控:需要对整个调度系统进行全面的监控,监控内容包括:

      • 任务的执行时间、执行次数、成功/失败次数等。
      • 每个节点的负载情况、任务执行时长等。
      • 任务的状态流转(从调度到执行的各个步骤)。
    • 告警:任务执行失败、超时、节点故障时触发告警,并支持多种告警方式(如邮件、短信、钉钉,企业微信,飞书等)。

  7. 高可用与扩展性设计

    • 调度器的高可用:可以采用主从架构,主调度器负责任务调度,当主调度器发生故障时,自动选举从调度器接管工作(如使用ZooKeeper实现主从选举)。
    • 任务的容错性:任务执行失败后可以重试,重试次数和重试间隔可配置。某些任务执行时间可能较长,任务系统需要支持长时间运行任务
    • 水平扩展:通过增加调度器或执行器节点来横向扩展系统。调度器和执行器之间应该是松耦合的,增加节点时不需要修改任务系统的整体架构。
    • 数据库的扩展:由于任务元数据和状态信息需要频繁读写,可能需要考虑数据库的扩展性,如读写分离或使用分布式数据库(如Cassandra、HBase)存储任务信息。

系统架构图示(简化版)

+--------------------+
|   API Gateway      |  <--- 用户通过API创建、管理任务
+--------------------+
         |
+--------------------+
|   Task Scheduler   |  <--- 任务调度器,负责任务的时间触发、分发
+--------------------+
         |
         v
+--------------------+      +--------------------+
|   Task Dispatcher  | ---> | Distributed Lock   |  <--- 保证任务调度的唯一性(Redis/ZooKeeper)
+--------------------+      +--------------------+
         |
         v
+---------------------+    +---------------------+    +---------------------+
| Task Executor Node  |    | Task Executor Node  |    | Task Executor Node  |  <--- 执行器,执行任务
+---------------------+    +---------------------+    +---------------------+
         |
         v
+--------------------+
|   Status Tracker   |  <--- 监控任务执行状态,记录任务日志
+--------------------+
         |
         v
+--------------------+
|  Monitoring/Alert  |  <--- 监控系统,任务失败或异常时告警
+--------------------+

设计细节与技术选型

  • 分布式锁:选择 Redis 或 ZooKeeper 实现分布式锁,防止任务重复调度和分发。
  • 消息队列:在任务调度器和任务执行器之间可以使用消息队列(如 Kafka、RabbitMQ、ActiveMQ)作为任务的分发通道,保证任务在不同节点之间的可靠分发。
  • 任务持久化:任务元数据和状态信息可以使用关系型数据库(如 MySQL)或者 NoSQL 数据库(如 MongoDB)进行存储,具体选型依据系统需求和扩展性考虑。
  • 任务负载均衡:可以使用一致性哈希或基于权重的负载均衡策略,或者借助已有的负载均衡工具(如 NGINX、Kubernetes 自带的调度器)。

具体实现方案和技术细节

1. 任务存储设计

-- 任务定义表
CREATE TABLE task_definition (
    task_id BIGINT PRIMARY KEY,
    task_name VARCHAR(100),
    task_desc VARCHAR(500),
    cron_expression VARCHAR(100),
    task_handler VARCHAR(200), -- 任务处理类
    task_param TEXT,          -- 任务参数(JSON格式)
    retry_times INT,          -- 重试次数
    retry_interval INT,       -- 重试间隔(秒)
    status TINYINT,          -- 任务状态(0:停用 1:启用)
    create_time DATETIME,
    update_time DATETIME
);

-- 任务执行记录表
CREATE TABLE task_execution (
    execution_id BIGINT PRIMARY KEY,
    task_id BIGINT,
    executor_ip VARCHAR(50),  -- 执行器IP
    start_time DATETIME,      -- 开始时间
    end_time DATETIME,        -- 结束时间
    status TINYINT,          -- 执行状态(0:执行中 1:成功 2:失败)
    error_msg TEXT,          -- 错误信息
    create_time DATETIME
);

2. 调度器实现

@Component
public class TaskScheduler {
   
    
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private TaskExecutorRegistry executorRegistry;
    
    // 使用Redis分布式锁实现任务调度
    public void scheduleTask(TaskDefinition task) {
   
        String lockKey = "task_lock:" + task.getTaskId();
        try {
   
            // 获取分布式锁
            boolean locked = redisTemplate.opsForValue()
                .setIfAbsent(lockKey, "1", 30, TimeUnit.SECONDS);
            
            if (locked) {
   
                // 计算下次执行时间
                Date nextFireTime = CronUtils.getNextFireTime(task.getCronExpression());
                if (shouldExecuteNow(nextFireTime)) {
   
                    // 选择执行器节点
                    String executorNode = selectExecutor();
                    // 发送任务到执行器
                    dispatchTask(task, executorNode);
                }
            }
        } finally {
   
            // 释放锁
            redisTemplate.delete(lockKey);
        }
    }
    
    // 选择执行器节点(一致性哈希实现)
    private String selectExecutor() {
   
        List<String> nodes = executorRegistry.getAvailableNodes();
        ConsistentHash<String> hash = new ConsistentHash<>(nodes);
        return hash.getNode(UUID.randomUUID().toString());
    }
}

3. 任务执行器实现

@Component
public class TaskExecutor {
   
    
    @Autowired
    private TaskExecutionRepository executionRepository;
    @Autowired
    private ApplicationContext applicationContext;
    
    public void executeTask(TaskDefinition task) {
   
        TaskExecution execution = new TaskExecution();
        execution.setTaskId(task.getTaskId());
        execution.setExecutorIp(IpUtils.getLocalIp());
        execution.setStartTime(new Date());
        
        try {
   
            // 获取任务处理器
            TaskHandler handler = (TaskHandler) applicationContext
                .getBean(task.getTaskHandler());
            
            // 执行任务
            handler.execute(task.getTaskParam());
            
            // 更新执行状态为成功
            execution.setStatus(1);
            execution.setEndTime(new Date());
            
        } catch (Exception e) {
   
            // 任务执行失败,进行重试
            if (shouldRetry(task)) {
   
                retryTask(task);
            }
            
            // 更新执行状态为失败
            execution.setStatus(2);
            execution.setErrorMsg(e.getMessage());
            execution.setEndTime(new Date());
        }
        
        // 保存执行记录
        executionRepository.save(execution);
    }
    
    // 任务重试逻辑
    private void retryTask(TaskDefinition task) {
   
        // 使用延迟队列实现重试
        DelayQueue<RetryTask> delayQueue = new DelayQueue<>();
        delayQueue.offer(new RetryTask(task, 
            task.getRetryInterval(), TimeUnit.SECONDS));
    }
}

4. 监控告警实现

@Component
public class TaskMonitor {
   
    
    @Autowired
    private AlertService alertService;
    
    // 监控任务执行时长
    public void monitorExecutionDuration(TaskExecution execution) {
   
        long duration = execution.getEndTime().getTime() - 
            execution.getStartTime().getTime();
            
        // 如果执行时间超过阈值,发送告警
        if (duration > getThreshold(execution.getTaskId())) {
   
            AlertMessage message = new AlertMessage();
            message.setType(AlertType.TASK_TIMEOUT);
            message.setContent("Task execution timeout: " + execution.getTaskId());
            alertService.sendAlert(message);
        }
    }
    
    // 监控任务失败
    @EventListener(TaskFailedEvent.class)
    public void onTaskFailed(TaskFailedEvent event) {
   
        AlertMessage message = new AlertMessage();
        message.setType(AlertType.TASK_FAILED);
        message.setContent("Task failed: " + event.getTaskId() + 
            ", error: " + event.getErrorMessage());
        alertService.sendAlert(message);
    }
}

5. 高可用实现

@Component
public class SchedulerHA {
   
    
    @Autowired
    private CuratorFramework zkClient;
    
    private static final String MASTER_PATH = "/scheduler/master";
    
    // 使用ZooKeeper实现主从选举
    public void electMaster() {
   
        try {
   
            // 创建临时节点
            zkClient.create()
                .withMode(CreateMode.EPHEMERAL)
                .forPath(MASTER_PATH, IpUtils.getLocalIp().getBytes());
                
            // 成为主节点,开始调度任务
            startScheduling();
            
        } catch (Exception e) {
   
            // 创建节点失败,说明已经有主节点
            // 注册监听器,等待成为主节点
            registerMasterListener();
        }
    }
    
    // 监听主节点变化
    private void registerMasterListener() {
   
        PathChildrenCache cache = new PathChildrenCache(
            zkClient, MASTER_PATH, true);
            
        cache.getListenable().addListener((client, event) -> {
   
            if (event.getType() == PathChildrenCacheEvent.Type.CHILD_REMOVED) {
   
                // 主节点下线,重新选举
                electMaster();
            }
        });
    }
}

6. 任务分发实现

@Component
public class TaskDispatcher {
   
    
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;
    
    // Kafka消息发送回调处理
    private class TaskDispatchCallback 
        implements ListenableFutureCallback<SendResult<String, String>> {
   
        
        @Override
        public void onSuccess(SendResult<String, String> result) {
   
            // 任务发送成功,记录日志
            log.info("Task dispatched successfully, offset: {}", 
                result.getRecordMetadata().offset());
        }
        
        @Override
        public void onFailure(Throwable ex) {
   
            // 任务发送失败,进行重试
            log.error("Failed to dispatch task", ex);
            retryDispatch();
        }
    }
    
    // 任务分发重试机制
    private void retryDispatch(TaskDefinition task, String executorNode) {
   
        RetryTemplate retryTemplate = new RetryTemplate();
        
        RetryPolicy retryPolicy = new SimpleRetryPolicy(3); // 最多重试3次
        retryTemplate.setRetryPolicy(retryPolicy);
        
        // 指数退避策略
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        backOffPolicy.setInitialInterval(1000L);
        backOffPolicy.setMultiplier(2.0);
        retryTemplate.setBackOffPolicy(backOffPolicy);
        
        retryTemplate.execute(context -> {
   
            dispatchTask(task, executorNode);
            return null;
        });
    }
}

7. 执行器注册与发现

@Component
public class ExecutorRegistry {
   
    
    @Autowired
    private CuratorFramework zkClient;
    
    private static final String EXECUTOR_PATH = "/task/executors";
    
    // 执行器节点注册
    public void registerExecutor() {
   
        String path = EXECUTOR_PATH + "/" + IpUtils.getLocalIp();
        try {
   
            // 创建临时节点
            zkClient.create()
                .creatingParentsIfNeeded()
                .withMode(CreateMode.EPHEMERAL)
                .forPath(path, "1".getBytes());
                
            // 定期更新节点状态(心跳)
            startHeartbeat(path);
            
        } catch (Exception e) {
   
            log.error("Failed to register executor", e);
        }
    }
    
    // 获取可用执行器列表
    public List<String> getAvailableExecutors() {
   
        try {
   
            List<String> children = zkClient.getChildren()
                .forPath(EXECUTOR_PATH);
                
            return children.stream()
                .map(child -> new String(zkClient.getData()
                    .forPath(EXECUTOR_PATH + "/" + child)))
                .collect(Collectors.toList());
                
        } catch (Exception e) {
   
            log.error("Failed to get available executors", e);
            return Collections.emptyList();
        }
    }
    
    // 心跳更新
    private void startHeartbeat(String path) {
   
        ScheduledExecutorService executor = 
            Executors.newSingleThreadScheduledExecutor();
            
        executor.scheduleAtFixedRate(() -> {
   
            try {
   
                // 更新节点数据
                zkClient.setData()
                    .forPath(path, String.valueOf(System.currentTimeMillis())
                    .getBytes());
            } catch (Exception e) {
   
                log.error("Failed to update heartbeat", e);
            }
        }, 0, 30, TimeUnit.SECONDS);
    }
}

8. 任务执行状态追踪

@Component
public class TaskTracker {
   
    
    @Autowired
    private TaskExecutionRepository executionRepository;
    @Autowired
    private TaskMonitor taskMonitor;
    
    // 任务状态更新
    public void updateTaskStatus(TaskExecution execution) {
   
        // 保存执行记录
        executionRepository.save(execution);
        
        // 发送任务状态变更事件
        ApplicationEventPublisher.publishEvent(
            new TaskStatusChangeEvent(execution));
            
        // 监控任务执行情况
        taskMonitor.monitorExecution(execution);
    }
    
    // 任务执行超时检测
    @Scheduled(fixedRate = 60000) // 每分钟检查一次
    public void checkTimeout() {
   
        List<TaskExecution> runningTasks = 
            executionRepository.findByStatus(TaskStatus.RUNNING);
            
        for (TaskExecution task : runningTasks) {
   
            if (isTimeout(task)) {
   
                // 标记任务超时
                task.setStatus(TaskStatus.TIMEOUT);
                updateTaskStatus(task);
                
                // 终止任务执行
                terminateTask(task);
            }
        }
    }
    
    // 任务执行历史查询
    public Page<TaskExecution> queryTaskHistory(
            Long taskId, Date startTime, Date endTime, Pageable pageable) {
   
        return executionRepository.findByTaskIdAndTimeRange(
            taskId, startTime, endTime, pageable);
    }
}

9. 任务执行统计

@Component
public class TaskStatistics {
   
    
    @Autowired
    private RedisTemplate redisTemplate;
    
    // 统计任务执行次数
    public void incrementExecutionCount(Long taskId) {
   
        String key = "task:stats:exec_count:" + taskId;
        redisTemplate.opsForValue().increment(key);
    }
    
    // 统计任务执行时长
    public void recordExecutionDuration(Long taskId, long duration) {
   
        String key = "task:stats:duration:" + taskId;
        redisTemplate.opsForZSet().add(key, 
            String.valueOf(System.currentTimeMillis()), duration);
    }
    
    // 获取任务统计信息
    public TaskStats getTaskStats(Long taskId) {
   
        TaskStats stats = new TaskStats();
        
        // 获取执行次数
        String countKey = 

你可能感兴趣的:(tech-review,分布式,后端,架构,java,spring,微服务,数据库)