NatsJob分布式定时任务

7 阅读11分钟

NatsJob

natsjob 是一款基于 NATS 消息中间件 + Go 语言构建的分布式定时任务框架。框架设计不追求功能上的“大而全”,而是以工程实践场景为核心导向,聚焦“轻量、可控、场景”三大目标,为分布式定时任务提供极简且高效的解决方案。

github:

设计核心理念

natsjob 围绕“零侵入、云原生、轻量级、高可用”核心特性构建,并通过标准化通信约定,支持多编程语言、多数据库适配,实现定时任务的灵活交互与管理:

  • 零侵入集成:客户端仅需依赖 NATS 官方 SDK 与 NATS 服务建立通信,即可完成定时任务交互;客户端业务逻辑完全自主掌控,无任何侵入性约束。
  • 云原生适配:全面兼容云原生生态,NATS 与 natsjob 均支持 Docker 容器化部署、K8s 编排管理,适配云原生环境的弹性伸缩与运维体系。
  • 极致轻量化:NATS 二进制 + natsjob 二进制总体积100M左右;运行时资源占用低,即使在高频读写场景下,二者总和的内存占用仍可控制在较低水平。
  • 多语言兼容:依托 NATS 对40+ 种编程语言的全面支持(详见 NATS 官网:nats.io/download/),natsjob 可无缝对接多语言技术栈的业务系统。
  • 多数据库支持:默认SQLite,同时兼容 MySQL、PostgreSQL(含人大金仓 PostgreSQL 兼容模式)、达梦等主流数据库,满足不同数据存储场景需求。手动建库natsjob,服务natsjob连接后会自动建表。
  • 高可用保障:底层基于 NATS 集群模式,natsjob 自身支持主从架构——当 master 节点故障时,集群可30s内自动选举新 master 节点,继续执行任务。
  • 跨平台部署:提供 Windows、Linux、macOS 多系统二进制包,以及 AMD/ARM 架构的 Docker 镜像,适配不同部署环境。
  • 可视化管理:内置 Web 管理界面,支持定时任务的可视化配置:单机、广播、MAP。命名空间互相独立,划分不同场景区域,例如:dev,uat,test等。
架构图
flowchart TD
    %% 定义节点样式
    classDef cluster fill:#e1f5fe,stroke:#01579b,stroke-width:2px;
    classDef nats fill:#fff3e0,stroke:#e65100,stroke-width:2px;
    classDef client fill:#f3e5f5,stroke:#4a148c,stroke-width:1px,stroke-dasharray: 5 5;

    %% 节点定义
    A["NatsJob定时任务集群"]:::cluster
    B["NATS 集群<br/>(核心消息总线)"]:::nats
    
    subgraph Clients [客户端层]
        direction LR
        C["服务客户端"]:::client
        D["组模式客户端"]:::client
        E["Agent脚本客户端"]:::client
        F["监控客户端"]:::client
        G["自定义客户端"]:::client
    end

    %% 连接关系
    A -- "连接 (提交任务/状态同步)" --> B   
    B <-- "连接 (发布/订阅)" --> C
    B <-- "连接 (队列组订阅)" --> D
    B <-- "连接 (指令下发/心跳)" --> E
    B <-- "连接 (指标采集)" --> F
    B <-- "连接 (自定义)" --> G

NATS

NATS 是一款轻量级、高性能、分布式的开源消息中间件(MQ),专为云原生架构、分布式系统场景设计,具备低延迟、高可用、易部署的核心特性。

官方资源

安装与启动

1. 启动 NATS 服务

必须开启js模式

# Windows 环境
#nats-server.exe -a 0.0.0.0 -p 4222 -m 8222 -js
nats-server.exe -a 0.0.0.0 -p 4222  -js -sd ./data
# Linux/macOS 环境
#nats-server -a 0.0.0.0 -p 4222 -m 8222 -js
nats-server -a 0.0.0.0 -p 4222  -js -sd ./data
# docker
#docker run -d --name nats -p 4222:4222 -p 8222:8222 nats:latest -js
docker run -d \
-v $(pwd)/data:/data \
-p 4222:4222 \
--name nats nats:latest -js -sd /data

# Windows 环境 带账号密码启动
nats-server.exe -a 0.0.0.0 -p 4222 -js -sd ./data --user natsjob --pass natsjob123 
# Linux/macOS 环境 带账号密码启动
nats-server -a 0.0.0.0 -p 4222  -js  -sd ./data --user natsjob --pass natsjob123
# docker 带账号密码启动
docker run -d \
-v $(pwd)/data:/data \
-p 4222:4222 \
--name nats nats:latest -js  -sd ./data --user natsjob --pass natsjob123

参数说明

  • -a 0.0.0.0:监听所有网络地址,允许外部访问
  • -p 4222:NATS 核心通信端口
  • -m 8222:HTTP 监控端口,可以不开启
  • -js:启用 NATS JetStream 功能

2. 启动 natsjob

natsjob 配置参数说明

配置项名称默认值参数说明
NATSJOB_LOG_ENV"console"日志输出方式,console、json两种模式
NATSJOB_NATS_URL"nats://127.0.0.1:4222"NATS 服务连接地址,带账号密码时格式为:nats://用户名:密码@IP:端口
- 示例:nats://natsjob:natsjob123@127.0.0.1:4222
NATSJOB_SECRET_KEY"******"系统秘钥,用于接口签名、Token 加密等安全相关操作,建议自行修改为随机字符串
NATSJOB_HTTP_PORT"7788"Web 管理界面及 HTTP 接口的监听端口
NATSJOB_LOGIN_USER"natsjob"Web 管理界面的默认登录用户名
NATSJOB_LOGIN_PWD"natsjob"Web 管理界面的默认登录密码,建议部署后修改
NATSJOB_LOGIN_TOKEN_EXPIRE_HOURS"2"Web 登录生成的 Token 过期时间(小时),默认 2 小时
NATSJOB_JOB_RESULT_EXPIRE_HOURS"24"定时任务执行结果的保留时长(小时),超时后自动清理,默认 24 小时
NATSJOB_DB_TYPE"sqlite"数据库类型,支持
- sqlite
- mysql
- postgres
- dm(达梦)
NATSJOB_DB_URL""数据库连接地址:
- sqlite 可留空(默认使用本地文件)
- mysql 格式:root:natsjob123@tcp(127.0.0.1:3306)/natsjob?charset=utf8mb4&parseTime=True&loc=Local
- postgres 格式:host=127.0.0.1 user=postgres password=natsjob123 port=5432 dbname=natsjob sslmode=disable TimeZone=Asia/Shanghai
- 达梦 格式:dm://SYSDBA:natsjob123@127.0.0.1:5236?schema=natsjob
NATSJOB_DB_CHAN_COUNT"100"数据库操作的通道数量,仅控制任务结果并发写库的最大数,建议根据数据库性能调整;默认100

启动命令

启动参数优先级: 环境变量(docker/k8s) > 命令行 > 配置文件

###本地config.yaml文件启动,[启动服务就自动创建conf下配置文件]
natsjob.exe 
natsjob

###命令行启动
natsjob.exe --NATSJOB_LOG_ENV=json ...其他环境变量
natsjob --NATSJOB_LOG_ENV=json ...其他环境变量

#docker环境变量(k8s同理)
docker run -idt --restart=always \
-p 7788:7788 \
-e TZ=Asia/Shanghai \
-v /opt/natsjob/logs:/app/logs \
-v /opt/natsjob/data:/app/data \
-v /opt/natsjob/conf:/app/conf \
-e NATSJOB_NATS_URL=nats://127.0.0.1:4222 \
-e NATSJOB_DB_TYPE=mysql \
-e "NATSJOB_DB_URL=root:natsjob123@tcp(127.0.0.1:3306)/natsjob?charset=utf8mb4&parseTime=True&loc=Local" \
--name natsjob luanxinghai/natsjob:latest




Web 管理页面访问

地址:http://127.0.0.1:7788

说明:启动 natsjob 后,直接访问该地址即可进入可视化管理界面,无需额外配置。

Web 页面 Nginx 代理配置

natsjob Web 管理页面支持通过 Nginx 反向代理访问,可集成至统一的服务管理域名下,无需直接暴露后端端口,提升安全性。

完整 Nginx 配置示例

# 配置后端服务上游节点
upstream natsjobweb {
    server 127.0.0.1:7788;  # natsjob Web 服务的实际地址和端口
    # 若部署多实例,可添加多行 server 实现负载均衡,示例:
    # server 127.0.0.1:7789;
}

# 代理 API 接口(适配接口路由)
location /natsjob/api {
    proxy_set_header X-Real-IP $remote_addr;          # 透传客户端真实 IP
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;       # 透传协议(http/https)
    proxy_set_header Host $http_host;                 # 透传请求主机名
    proxy_pass http://natsjobweb$request_uri;         # 转发请求至上游服务
}

# 代理 Web 页面(适配前端路由)
location /natsjob/ {
    proxy_pass http://natsjobweb/;                    # 转发前端页面请求
    proxy_set_header Host $host;                      # 透传主机名
    proxy_set_header X-Real-IP $remote_addr;          # 透传客户端真实 IP
}

定时任务表达式

natsjob 底层采用 github.com/robfig/cron/v3 库实现定时任务调度,支持最小秒级粒度的任务触发,兼容标准 cron 表达式并扩展了秒级能力。

核心依赖信息

表达式含义等价 cron 表达式(秒级)适用场景
@every 1s每 1 秒执行一次*/1 * * * * *高频心跳、实时监控
@every 5s每 5 秒执行一次*/5 * * * * *秒级数据采集
@every 10s每 10 秒执行一次*/10 * * * * *短间隔检测任务
@every 1m每 1 分钟执行一次0 */1 * * * *常规业务定时任务
@every 2m30s每 2 分 30 秒执行一次30 */2 * * * *非整数分钟间隔任务
@every 1h每 1 小时执行一次0 0 */1 * * *小时级统计任务
@every 1h15m每 1 小时 15 分执行0 15 */1 * * *非整数小时间隔任务
@every 1d每 1 天执行一次0 0 0 * * *每日备份、结算
@every 24h每 24 小时执行一次0 0 0 * * *等价 @every 1d
@every 7d每 7 天执行一次0 0 0 * * 0周度数据汇总

注意:@every从任务启动时间开始计算间隔,而非严格对齐时钟(如 @every 1h 若 10:05 启动,下次执行是 11:05,而非 11:00);若需对齐时钟,建议使用标准 cron 表达式。

秒级 cron 表达式示例(6字段格式) 该库默认启用 6 字段(秒 分 时 日 月 周)的秒级扩展,以下是精准控制秒级触发的典型示例:

表达式含义适用场景
0 * * * * *每分钟的第 0 秒执行一次对齐分钟的常规任务
15 * * * * *每分钟的第 15 秒执行一次分钟内精准秒级触发
0,30 * * * * *每分钟的 0 秒、30 秒各执行一次分钟内两次触发
10-20 * * * * *每分钟的 10-20 秒,每秒执行一次分钟内连续秒级任务

压力测试

1. 测试环境

组件配置说明
操作系统Ubuntu 22.04.3(笔记本 VMware 虚拟机)
CPU:4核 内存:8G 硬盘:200G
NATS默认部署(无特殊配置)
natsjob默认部署(无特殊配置)
MySQL 8.0数据库连接池设置:2000

2. 测试场景与结果

测试说明

测试场景为1000个定时任务同时启用+结束,并发写库,数据为多轮测试的平均值,受环境差异影响仅供参考。

注意测试针对并发场景,多少任务数产生这些并发取决于实际场景,可能数万任务才产生这种并发。

并发压力场景natsjob 资源占用NATS 资源占用cron执行时间并发限制超时时间数据库回写结果并发限制
1000 次/10秒内存 < 100M、CPU < 50%内存 < 300M、CPU < 50%1s110s100
2000 次/10秒内存 < 200M、CPU < 100%内存 < 400M、CPU < 100%1s210s100
10000 次/10秒
(长时间运行服务器资源全打满,数据库CPU较高,访问出现异常)
内存 < 400M、CPU < 600%内存 < 600M、CPU < 200%1s1010s100 / 1000(对比测试)

3. 性能瓶颈分析

测试结果表明,系统性能瓶颈主要集中在数据库层,具体评估如下:

  • 低压力场景(并发 < 1000 次/10秒): 低频读写下,natsjob、NATS、数据库均可轻松应对,无资源瓶颈,系统运行稳定。
  • 高压力场景(并发 >= 10000 次/10秒): 高频读写时需重点优化数据库服务器配置(提升 CPU 算力、扩容内存、更换 SSD 硬盘); 核心问题为:定时任务调度结果会缓存至内存,若数据库消费速度低于任务产生速度,长时间运行易引发内存溢出、CPU 占满等异常。

4. 优化建议

  1. 高并发场景下,优先升级数据库服务器硬件配置(SSD 硬盘可显著提升写库速度);
  2. 调整数据库回写并发限制(如从 100 提升至 1000),匹配高并发任务的写库需求;
  3. 超高频任务场景监控内存/CPU 阈值,避免数据库瞬时压力过大;

demo示例

java (兼容各版本JDK)

github.com/luanxinghai…

消费模式说明 本文档中提供的所有示例均基于临时消费模式,该模式轻量、高效,能够满足绝大多数业务场景的需求。

若需使用 NATS JetStream 的高阶特性(如消息确认(ACK)、至少一次(At-Least-Once)消费策略、消息重发等),可参考示例代码:MaxJsTest.java

提示:临时消费模式已覆盖 90%+ 的常规业务场景,无需盲目追求高阶特性;仅在对消息可靠性有强要求(如金融交易、数据同步)的场景下,建议使用 ACK 机制保障消费可靠性。

比划一下单机版示例:

本示例实现 NATS 自动重连,服务不会因 NATS 异常而退出,会持续后台重连,推荐生产环境使用。

        <!-- Source: https://mvnrepository.com/artifact/io.nats/jnats -->
<dependency>
  <groupId>io.nats</groupId>
  <artifactId>jnats</artifactId>
  <version>最新版</version>
</dependency>
 public static void main(String[] args) {
        String natsURL = "nats://127.0.0.1:4222";
        String subject = "natsjob-void.job.start.app.biz.owner";
        String workQueue = "worker-queue";
        //设置自动重连,并打印日志信息
        Options options = new Options.Builder().server(natsURL)
                .connectionListener((conn, type) -> {
                    System.out.println("connectionListener:" + type);
                })
                .build();
        try (Connection nc = Nats.connectReconnectOnConnect(options)) {
            //模拟单机客户端1
            nc.createDispatcher()
                    .subscribe(subject, workQueue, (msg) -> {
                        System.out.printf("work1: %s on subject %s\n", new String(msg.getData(), StandardCharsets.UTF_8), msg.getSubject());
                    });

            //模拟单机客户端2
            nc.createDispatcher()
                    .subscribe(subject, workQueue, (msg) -> {
                        System.out.printf("work2: %s on subject %s\n", new String(msg.getData(), StandardCharsets.UTF_8), msg.getSubject());
                    });
            //可继续模拟单机客户端....无数个

            System.out.println("start success...");
            Thread.currentThread().join();
        } catch (InterruptedException | IOException e) {
            e.printStackTrace();
        }
    }

skills

你也可以使用skill生成代码,前面有skills路径文件

你读取这个skill 给我一个java  单机版 max场景的代码示例

其他语言

python go .net node等可用大模型智能体将java示例转换即可(亲测可行),为大模型点赞,强的一批!!!

打包部署

前端

pnpm i
pnpm build

打包在dist文件夹内,将dist中静态文件拷贝到后端的static目录中(先清空static目录)

后端

#linux打包
./script/build.sh

#windows打包
./script/build.bat

就可以在lib目录中看到打包好的文件

制作镜像

docker目录下有打包sh和Dockerfile文件,可以根据需要修改