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:
- Wrap scripts in Java: Write a Java job that calls
Runtime.exec()— messy and hard to test - Use a separate scheduler: Cron jobs or systemd timers — lose ElasticJob’s distributed features
- 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
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 typeScriptJobProperties.SCRIPT_KEYpoints to the script path3is 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:
#!/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 IDcase $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:
{ "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
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:
POST /api/generate HTTP/1.1Host: reports.example.comContent-Type: application/x-www-form-urlencodedShardingContext: {"jobName":"ReportGenerationJob","shardingItem":1,"shardingTotalCount":3}
source=ElasticJob&type=scheduledYour endpoint can read the ShardingContext header to know which shard it’s processing:
from flask import Flask, requestimport 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:
chmod +x /scripts/backup.shScript 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 Type | Best For | Example |
|---|---|---|
| Script Job | Local operations, legacy scripts | Database backups, log rotation, file cleanup |
| HTTP Job | Remote services, microservices | Trigger CI/CD pipelines, call reporting APIs, webhook notifications |
Key Takeaways
- No Java code needed: Use sentinel values
"SCRIPT"or"HTTP"instead of class names - ShardingContext is automatic: Scripts receive it as the first argument (JSON), HTTP jobs get it as a header
- Script jobs need absolute paths: And executable permissions
- 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