当前位置: 首页 > news >正文

微服务集成snail-job分布式定时任务系统实践

前言

从事开发工作的同学,应该对定时任务的概念并不陌生,就是我们的系统在运行过程中能够自动执行的一些任务、工作流程,无需人工干预。常见的使用场景包括:数据库的定时备份、文件系统的定时上传云端服务、每天早上的业务报表数据生成等等,实现方式也是层出不穷,对于简单的任务,我们可能在代码里写个死循环一直判断是否触发执行即可,对于复杂的、或者定时任务的种类数量较多,需要严密监控管理的业务场景,我们需要一些专门的定时任务工具去处理。笔者用过的定时任务有三个,分别是QuartZ,XXL-JOB,Snail-Job,关于这三个工具,我做了一个表格用来区分其使用场景和特点。

对比维度QuartzXXL-JOB
项目定位经典的 Java 定时任务框架,专注于任务调度核心能力分布式任务调度平台,强调易用性和企业级特性轻量级分布式任务调度框架,注重性能和简洁性
核心架构单机 / 集群模式(基于数据库锁实现集群)中心化架构(调度中心 + 执行器)去中心化架构(无单独调度中心,节点对等)
任务触发方式基于 Cron 表达式、固定间隔、日历等支持 Cron 表达式、固定间隔、任务依赖、API 触发支持 Cron 表达式、固定间隔、事件触发
分布式能力需手动配置集群(依赖数据库同步状态)原生支持分布式部署,自动负载均衡原生分布式,节点动态发现,自动容错
任务管理需通过代码配置,无可视化界面提供 Web 控制台,支持任务 CRUD、启停、日志查看轻量控制台,支持基础任务管理和监控
失败重试支持简单重试策略(需手动配置)内置失败重试机制,可配置重试次数和间隔支持自定义重试策略,默认失败告警
任务依赖不直接支持,需手动实现依赖逻辑支持任务链式依赖、父子任务关系支持简单任务依赖,基于事件驱动
监控告警无内置监控,需集成第三方工具(如 Prometheus)内置监控面板,支持邮件、钉钉等告警方式支持指标暴露(适配 Prometheus),告警集成
性能表现单机性能稳定,集群模式下受数据库性能限制调度中心性能优异,支持高并发任务触发去中心化设计,减少网络开销,性能高效
学习成本中等(需理解 Job、Trigger、Scheduler 等核心概念)低(文档丰富,开箱即用,API 简洁)低(设计简洁,源码轻量,易于上手)
适用场景中小型应用、非分布式环境,或作为底层调度组件中大型分布式系统、企业级应用,需可视化管理微服务架构、轻量级应用、对性能敏感的场景
生态集成可集成 Spring、Spring Boot 等框架深度集成 Spring 生态,支持 Docker、K8s 部署支持 Spring Boot 自动配置,适配主流框架
社区活跃度老牌项目,社区成熟但更新较慢社区活跃,更新频繁,问题响应及时新兴项目,社区正在成长,维护积极
  1. Quartz:作为定时任务领域的 “老前辈”,其核心优势在于稳定性和灵活性,适合作为底层调度引擎,但在分布式场景下需要额外开发适配逻辑,更适合传统单体应用或对定制化要求高的场景。

  2. XXL-JOB:目前国内使用最广泛的分布式任务调度平台之一,凭借完善的功能和易用性成为企业级首选,尤其适合需要可视化管理、复杂任务依赖和高可靠性的场景(如电商定时任务、数据同步等)。

  3. Snail-Job:作为新兴框架,主打轻量和性能,去中心化设计减少了单点故障风险,适合微服务、云原生环境,或对部署复杂度和资源占用有严格要求的场景。

前两种笔者都在实际开发中使用过,相比Quartz,XXL-JOB功能更为强大,但是也是比较重了,所以我在网上寻找有没有类似的替代工具。哎还真让我找到了,就是snail-job,轻量级的分布式任务重试、任务调度平台,特别适合微服务项目,而且界面清爽颜值不错,简单好用,没有复杂配置,接下来的内容我会逐步介绍如何在我们的微服务项目种集成它。不过我在这里需要声明的一点是,企业级的大型项目,还是使用XXL-JOB,毕竟社区活跃,有很多技术支持,snail-job的很多技术问题,是需要付费才能获得解答,这一点让笔者不太舒服,而且开源也只开了一半,前端的代码给的是打包编译后的文件,不过一般的项目用着还是可以的。话不多说,直接上集成教程。


一、snail-job简介

snail-job的官网地址:官网

在这里插入图片描述

上面的是它的官网界面截图,snail-job分为服务端客户端两组概念,这里简单介绍下

  1. 服务端:用于管理和配置定时任务、监控定时任务的执行、告警配置等,总的来说就是定时任务的统筹调度中心
  2. 客户端:这个是需要我们自己开发的部分,开发语言目前支持Python、Java,后续会支持其他语言,比如Go语言,主要是我们用来写定时任务的执行逻辑的。

二、端口概念介绍

先介绍下snail-job集成涉及到的四个端口的概念,防止混淆不清

1.服务端应用端口:这个端口是后台服务端的管理web页面的入口端口,这个是可以自由配置修改的,snail-job的服务端和XXL-JOB一样有一个管理页面。
2. 服务端通讯端口:服务端对客户端暴露的用来通信的端口,默认是17888,可以修改。
3. 客户端应用端口:这是我们自己开发的客户端服务的应用端口,也是自定义配置的。
4. 客户端通讯端口:这个是我们开发的客户端对服务端暴露的进行通信的端口,这个官网给的配置默认值是1789,当然支持修改。

之所以是高性能通信,因为其内部使用了netty框架,除了应用端口,单独再搞两个通信端口,一是为了通信不被干扰,二是为了通信数据的安全考虑,可以使用各种加密技术。本质是将 “用户交互层” 和 “核心调度层” 解耦,在功能、协议、性能、安全等层面实现精细化管控,最终保障分布式任务调度的高效、稳定和可维护性。这一设计在主流分布式框架(如 XXL-Job、Dubbo)中也较为常见,是分布式系统架构的典型优化思路。


三、服务端集成部署

代码拉取地址如下

# Gitee
git clone https://gitee.com/aizuda/snail-job.git# GitHub
git clone https://github.com/aizuda/snail-job.git

国内用户直接使用gitee地址拉取就行,项目结构如下

在这里插入图片描述
项目拉下来后,笔者因为要和自己的微服务集成,所以对源码做了一些简单的修改,比如docker文件夹,就是我新加的,docker相关配置内容,dockerfile等。

还有就是,我把这个后端的服务也集成了nacos,使得能成功注册到nacos中去,以便能和客户端通信。下面是具体操作步骤。

引入nacos

在这里插入图片描述

如上图所示,在源码的相关路径下引入nacos依赖,一个是注册中心配置,一个是配置中心,这里的配置中心我其实是没有使用了,即只是把服务注册到了nacos上

 <!-- SpringCloud Alibaba Nacos --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId><version>2023.0.3.2</version></dependency><!-- SpringCloud Alibaba Nacos Config --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId><version>2023.0.3.2</version></dependency>

application.yml文件修改

server:port: 9082servlet:context-path: /snail-jobspring:application:name: snail-job-serverprofiles:active: prodcloud:nacos:# 注册中心discovery:server-addr: ****:8848namespace: hulei-devgroup: DEFAULT_GROUPusername: ******password: ******# 配置中心config:import-check:enabled: falseserver-addr: ****:8848namespace: hulei-devgroup: DEFAULT_GROUPusername: ******password: ******file-extension: ymldatasource:name: snail_job## mysqldriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/snail_job?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=Asia/Shanghaiusername: rootpassword: root
#    url: jdbc:mysql://usteu-it.rwlb.rds.aliyuncs.com:3306/snail_job?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=Asia/Shanghai
#    username: usteu_dev
#    password: KDNT_dev999## postgres#    driver-class-name: org.postgresql.Driver#    url: jdbc:postgresql://localhost:5432/snail_job?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true#    username: root#    password: root## Oracle#    driver-class-name: oracle.jdbc.OracleDriver#    url: jdbc:oracle:thin:@//localhost:1521/XEPDB1#    username: snail_job#    password: SnailJob## SQL Server#    driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver#    url: jdbc:sqlserver://localhost:1433;DatabaseName=snail_job;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true#    username: SA#    password: SnailJob@24## mariadb#    driver-class-name: org.mariadb.jdbc.Driver#    url: jdbc:mariadb://localhost:3308/snail_job?useSSL=false&characterEncoding=utf8&useUnicode=true#    username: root#    password: root## dm8#    driver-class-name: dm.jdbc.driver.DmDriver#    url: jdbc:dm://127.0.0.1:5236#    username: SYSDBA#    password: SYSDBA001# kingbase#    driver-class-name: com.kingbase8.Driver#    url: jdbc:kingbase8://localhost:54321/test#    username: root#    password: roottype: com.zaxxer.hikari.HikariDataSourcehikari:connection-timeout: 30000minimum-idle: 5maximum-pool-size: 100auto-commit: trueidle-timeout: 30000pool-name: snail_jobmax-lifetime: 1800000web:resources:static-locations: classpath:admin/mybatis-plus:typeAliasesPackage: com.aizuda.snailjob.template.datasource.persistence.poglobal-config:db-config:where-strategy: NOT_EMPTYcapital-mode: falselogic-delete-value: 1logic-not-delete-value: 0configuration:map-underscore-to-camel-case: truecache-enabled: truelogging:config: classpath:logback-boot.xml
#    level:
#    ## 方便调试 SQL
#        com.aizuda.snailjob.template.datasource.persistence.mapper: debugsnail-job:retry-pull-page-size: 1000 # 拉取重试数据的每批次的大小job-pull-page-size: 1000 # 拉取重试数据的每批次的大小server-port: 17888  # 服务器端口log-storage: 7 # 日志保存时间(单位: day)rpc-type: grpc

从上面的yml文件可以看到,我的服务端的应用端口配置的是9082这个端口,关于服务端的通信端口,在如上文件底部所示为17888,这个是默认的,一般没必要不去修改它。

服务端使用的数据库,我用的是mysql,所以我注释了其他数据库的连接配置。一些建表的sql脚本自然也就是mysql相关的,sql脚本位置如下

在这里插入图片描述
把对应的sql脚本拷贝到,application.yml中,你配置的mysql数据库中执行即可,执行后的自动创建的数据库名称就叫snail-job

关于docker文件,我没有使用项目自带的doc/docker下的任何内容,而是我直接在项目根目录下创建了一个文件夹docker,里面配置了我自己的dockerfile,内容如下,给大家做个参考

# 基础镜像
FROM  openjdk:17-jdk
# author
MAINTAINER usteuENV JAVA_OPTS="-Xms1g -Xmx2g -XX:+UseG1GC"# 挂载目录
VOLUME /home/snailjob-server
# 创建目录
RUN mkdir -p /home/snailjob-server
# 指定路径
WORKDIR /home/snailjob-server
# 复制jar文件到路径
COPY ./jar/snail-job-server-exec.jar /home/snailjob-server/snail-job-server-exec.jar
# 启动系统服务
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar snail-job-server-exec.jar","-Duser.timezone=GMT+08"]

docker-compose.yml中有关snail-job服务端的service配置内容如下,可以看到,我映射了宿主机和容器的两个端口

  #snail-job的服务端docker部署,使用的是本地拉取的源码打包部署,非镜像snail-job-server:container_name: snail-job-serverbuild:context: ./snailjobServerdockerfile: dockerfileports:- "9082:9082"- "17888:17888"volumes:- /etc/localtime:/etc/localtime:ro# 添加日志限制,也可以全局限制,和services同级logging:driver: "json-file"options:max-size: "100m"max-file: "1"

读者朋友也可以不用docker部署,这完全看个人需求,核心是snail-job-server-exec.jar这个jar包,至于怎么部署使用随便。

snail-job-server-exec.jar

这个就是我们package后的可执行jar包了,里面包含服务端的前端页面内容,具体位置如下

在这里插入图片描述

笔者使用的工具是IDEA,打包时操作如下图所示,注意一定要勾选skipFrontend,否则就会打包失败,前面说了前端并没有给源码,对此笔者也无力吐槽,毕竟人家搞出来这个东西就是为了挣钱的,免费给你用也还算可以了,要求不要太高哈。

在这里插入图片描述

服务端启动登录

对面上面的配置,使用IDEA启动后登录界面如下

浏览器访问地址:http://localhost:9082/snail-job

在这里插入图片描述

默认登录用户admin,密码也是admin,可以修改密码,也可以新增普通用户进行权限管控,这个后面再说,先登录进去

在这里插入图片描述
可以看到一个在线机器,为服务端机器,同时nacos服务也多了一个snail-job-server的服务

在这里插入图片描述

新建命名空间

这个只有admin账户或者拥有管理员权限的账户才能新建,有一个默认的default,但我不想用,所以自己建了,名字什么的更专业点。

在这里插入图片描述

新建执行器组

上面选择新建的命名空间,然后新增执行器组,这里的token后面客户端会用到,比较重要

在这里插入图片描述

在这里插入图片描述

新建用户

在这里插入图片描述
这个比较简单了,就是新建用户,赋予对应执行器组的权限,也可以对admin用户密码进行修改,不再叙述。

告警通知配置

这个主要是用来针对定时任务执行异常时的通知配置,可以配置通知人,通知方式等,实际使用中一般是,针对某个组的某个定时任务去配置,设置对应的通知人。

在这里插入图片描述

定时任务配置

这个是核心了,支持按照固定时间间隔、cron表达式、指定时间点、工作流方式,具体请看官方文档介绍,十分详细了。
在这里插入图片描述

任务类型一般就是默认集群。集群模式下,一个任务会根据路由策略只打到一个节点上执行。
执行器选择自定义执行器,里面的名称是我们在客户端代码些定时任务逻辑时对应的,不是乱填的。

在这里插入图片描述


四、客户端开发部署

这部分内容需要我们自己在应用内开发了,也非常简单,就拿笔者的微服务项目来展示吧

在这里插入图片描述

创建一个snail-job的客户端微服务,也是要注册到nacos当中去的,pom文件主要内容如下

<!-- SpringCloud Alibaba Nacos --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!-- SpringCloud Alibaba Nacos Config --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><!--snail-job相关组件--><dependency><groupId>com.aizuda</groupId><artifactId>snail-job-client-starter</artifactId><version>${snail-job.version}</version></dependency><dependency><groupId>com.aizuda</groupId><artifactId>snail-job-client-job-core</artifactId><version>${snail-job.version}</version></dependency><dependency><groupId>com.aizuda</groupId><artifactId>snail-job-client-retry-core</artifactId><version>${snail-job.version}</version></dependency>

上面只是列举的必要的依赖,可能还需要其他依赖,比如数据库驱动等依赖。这个服务就是客户端服务了,客户端的应用端口我设置成了9081,yml配置文件如下

server:port: 9081spring:application:name: usteu-snailjobprofiles:active: @activatedProperties@cloud:nacos:# 注册中心discovery:server-addr: @nacosAddress@namespace: @nacosNamespace@group: DEFAULT_GROUPusername: nacospassword: KDNT_dev666# 配置中心config:server-addr: @nacosAddress@namespace: @nacosNamespace@group: DEFAULT_GROUPusername: nacospassword: KDNT_dev666file-extension: ymlshared-configs:- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}

其中还有部分配置,我是放在nacos中了,关键配置如下:

snail-job:# 任务调度服务器信息server:# 服务器IP地址(或域名);集群时建议通过 nginx 做负载均衡host: 127.0.0.1# 服务器通讯端口(不是后台管理页面服务端口)port: 17888# 命名空间 【上面配置的空间的唯一标识】namespace: vL4pfYMz8vFf5m0PXItAVK_aA9pGpH6L# 接入组名【上面配置的组名称】注意: 若通过注解配置了这里的配置不生效group: usteu_group# 接入组 token 【上面配置的token信息】token: SJ_Wyz3dmsdbDOkDujOTSSoBjGQP1BMsVnj# 客户端绑定IP,必须服务器可以访问到;默认自动推断,在服务器无法调度客户端时需要手动配置host: 127.0.0.1# 指定客户端通讯端口,不配置默认 1789port: 1789# 是否开启enabled: true

这里客户端配置也是需要写上服务端的ip地址和通信端口号的,之前说过是17888,剩下的命名空间,组名称,以及执行器组对应的token,都可以登录服务端管理界面去查找,客户端的通讯端口,笔者这里配置的是1789,默认值也是这个。

定时任务编写

我根据官方提供的案例,写了一个测试的定时任务

import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
import com.aizuda.snailjob.client.job.core.dto.JobArgs;
import com.aizuda.snailjob.client.model.ExecuteResult;
import com.aizuda.snailjob.common.log.SnailJobLog;
import org.springframework.stereotype.Component;/*** 测试定时任务*/
@Component
@JobExecutor(name = "testJob")
public class TestJob {public ExecuteResult jobExecute(JobArgs jobArgs) {SnailJobLog.REMOTE.info("哈哈,测试成功了:"+jobArgs.getJobId() + " " + jobArgs.getJobParams());return ExecuteResult.success();}
}

这个@JobExecutor(name = “testJob”)就是核心注解了,testJob即为我们在服务端进行定时配置时设置的执行器名称,要对应上。

另外要注意的点是,再定义其他定时器时,方法名不要修改统一都是jobExecute,否则会执行报错,我估计是反射时会寻找这个固定的执行方法吧。具体的定时任务执行业务逻辑,在方法内部写即可。

附上一张测试结果图

在这里插入图片描述


五、总结

以上内容简单介绍了snail-job的使用方法和集成步骤,具体细节,如有不明白的,可参考官方文档,学习测试。希望本文可以给各位带来帮助。

http://www.dtcms.com/a/270120.html

相关文章:

  • Mac安装Docker(使用orbstack代替)
  • 单机分布式一体化数据库的架构设计与优化
  • 一个猜想不等式的推广
  • 业务分析技术实践篇
  • kafka集群安装
  • 让事情变得更好
  • Shader面试题100道之(21-40)
  • 光流 | RAFT光流算法如何改进提升
  • 【适合 Java 工程师的 AI 转型方向】
  • 基于PHP/MySQL的企业培训考试系统源码,高并发、稳定运行,源码开源可二开
  • Java中的生产消费模型解析
  • Distance Information Improves Heterogeneous Graph Neural Networks
  • 质量小议56 - 说教
  • [ESP32]VSCODE+ESP-IDF环境搭建及blink例程尝试(win10 win11均配置成功)
  • vscode打开stm32CubeIDE的项目的注释问题
  • 从分层训练到一步生成:Kaiming He 的生成模型进化之路—CVPR2025演讲小结
  • 网络--初级
  • springboot单体项目的发布生产优化
  • DMA(直接内存访问)是什么?
  • 第2章,[标签 Win32] :匈牙利标记法
  • 13届蓝桥杯省赛程序设计试题
  • 字符串大小比较的方式|函数的多返回值
  • 作业03-SparkSQL开发
  • 数字化校园升级:传统网络架构与SD-WAN智能方案对比详解
  • 汽车功能安全-软件单元验证 (Software Unit Verification)【定义、目的、要求建议】6
  • 【数据分析】基于 HRS 数据的多变量相关性分析与可视化
  • uniapp b树
  • C++笔记之使用bitset对uint32_t类型变量对位状态判断
  • 2025年深圳杉川机器人性格测评和Verify测评SHL题库高分攻略
  • 论文略读:Parameter-efficient transfer learning for NLP