java分布式定时任务

一、分布式锁的底层实现细节(以 Redis 为例)

分布式锁是解决任务重复执行的核心,需保证原子性超时释放可重入性。以下是生产级 Redis 锁实现:

public class RedisDistributedLock {private final RedisTemplate<String, String> redisTemplate;private final String lockKey;private final String lockValue; // 用于标识锁持有者(支持可重入)private final long expireMillis; // 锁过期时间(避免死锁)// 构造函数:初始化锁参数public RedisDistributedLock(RedisTemplate<String, String> redisTemplate, String lockKey, String requestId, long expireMillis) {this.redisTemplate = redisTemplate;this.lockKey = lockKey;this.lockValue = requestId; // 建议使用UUID+线程IDthis.expireMillis = expireMillis;}// 尝试获取锁(原子操作)public boolean tryLock() {// 使用Redis的SET命令实现:NX(不存在则设置)+ PX(毫秒过期)return redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, expireMillis, TimeUnit.MILLISECONDS);}// 释放锁(需校验持有者,避免误释放)public boolean unlock() {// 使用Lua脚本保证删除操作的原子性String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";Long result = (Long) redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),Collections.singletonList(lockKey),lockValue);return result != null && result > 0;}// 带超时等待的获取锁(轮询重试)public boolean tryLock(long waitTime, TimeUnit unit) throws InterruptedException {long timeout = unit.toMillis(waitTime);long start = System.currentTimeMillis();while (true) {if (tryLock()) {return true;}// 等待重试(避免自旋过于频繁)long remaining = timeout - (System.currentTimeMillis() - start);if (remaining <= 0) {return false; // 超时未获取到锁}Thread.sleep(Math.min(remaining, 100)); // 最多等待100ms重试}}
}

关键设计点

  1. 锁标识(lockValue):用 UUID + 线程 ID 区分持有者,避免释放其他节点的锁。
  2. 过期时间:需大于任务执行时间(如任务耗时 5s,锁过期设 10s),防止节点宕机导致锁永久持有。
  3. 续约机制:若任务执行时间可能超过锁过期时间,需启动后台线程定期续约(如每 3s 续期 10s)。

二、任务调度核心原理(以 XXL-Job 为例)

1. 调度中心与执行器通信流程
  • 执行器注册:执行器启动时通过 HTTP 请求向调度中心注册(携带 appname、IP、端口)。
  • 任务触发:调度中心根据 CRON 表达式计算下次执行时间,到达时间后通过线程池触发任务,向执行器发送 HTTP 请求(POST 方式)。
  • 执行反馈:执行器执行完任务后,将结果(成功 / 失败、日志)同步回调度中心。
2. 路由策略与负载均衡

XXL-Job 支持多种路由策略,解决任务在集群节点的分配问题:

  • 第一个节点:固定选择集群中第一个在线节点(适合单节点执行的任务)。
  • 轮询:按顺序依次分配给在线节点(均衡负载)。
  • 分片广播:所有在线节点同时执行,每个节点处理不同分片(适合大规模任务)。

分片示例:100 万条数据需批量处理,分为 5 个分片,集群 3 个节点:

@XxlJob("shardingTask")
public ReturnT<String> shardingHandler(String param) {// 获取分片参数(由调度中心分配)ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo();int shardIndex = shardingVO.getIndex(); // 当前分片索引(0-4)int shardTotal = shardingVO.getTotal(); // 总分片数(5)// 按分片处理数据(如按ID取模:id % shardTotal == shardIndex)List<Data> dataList = dataService.queryBySharding(shardIndex, shardTotal);for (Data data : dataList) {processData(data);}return ReturnT.SUCCESS;
}

三、高可用设计(避免单点故障)

1. 调度中心集群化
  • 部署方式:多实例部署(如 2 个节点),通过 Nginx 负载均衡对外提供服务。
  • 数据一致性:依赖 MySQL 主从同步(调度中心数据存储在 MySQL),确保多实例数据一致。
2. 执行器故障转移
  • 心跳检测:执行器定期向调度中心发送心跳(默认 30s 一次),超过 90s 未心跳则标记为离线。
  • 任务转移:若执行器离线,调度中心会将其负责的任务分配给其他在线节点(需任务支持重执行)。

四、监控与告警体系

1. 核心监控指标
  • 任务维度:执行次数、成功率、平均耗时、最大耗时。
  • 节点维度:CPU 使用率、内存占用、任务并发数。
2. 集成 Prometheus 监控
// 自定义任务执行指标(使用Micrometer)
@Component
public class TaskMetrics {private final MeterRegistry meterRegistry;public TaskMetrics(MeterRegistry meterRegistry) {this.meterRegistry = meterRegistry;}// 记录任务执行耗时public void recordTaskDuration(String taskName, long durationMs) {Timer.builder("task.execution.duration").tag("task", taskName).register(meterRegistry).record(durationMs, TimeUnit.MILLISECONDS);}// 记录任务失败次数public void incrementFailCount(String taskName) {Counter.builder("task.execution.fail").tag("task", taskName).register(meterRegistry).increment();}
}

在任务执行中埋点:

@XxlJob("orderTimeoutTask")
public ReturnT<String> orderTimeoutHandler(String param) {long start = System.currentTimeMillis();try {// 任务逻辑...metrics.recordTaskDuration("orderTimeoutTask", System.currentTimeMillis() - start);return ReturnT.SUCCESS;} catch (Exception e) {metrics.incrementFailCount("orderTimeoutTask");return ReturnT.FAIL;}
}
3. 告警配置

通过 Grafana 设置告警规则(如任务失败率 > 5% 时触发告警),并集成钉钉 / 企业微信机器人:

// 钉钉告警示例
public class DingTalkAlarm {private final String webhook;public void sendAlarm(String message) {HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);Map<String, Object> body = new HashMap<>();body.put("msgtype", "text");body.put("text", Map.of("content", "定时任务告警:" + message));new RestTemplate().postForObject(webhook, new HttpEntity<>(body, headers), String.class);}
}

五、自定义轻量级方案(无框架依赖)

若场景简单(如无动态配置需求),可基于 Redis + 线程池实现极简方案:

@Component
public class RedisScheduledTask {@Autowiredprivate RedisTemplate<String, String> redisTemplate;@Autowiredprivate TaskService taskService;// 初始化定时任务(每分钟执行一次)@PostConstructpublic void init() {ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();executor.scheduleAtFixedRate(this::executeTask, 0, 1, TimeUnit.MINUTES);}// 执行任务(加分布式锁)private void executeTask() {String lockKey = "task:order:timeout";String requestId = UUID.randomUUID().toString();RedisDistributedLock lock = new RedisDistributedLock(redisTemplate, lockKey, requestId, 60000);try {if (lock.tryLock()) {// 执行核心逻辑taskService.processTimeoutOrders();} else {log.info("任务被其他节点执行,当前节点跳过");}} finally {lock.unlock(); // 释放锁}}
}

六、避坑指南

  1. 锁过期时间设置:需大于任务最大执行时间(可通过压测评估),避免任务未执行完锁已释放。
  2. 任务幂等性:即使加了锁,仍需保证任务可重复执行(如使用UPDATE orders SET status=1 WHERE id=? AND status=0)。
  3. 线程池隔离:核心任务与非核心任务使用独立线程池(如Executors.newScheduledThreadPool(5)),避免相互阻塞。
  4. 日志追踪:任务执行日志需包含唯一 ID(如订单号),便于问题排查。

通过以上细节设计,可构建既高效又可靠的分布式定时任务系统,兼顾性能、可用性和可运维性。实际项目中,建议优先选用 XXL-Job 等成熟框架,减少重复开发;特殊场景下再考虑自定义方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/94751.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/94751.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kafka 的基本操作(1)

Kafka 是一个分布式流处理平台&#xff0c;核心功能是高吞吐量的消息发布与订阅。以下是 Kafka 最常用的基本操作&#xff0c;涵盖环境启动、主题管理、消息生产与消费等核心场景&#xff08;基于 Kafka 2.x 版本&#xff0c;使用命令行工具&#xff09;。 一、环境准备与启动 …

React 为什么要自定义 Hooks?

历史相关文章2024年&#xff1a; React 为什么引入 Hooks &#xff1f; React 中&#xff0c;Hook 是一个特定的概念 自定义 Hook&#xff08;Custom Hook&#xff09;在 React 中相当于&#xff1a; ✅ 一个可以复用的逻辑片段&#xff0c;封装了多个内置 Hooks 的组合和行为 …

[激光原理与应用-181]:测量仪器 - 频谱型 - 干涉仪,OCT(光学相干断层扫描技术)

OCT&#xff08;光学相干断层扫描技术&#xff09;的核心工作原理基于低相干光干涉&#xff0c;通过测量生物组织或材料内部不同深度结构的背向散射光信号差异&#xff0c;构建高分辨率的二维或三维图像。以下是其工作原理的详细解析&#xff1a;一、基础原理&#xff1a;低相干…

python学智能算法(三十五)|SVM-软边界拉格朗日方程乘子非负性理解

【1】引言 前序学习进程中&#xff0c;已经学习了构建SVM软边界拉格朗日方程&#xff0c;具体方程形式为&#xff1a; L(w,b,ξ,α,μ)12∣∣w∣∣2C∑i1nξi−∑i1nαi[yi(w⋅xib)−1ξi]−∑i1nμiξiL(w,b,\xi,\alpha,\mu)\frac{1}{2}||w||^2C\sum_{i1}^{n}\xi_{i}-\sum_{i…

LeetCode 刷题【34. 在排序数组中查找元素的第一个和最后一个位置、35. 搜索插入位置】

34. 在排序数组中查找元素的第一个和最后一个位置 自己做 解&#xff1a;二分查找 class Solution { public://二分查找int halfFind(vector<int> nums, int begin, int end, int target){if(begin > end) //找不到的情况return -1;int mid (begin end) / …

Vue3 计算属性与监听器

文章目录计算属性配置项 computedHTML 结构Vue 实例数据方法计算属性绑定数据和方法完整代码vue3商品加减案例监听器配置项 watch简单类型写法深度监听写法计算属性配置项 computed 使用 Vue 实现一个商品价格计算器&#xff0c;设置一个初始单价&#xff0c;初始数量为 1&…

Mysql如何迁移数据库数据

文章目录一、使用 mysqldump 工具&#xff08;最常用&#xff09;&#xff08;一&#xff09;导出数据&#xff08;二&#xff09;导出数据库&#xff08;不含数据&#xff09;&#xff08;三&#xff09;导出指定表&#xff08;四&#xff09;导入数据二、直接拷贝文件三、使用…

为什么输入 URL 后会显示页面?HTTP 协议的 “幕后操作”

&#x1f680; 浏览器输入URL后&#xff0c;到底发生了什么&#xff1f;前端面试HTTP协议深度解析 今天咱们不聊八卦&#xff0c;来点硬核的——前端面试中绕不开的HTTP协议。是不是一提到“浏览器输入URL后发生了什么”&#xff0c;你就开始头大&#xff1f;别担心&#xff0c…

内网穿透原理和部署教程

前言&#xff1a;本文介绍了内网穿透技术原理及frp工具的部署方法。由于NAT映射表是临时且单向的&#xff0c;外网无法直接访问内网服务。通过部署公网服务器作为中转&#xff0c;frp实现了内网服务的穿透访问。具体步骤包括&#xff1a;下载frp软件包&#xff0c;详细说明了配…

Ping32:为企业数据安全筑起铜墙铁壁​

Ping32&#xff1a;为企业数据安全筑起铜墙铁壁在数字经济飞速发展的今天&#xff0c;企业数据已成为核心竞争力的重要组成部分。然而&#xff0c;数据泄露事件频发&#xff0c;给企业带来的损失难以估量。从商业机密外泄到客户信息曝光&#xff0c;每一次数据安全事故都可能让…

2025年国内iPaaS平台精选

在过去几年里&#xff0c;许多企业在业务系统中面临了诸多有关集成的难题&#xff1a;系统建好了&#xff0c;数据流不动&#xff1b;接口打通了&#xff0c;流程仍卡顿&#xff1b;工具堆叠越来越多&#xff0c;但协同效率反而走低。 这并不是架构设计的问题&#xff0c;也不是…

AD绘制PCB之-板外形设计

1、通过机械层1 【Mechanical 1】绘制出板子轮廓2、选中上面绘制得轮廓先选中一条边&#xff0c;然后按tab键&#xff0c;可以自动选择这条边闭合得线条3、按照选择对象定义设计--->板子形状------>按照选择对象定义执行后得效果&#xff1a;4、根据需要设置板子四角为半…

《汇编语言:基于X86处理器》第12章 浮点数处理与指令编码(2)

Intel X86架构数据的运算主要由通用寄存器处理&#xff0c;但浮点数例外&#xff0c;浮点数的运算由专门的FPU寄存器处理。二进制浮点数由三部分组成&#xff1a;符号&#xff0c;有效数字和阶码。这些格式都出自由IEEE组织制定的标准754-1985&#xff1a;以下是三种浮点数的格…

vue3通过按钮实现横向滚动、鼠标滚动横坐标滚动

效果图&#xff1a;可点击左右文字进行滚动、或通过滚动鼠标 内容左右滚动<template><div class"Home"><div style"display: flex;height: 100%;align-items: center;"><div click"scrollLeft()" style"width: 80px;t…

【Agent】AutoGen:LLM驱动的多Agent对话框架

文章目录一、AutoGen简介1.1 AutoGen的特点1.2 AutoGen的实现1.2.1 可对话Agent1.2.2 对话编程二、基于AutoGen构建多智能体系统2.1 构建步骤2.1 协作模式2.2 通信模型2.3 人机协同2.4 具体示例参考资料一、AutoGen简介 AutoGen是微软推出的一个Multi-Agent框架&#xff0c;允…

乙巳年闰六月十六凌晨感怀

乙巳年闰六月十六凌晨感怀 一段历程一段情&#xff0c;儿郎峥嵘儿郎行。 岁月流金建功业&#xff0c;春秋风尚能潮赢。 路途苦乐人生度&#xff0c;评说成败当下名。 百年孤寂留水墨&#xff0c;千载独步守安宁。

Redis 分布式Session

一、引入依赖引入spring-session-data-redis依赖&#xff0c;不需要指定version&#xff0c;默认和springboot的version保持一致<!-- Spring Session Redis --> <dependency><groupId>org.springframework.session</groupId><artifactId>spring…

JAVA实现附件分片上传

项目需求由于文件服务器的限制&#xff0c;单次调用文件上传接口上传的附件的大小不能超过500MB&#xff0c;对于超过500MB的附件需要分片上传程序示例private Boolean uploadFile(File uploadFile, String uploadUrl, List<Object> fileList) {final long CHUNK_SIZE 5…

PyTorch环境安装

pytorch安装 建议&#xff08;非常强烈的那种&#xff09;用Anaconda创建一个虚拟环境&#xff0c;用于运行安装你的PyTorch conda create -n universal python3.9 1. 基础认知 cuDNN&#xff08;CUDA Deep Neural Network library&#xff09;是 NVIDIA 开发的用于深度学习…

机场风云:AI 云厂商的暗战,广告大战一触即发

文 | 大力财经机场广告牌背后&#xff0c;一场决定云计算未来格局的隐形战争已悄然打响。当你匆匆走过首都机场T3航站楼的通道&#xff0c;巨幅屏幕上“阿里云&#xff1a;开源的力量”与不远处“百度智能云&#xff1a;AI落地领导者”的广告交相辉映。它们精准锁定着日均10万的…