XXL-Job Distributed Task Scheduling Framework: Project Overview and Hands‑On Guide
This article introduces the open‑source XXL-Job distributed task scheduling framework, explains its architecture and communication design, and provides a step‑by‑step hands‑on tutorial covering server deployment, executor configuration, task development (annotation, API, sharding), execution, and log inspection, with code examples and screenshots.
XXL‑Job is a lightweight open‑source distributed task scheduling framework written in Java. It consists of an admin (management) console and one or more executor (worker) nodes. The admin handles task configuration and log viewing, while executors run the actual job logic.
Project Introduction
XXL‑Job can be downloaded from https://github.com/xuxueli/xxl-job . After cloning, import the SQL script doc/db/table_xxl_job.sql into MySQL to create the required tables.
Server Deployment
Create a new Spring Boot project and copy the xxl-job-admin directory and its pom.xml into the project, or use the original project directly. Modify application.properties to point to your MySQL instance.
Update the xxl-job-core dependency version to 2.2.0 and adjust logback.xml for log output. Start the application and access the admin console at http://localhost:8080/xxl-job-admin/ (default credentials admin/123456).
Executor Configuration
Create a separate module for the executor, add the same logback.xml and the xxl-job-core dependency. Provide two configuration files (e.g., application-9998.properties and application-9999.properties ) with different server.port and xxl.job.executor.port values to simulate two executor instances.
Define a Spring configuration class that creates a XxlJobSpringExecutor bean, setting properties such as appname , address , ip , port , and logPath . Start both executors; the admin console will show both nodes registered.
Task Development
Three common ways to create jobs are demonstrated:
Annotation‑based job : add @XxlJob("myJob") on a method.
API‑based job : invoke the admin API to register a job programmatically.
Sharding‑broadcast job : configure a job with multiple shards that are executed on every executor.
Task Execution
In the admin UI, create a job with a cron expression and select the handler name (e.g., myJobAnnotationHandler ). Trigger the job manually to see real‑time execution and log output. The framework supports round‑robin routing, child jobs, and sharding broadcast.
Log Inspection
Job logs are stored in the database and can be viewed from the admin console, showing execution timestamps, parameters, and any exceptions printed by XxlJobLogger .
Communication Design
XXL‑Job uses Netty HTTP for communication between admin and executors (other transports like Mina or Netty TCP are optional but not used in the default code). The design employs dynamic proxies ( ExecutorBiz and AdminBiz ) to hide network details, fully asynchronous processing with a LinkedBlockingQueue , and a future‑response mechanism to turn asynchronous calls into synchronous results.
Key Code Snippets
public static ReturnT
runExecutor(TriggerParam triggerParam, String address) {
ReturnT
runResult = null;
try {
ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(address);
// asynchronous handling, finally get synchronous result
runResult = executorBiz.run(triggerParam);
} catch (Exception e) {
logger.error(">>>>>>>>>> xxl-job trigger error, please check if the executor[{}] is running.", address, e);
runResult = new ReturnT
(ReturnT.FAIL_CODE, ThrowableUtil.toString(e));
}
StringBuffer runResultSB = new StringBuffer(I18nUtil.getString("jobconf_trigger_run") + ":");
runResultSB.append("
address:").append(address);
runResultSB.append("
code:").append(runResult.getCode());
runResultSB.append("
msg:").append(runResult.getMsg());
runResult.setMsg(runResultSB.toString());
return runResult;
} if (CallType.SYNC == callType) {
// future‑response set
XxlRpcFutureResponse futureResponse = new XxlRpcFutureResponse(invokerFactory, xxlRpcRequest, null);
try {
// do invoke
client.asyncSend(finalAddress, xxlRpcRequest);
// future get
XxlRpcResponse xxlRpcResponse = futureResponse.get(timeout, TimeUnit.MILLISECONDS);
if (xxlRpcResponse.getErrorMsg() != null) {
throw new XxlRpcException(xxlRpcResponse.getErrorMsg());
}
return xxlRpcResponse.getResult();
} catch (Exception e) {
logger.info(">>>>>>>>>> xxl-rpc, invoke error, address:{}, XxlRpcRequest{}", finalAddress, xxlRpcRequest);
throw (e instanceof XxlRpcException) ? e : new XxlRpcException(e);
} finally {
// future‑response remove
futureResponse.removeInvokerFuture();
}
} public void setResponse(XxlRpcResponse response) {
this.response = response;
synchronized (lock) {
done = true;
lock.notifyAll();
}
}
@Override
public XxlRpcResponse get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
if (!done) {
synchronized (lock) {
try {
if (timeout < 0) {
// thread block
lock.wait();
} else {
long timeoutMillis = (TimeUnit.MILLISECONDS == unit) ? timeout : TimeUnit.MILLISECONDS.convert(timeout, unit);
lock.wait(timeoutMillis);
}
} catch (InterruptedException e) {
throw e;
}
}
}
if (!done) {
throw new XxlRpcException("xxl-rpc, request timeout at:" + System.currentTimeMillis() + ", request:" + request.toString());
}
return response;
}Each remote call carries a UUID request ID, which is used to locate the corresponding XxlRpcFutureResponse and wake the waiting thread.
Overall, the article provides a complete end‑to‑end guide for setting up XXL‑Job, configuring multiple executors, writing jobs in different styles, and understanding the underlying asynchronous communication mechanism.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.