Skip to content

How to Run Script and HTTP Jobs with ElasticJob

The Problem

I had a Spring Boot application using ElasticJob for distributed scheduling. Everything worked great for Java jobs—I could create job classes, configure them, and let ElasticJob handle the sharding and failover.

Then I needed to schedule a legacy shell script that did database backups. And trigger a webhook on a remote microservice. Both tasks had nothing to do with Java.

I started looking for solutions:

  1. Wrap scripts in Java: Write a Java job that calls Runtime.exec() — messy and hard to test
  2. Use a separate scheduler: Cron jobs or systemd timers — lose ElasticJob’s distributed features
  3. Build HTTP endpoints: Create endpoints just to trigger the script — overkill

None of these felt right. I wanted ElasticJob’s benefits (sharding, failover, monitoring) without rewriting existing scripts.

Then I discovered ElasticJob has built-in support for this exact scenario.

The Solution

ElasticJob provides two non-Java job types:

  • Script Jobs: Execute shell scripts or any executable on the host machine
  • HTTP Jobs: Make HTTP requests to remote endpoints

Both use special sentinel values ("SCRIPT" and "HTTP") instead of actual Java class names. The ShardingContext is passed to the job automatically—via command-line argument for scripts, or HTTP header for HTTP jobs.

Script Jobs: Running Shell Scripts

Let’s say I have a backup script at /scripts/backup.sh that I want to run every 5 minutes, with 3 shards for parallel processing.

Step 1: Configure the Script Job

ScriptJobConfig.java
import org.apache.shardingsphere.elasticjob.api.JobConfiguration;
import org.apache.shardingsphere.elasticjob.api.ShardingContext;
import org.apache.shardingsphere.elasticjob.http.props.HttpJobProperties;
import org.apache.shardingsphere.elasticjob.script.props.ScriptJobProperties;
import org.apache.shardingsphere.elasticjob.lite.api.bootstrap.ScheduleJobBootstrap;
public class ScriptJobConfig {
public void scheduleBackupJob(CoordinatorRegistryCenter registryCenter) {
JobConfiguration config = JobConfiguration.newBuilder("DatabaseBackupJob", 3)
.cron("0 0/5 * * * ?") // Every 5 minutes
.setProperty(ScriptJobProperties.SCRIPT_KEY, "/scripts/backup.sh")
.build();
new ScheduleJobBootstrap(registryCenter, "SCRIPT", config).schedule();
}
}

The key parts:

  • "SCRIPT" is the sentinel value that tells ElasticJob to use the script job type
  • ScriptJobProperties.SCRIPT_KEY points to the script path
  • 3 is the sharding count—ElasticJob will run the script on 3 different nodes

Step 2: Write the Script

The script receives the ShardingContext as JSON via the first argument:

/scripts/backup.sh
#!/bin/bash
# ShardingContext is passed as the first argument (JSON format)
# Example: {"jobName":"DatabaseBackupJob","shardingItem":1,"shardingTotalCount":3}
SHARD_ID=$(echo $1 | jq -r '.shardingItem')
TOTAL_SHARDS=$(echo $1 | jq -r '.shardingTotalCount')
echo "Running backup for shard $SHARD_ID of $TOTAL_SHARDS"
# Process different data based on shard ID
case $SHARD_ID in
0) mysqldump -u root db1 > /backup/db1_shard0.sql ;;
1) mysqldump -u root db2 > /backup/db2_shard1.sql ;;
2) mysqldump -u root db3 > /backup/db3_shard2.sql ;;
esac
echo "Backup completed for shard $SHARD_ID"

Understanding the ShardingContext

ElasticJob automatically passes context to your script:

ShardingContext JSON Structure
{
"jobName": "DatabaseBackupJob",
"shardingItem": 1,
"shardingTotalCount": 3,
"jobParameter": "",
"shardingItemParameters": {}
}
  • shardingItem: Which shard this instance is processing (0, 1, or 2)
  • shardingTotalCount: Total number of shards
  • Each script instance gets a different shardingItem, enabling parallel processing

HTTP Jobs: Triggering Remote Endpoints

For HTTP jobs, I wanted to trigger a reporting service at https://reports.example.com/api/generate every hour.

Step 1: Configure the HTTP Job

HttpJobConfig.java
import org.apache.shardingsphere.elasticjob.api.JobConfiguration;
import org.apache.shardingsphere.elasticjob.http.props.HttpJobProperties;
import org.apache.shardingsphere.elasticjob.lite.api.bootstrap.ScheduleJobBootstrap;
public class HttpJobConfig {
public void scheduleReportJob(CoordinatorRegistryCenter registryCenter) {
JobConfiguration config = JobConfiguration.newBuilder("ReportGenerationJob", 3)
.cron("0 0 * * * ?") // Every hour
.setProperty(HttpJobProperties.URI_KEY, "https://reports.example.com/api/generate")
.setProperty(HttpJobProperties.METHOD_KEY, "POST")
.setProperty(HttpJobProperties.DATA_KEY, "source=ElasticJob&type=scheduled")
.build();
new ScheduleJobBootstrap(registryCenter, "HTTP", config).schedule();
}
}

Step 2: What the HTTP Request Looks Like

ElasticJob sends the ShardingContext as an HTTP header:

HTTP Request Sent by ElasticJob
POST /api/generate HTTP/1.1
Host: reports.example.com
Content-Type: application/x-www-form-urlencoded
ShardingContext: {"jobName":"ReportGenerationJob","shardingItem":1,"shardingTotalCount":3}
source=ElasticJob&type=scheduled

Your endpoint can read the ShardingContext header to know which shard it’s processing:

report_endpoint.py (Example Server)
from flask import Flask, request
import json
app = Flask(__name__)
@app.route('/api/generate', methods=['POST'])
def generate_report():
# Parse the ShardingContext header
context = json.loads(request.headers.get('ShardingContext', '{}'))
shard_id = context.get('shardingItem', 0)
total_shards = context.get('shardingTotalCount', 1)
# Generate reports for this shard's data
generate_reports_for_shard(shard_id)
return {'status': 'success', 'shard': shard_id}

Common Pitfalls

Script Permission Denied

I forgot to make the script executable:

Fix script permissions
chmod +x /scripts/backup.sh

Script Path Issues

The script path must be absolute and accessible from all nodes. I initially used a relative path, which failed on production servers.

HTTP Job Response Handling

HTTP jobs expect a successful response (2xx status code). If your endpoint returns 4xx or 5xx, ElasticJob marks the job as failed and may retry.

When to Use Each Type

Job TypeBest ForExample
Script JobLocal operations, legacy scriptsDatabase backups, log rotation, file cleanup
HTTP JobRemote services, microservicesTrigger CI/CD pipelines, call reporting APIs, webhook notifications

Key Takeaways

  1. No Java code needed: Use sentinel values "SCRIPT" or "HTTP" instead of class names
  2. ShardingContext is automatic: Scripts receive it as the first argument (JSON), HTTP jobs get it as a header
  3. Script jobs need absolute paths: And executable permissions
  4. HTTP jobs need valid endpoints: That return 2xx status codes

You get ElasticJob’s distributed scheduling, sharding, and failover benefits without rewriting existing scripts or services.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments