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

发布于:2025-07-09 ⋅ 阅读:(21) ⋅ 点赞:(0)

前言

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

对比维度 Quartz XXL-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: 9082
  servlet:
    context-path: /snail-job

spring:
  application:
    name: snail-job-server
  profiles:
    active: prod
  cloud:
    nacos:
      # 注册中心
      discovery:
        server-addr: ****:8848
        namespace: hulei-dev
        group: DEFAULT_GROUP
        username: ******
        password: ******
      # 配置中心
      config:
        import-check:
          enabled: false
        server-addr: ****:8848
        namespace: hulei-dev
        group: DEFAULT_GROUP
        username: ******
        password: ******
        file-extension: yml
  datasource:
    name: snail_job
    ## mysql
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/snail_job?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=Asia/Shanghai
    username: root
    password: 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: root
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      connection-timeout: 30000
      minimum-idle: 5
      maximum-pool-size: 100
      auto-commit: true
      idle-timeout: 30000
      pool-name: snail_job
      max-lifetime: 1800000
  web:
    resources:
      static-locations: classpath:admin/


mybatis-plus:
  typeAliasesPackage: com.aizuda.snailjob.template.datasource.persistence.po
  global-config:
    db-config:
      where-strategy: NOT_EMPTY
      capital-mode: false
      logic-delete-value: 1
      logic-not-delete-value: 0
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: true

logging:
  config: classpath:logback-boot.xml
#    level:
#    ## 方便调试 SQL
#        com.aizuda.snailjob.template.datasource.persistence.mapper: debug

snail-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 usteu

ENV 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-server
    build:
      context: ./snailjobServer
      dockerfile: dockerfile
    ports:
      - "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: 9081

spring:
  application:
    name: usteu-snailjob
  profiles:
    active: @activatedProperties@
  cloud:
    nacos:
      # 注册中心
      discovery:
        server-addr: @nacosAddress@
        namespace: @nacosNamespace@
        group: DEFAULT_GROUP
        username: nacos
        password: KDNT_dev666
      # 配置中心
      config:
        server-addr: @nacosAddress@
        namespace: @nacosNamespace@
        group: DEFAULT_GROUP
        username: nacos
        password: KDNT_dev666
        file-extension: yml
        shared-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
  # 指定客户端通讯端口,不配置默认 1789
  port: 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的使用方法和集成步骤,具体细节,如有不明白的,可参考官方文档,学习测试。希望本文可以给各位带来帮助。


网站公告

今日签到

点亮在社区的每一天
去签到