Distributed Task Scheduling with XXL-JOB
Overview
What is Task Scheduling?
Consider the following real-world scenarios:
- A coupon distribution system that automatically issues vouchers at 10:00 AM, 3:00 PM, and 8:00 PM daily
- A banking application that sends SMS reminders three days before credit card payment due dates
- A finance platform that reconciles and aggregates the previous day's transactions at 00:10 every morning
These are the types of problems task scheduling is designed to solve. Task scheduling automates the execution of specific jobs at predetermined times without manual intervention.
Why Distributed Scheduling?
Spring Framework provides a built-in scheduling mechanism through the @Scheduled annotation. By annotating a method in a service class and enabling scheduling on the application entry point, you can achieve basic task automation:
@Scheduled(cron = "0/20 * * * * ?")
public void processBatch() {
// batch processing logic
}
@SpringBootApplication
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
This approach works well for single-instance deployments. However, when your application runs acros multiple nodes in a distributed or clustered environment, serious problems emerge.
Consider a daily point accumulation feature that awards users bonus points at midnight. In a single-server setup, @Scheduled handles this correctly. But in a clustered environment where three application instances are running behind a load balancer, each instance would independently trigger the job—resulting in triple credit issuance for every user.
While you could theoretically configure only one instance to run scheduled tasks, this introduces critical vulnerabilities:
- Single Point of Failure: If the designated instance crashes or becomes unavailable, the scheduled task never executes.
- Resource Bottleneck: A single server must handle the full computational load of all scheduled jobs, leading to performance degradation.
- Scalability Limits: When order volume grows from 10,000 to 100,000 per minute, a single server's CPU, memory, and I/O capacity becomes a hard ceiling that cannot be exceeded regardless of optimization efforts.
Distributed scheduling solves these problems by centralizing job coordination while allowing execution to be distributed across multiple nodes.
XXL-JOB Architecture
XXL-JOB is an open-source distributed task scheduling platform originally developed by Dianping. It is designed for rapid development, minimal learning curve, lightweight footprint, and easy extensibility. The platform has been running in production at Dianping, handling over one million job executions.
Other companies known to use XXL-JOB include JD.com, Uxin, 360 Financial, Lenovo, and NetEase.
The architecture separates concerns into two core components:
- Admin (调度中心): The centralized management console responsible for job registration, triggering requests, and monitoring. It does not contain any business logic.
- Executor: The worker component deployed alongside your application that receives调度 requests from the admin and executes the corresponding job handlers.
This decoupling ensures that business logic and scheduling logic remain independent, improving system stability and making horizontal scaling straightforward.
Quick Start Guide
Source Code
Clone the repository:
git clone https://gitee.com/xuxueli03.23/xxl-job.git
Key directories:
| Directory | Purpose |
|---|---|
doc/ |
Database scripts and documentation |
xxl-job-core/ |
Shared library containing core scheduling logic |
xxl-job-admin/ |
Spring Boot application serving as the admin console |
xxl-job-executor-samples/ |
Example executor implementations |
Database Setup
Execute the SQL initialization script included in the repository:
mysql -u root -p < doc/db/tables_xxl_job.sql
This creates the necesssary tables for job metadata, execution logs, and registry information.
Deploying the Admin Console
Configuration
Update the database connection settings in xxl-job-admin/src/main/resources/application.properties:
spring.datasource.url=jdbc:mysql://192.168.202.200:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.mail.host=smtp.qq.com
spring.mail.port=25
spring.mail.username=alerts@example.com
spring.mail.from=alerts@example.com
spring.mail.properties.mail.smtp.auth=true
Startup
Run XxlJobAdminApplication directly from your IDE or via the command line. The admin console will be available at:
http://localhost:8080/xxl-job-admin
Default credentials: admin / 123456
Deploying the Executor
Maven Dependency
Add the following dependency to your Spring Boot project's pom.xml:
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.3.1</version>
</dependency>
Configuration
Add the following properties to your application.properties:
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
xxl.job.accessToken=default_token
xxl.job.executor.appname=my-task-executor
xxl.job.executor.ip=127.0.0.1
xxl.job.executor.port=9999
xxl.job.executor.logpath=/data/applogs/xxl-job/executor
xxl.job.executor.logretentiondays=30
Executor Bean Configuration
Create a configuration class to initialize the executor:
@Configuration
public class XxlJobConfiguration {
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Value("${xxl.job.executor.appname}")
private String appname;
@Value("${xxl.job.executor.ip}")
private String ip;
@Value("${xxl.job.executor.port}")
private int port;
@Bean
public XxlJobExecutor xxlJobExecutor() {
XxlJobExecutor executor = new XxlJobExecutor();
executor.setAdminAddresses(adminAddresses);
executor.setAppname(appname);
executor.setIp(ip);
executor.setPort(port);
executor.setAccessToken(accessToken);
executor.setLogPath("/data/applogs/xxl-job/executor");
executor.setLogRetentionDays(30);
return executor;
}
}
Bean Mode: Method-Based Tasks
The Bean mode supports method-level job development. Each job is implemented as a simple method annotated with @XxlJob, and the framework automatically registers it as a handler in the executor container.
Advantages:
- Minimal boilerplate: only one method and annotation per job
- Automatic discovery: jobs are scanned and registered at startup without additional configuration
- Clean separation between business logic and scheduling infrastructure
Example Job Handler
@Component
public class UserPointJobHandler {
private static final Logger logger = LoggerFactory.getLogger(UserPointJobHandler.class);
@XxlJob("dailyPointAwardJob")
public ReturnT<String> execute(String param) {
logger.info("Daily point award job started");
try {
// Business logic: award points to all active users
List<User> activeUsers = userService.findActiveUsers();
for (User user : activeUsers) {
pointService.awardPoints(user.getId(), 10);
}
logger.info("Point award completed for {} users", activeUsers.size());
return new ReturnT<>(ReturnT.SUCCESS_CODE, "Processed " + activeUsers.size() + " users");
} catch (Exception e) {
logger.error("Point award job failed", e);
return new ReturnT<>(ReturnT.FAIL_CODE, e.getMessage());
}
}
}
The job method accepts an optional param string (configured in the admin console) and returns a ReturnT object indicating success or failure along with a message.
Register this executor application in the XXL-JOB admin console, then create a new job entry with:
- Job Name: Daily Point Award
- Job Group: your executor group
- Handler:
dailyPointAwardJob - Cron:
0 0 0 * * ?(midnight daily) - Route Strategy:
FIRST(ensures only one node executes the job)
The platform handles shard routing, conflict prevention, and execution logging automatically.