Spring Boot双节点项目中基于HazelCast实现定时邮件发送方法的单节点执行配置方案
针对你遇到的双节点定时任务重复发送邮件的问题,结合你已经配置好的HazelCast集群,这里有两种靠谱的解决方案,帮你实现任务仅在单个节点执行一次:
方案一:结合Spring @Scheduled + HazelCast分布式锁(最小改动)
这种方式几乎不需要改动现有定时任务的结构,只需要在任务执行前加一层分布式锁判断,确保只有拿到锁的节点才执行邮件发送逻辑。
步骤1:注入HazelCast实例
Spring Boot会自动识别你配置的HazelCast集群并创建HazelcastInstance实例,直接注入即可:
@Autowired private HazelcastInstance hazelcastInstance; @Autowired private MailService mailService; private static final Logger log = LoggerFactory.getLogger(YourSchedulerClass.class);
步骤2:修改定时任务方法,加入锁逻辑
给你的定时任务加上分布式锁判断,只有成功获取锁的节点才执行邮件发送:
@Scheduled(cron = "0 0 9 * * ?") public void sendMail(MailDTO mailDTO) { // 定义唯一的锁名称,建议和任务业务强关联,避免和其他任务冲突 ILock taskLock = hazelcastInstance.getLock("daily-mail-send-task-lock"); try { // 尝试获取锁,设置5秒超时(可根据任务实际执行时间调整) if (taskLock.tryLock(5, TimeUnit.SECONDS)) { try { // 仅当前拿到锁的节点执行邮件发送 log.info("Acquired lock, starting to send daily mail..."); mailService.sendMail(mailDTO); } finally { // 无论执行成功与否,都要释放锁 taskLock.unlock(); log.info("Released lock after mail task execution."); } } else { // 未获取到锁,说明其他节点正在执行任务,直接跳过 log.info("Another node is handling the daily mail task, skipping execution on this node."); } } catch (InterruptedException e) { log.error("Failed to acquire lock for daily mail task", e); Thread.currentThread().interrupt(); } }
方案二:使用HazelCast分布式调度器(更优雅的分布式任务管理)
如果希望更贴合分布式系统的任务调度规范,可以直接使用HazelCast提供的IScheduledExecutorService,它会自动在集群中选择一个节点执行任务,无需手动处理锁逻辑。
步骤1:配置分布式调度器Bean
先在Spring容器中配置HazelCast的分布式调度器:
@Configuration public class HazelcastSchedulerConfig { @Bean public IScheduledExecutorService hazelcastScheduledExecutorService(HazelcastInstance hazelcastInstance) { // 创建一个名为"mail-scheduler"的分布式调度器实例 return hazelcastInstance.getScheduledExecutorService("mail-scheduler"); } }
步骤2:初始化分布式定时任务
在你的调度类中,利用@PostConstruct初始化分布式任务:
@Autowired private IScheduledExecutorService scheduledExecutorService; @Autowired private MailService mailService; @PostConstruct public void initDailyMailTask() { // 定义邮件发送任务 Runnable dailyMailTask = () -> { log.info("Executing daily mail task on current node..."); mailService.sendMail(new MailDTO()); // 传入你的MailDTO参数 }; // 配置Cron表达式(每天9点执行) CronScheduleConfig cronConfig = CronScheduleConfig.cron("0 0 9 * * ?"); // 提交分布式定时任务 scheduledExecutorService.schedule(dailyMailTask, cronConfig); log.info("Distributed daily mail task scheduled successfully."); }
关键注意事项
- 锁名称唯一性:方案一中的锁名称必须唯一,确保只针对当前邮件任务,避免和其他分布式任务的锁冲突。
- 锁超时时间:方案一中的
tryLock超时时间要根据任务实际执行时长调整,避免任务还没执行完锁就被释放,导致其他节点重复执行。 - 集群通信:确保你的HazelCast集群节点之间通信正常,当前配置的
multicast适合局域网环境,如果是云服务器集群,建议改用tcp-ip方式配置节点列表。 - 日志排查:添加足够的日志,方便后续排查哪个节点执行了任务,哪个节点跳过了执行。
内容的提问来源于stack exchange,提问作者Artur Vartanyan
相关产品推荐
相关产品推荐

