Get started
This guide shows how to add QRush to an Actix Web app: define a job, initialize the queues, mount the metrics at /qrush/metrics
, and enqueue work (now or later).
Install
cargo add qrush actix-web anyhow async-trait serde chrono
Define a Job
Implement the Job
trait. Keep name()
and queue()
consistent with how you register the handler.
use qrush::job::Job;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
#[derive(Clone, Serialize, Deserialize)]
pub struct NotifyUser {
pub user_id: String,
pub message: String,
}
#[async_trait]
impl Job for NotifyUser {
async fn perform(&self) -> anyhow::Result<()> {
println!("Notify {} -> {}", self.user_id, self.message);
Ok(())
}
fn name(&self) -> &'static str { "NotifyUser" }
fn queue(&self) -> &'static str { "default" }
}
Initialize QRush (your pattern)
Initialize queues in a background task, wait until ready, register your jobs, and expose the metrics routes under /qrush/metrics
.
// jobs/qrush.rs
use actix_web::web;
use std::sync::Arc;
use tokio::sync::Notify;
use qrush::config::{QueueConfig, QUEUE_INITIALIZED, set_basic_auth, QrushBasicAuthConfig};
use qrush::registry::register_job;
use crate::jobs::notify_user::NotifyUser;
use qrush::routes::metrics_route::qrush_metrics_routes;
pub struct QrushInitializer;
impl QrushInitializer {
pub async fn initialize(basic_auth: Option<QrushBasicAuthConfig>) {
let notify = Arc::new(Notify::new());
// Optional: enable Basic Auth via env QRUSH_BASIC_AUTH="user:pass"
let basic_auth = basic_auth.or_else(|| {
std::env::var("QRUSH_BASIC_AUTH").ok().and_then(|auth| {
let parts: Vec<&str> = auth.splitn(2, ':').collect();
(parts.len() == 2).then(|| QrushBasicAuthConfig {
username: parts[0].to_string(),
password: parts[1].to_string(),
})
})
});
let _ = set_basic_auth(basic_auth);
let _ = QUEUE_INITIALIZED.set(notify.clone());
// Register job handler
register_job(NotifyUser::name(), NotifyUser::handler);
// Init queues
tokio::spawn({
let notify = notify.clone();
async move {
let redis_url = std::env::var("REDIS_URL")
.unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string());
let queues = vec![
QueueConfig::new("default", 5, 1),
QueueConfig::new("critical", 10, 0),
];
if QueueConfig::initialize(redis_url, queues).await.is_ok() {
println!("✅ qrush started");
notify.notify_waiters();
} else {
eprintln!("❌ qrush init failed");
}
}
});
// Wait until init completes
notify.notified().await;
}
pub fn routes(cfg: &mut web::ServiceConfig) { qrush_metrics_routes(cfg); }
}
Wire into main.rs
use actix_web::{web, App, HttpServer, HttpResponse, Responder, middleware::Logger};
use dotenv::dotenv;
use std::env;
use crate::jobs::qrush::QrushInitializer;
use qrush::queue::{enqueue, enqueue_in};
use crate::jobs::notify_user::NotifyUser;
async fn health_check() -> impl Responder {
HttpResponse::Ok().body("OK")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
dotenv().ok();
// Start QRush and wait until ready
QrushInitializer::initialize(None).await;
// Seed a few jobs
let _ = enqueue(NotifyUser { user_id: "abc".into(), message: "Hello from QRush!".into() }).await;
let _ = enqueue_in(NotifyUser { user_id: "abc".into(), message: "After 60s".into() }, 60).await;
let addr = env::var("SERVER_ADDRESS").unwrap_or_else(|_| "0.0.0.0:8083".to_string());
HttpServer::new(move || {
App::new()
.wrap(Logger::default())
.service(
web::scope("/qrush")
.configure(QrushInitializer::routes) // -> /qrush/metrics/*
)
.route("/", web::get().to(health_check))
.route("/health", web::get().to(health_check))
})
.bind(addr)?
.run()
.await
}
Environment
export REDIS_URL="redis://127.0.0.1:6379"
# optional
export QRUSH_BASIC_AUTH="admin:strongpassword"
export SERVER_ADDRESS="0.0.0.0:8083"
Open the metrics
- Dashboard:
http://localhost:8083/qrush/metrics
- Per-queue:
/qrush/metrics/queues/default
- Health:
/qrush/metrics/health
Need help? See Support or open an issue on GitHub.