قوائم المهام لـ Rust: نظرة شاملة على الأدوات والمكتبات
Rust هي لغة برمجة أنظمة تكتسب شعبية في تطوير الويب وإنشاء الخدمات المصغرة عالية التحميل. أحد المتطلبات الأساسية لتطبيقات backend الحديثة هو المعالجة غير المتزامنة للمهام الخلفية: إرسال البريد الإلكتروني، إنشاء التقارير، معالجة الملفات المرفوعة أو التفاعل مع واجهات API الخارجية. في هذه المقالة، سننظر في كيفية تنظيم قائمة مهام في Rust، وما هي المكتبات المتاحة لذلك وكيفية تطبيقها عمليًا.
لماذا نحتاج إلى قائمة مهام؟
في التطبيق المتزامن، يتم تنفيذ كل عملية بشكل تسلسلي. إذا بدأ المستخدم مهمة طويلة (مثل معالجة فيديو)، فسيتم حظر الخادم حتى اكتمالها. تحل قائمة المهام هذه المشكلة: يتم وضع المهام في وسيط (قائمة)، وتقوم العمال (workers) الخاصون بسحبها وتنفيذها بشكل غير متزامن. هذا يسمح بـ:
- تخفيف الحمل عن التدفق الرئيسي للتطبيق؛
- توسيع نطاق المعالجة أفقيًا (إضافة عمال)؛
- ضمان تنفيذ المهمة حتى في حالة الأعطال (بفضل التأكيدات والمحاولات المتكررة).
المكتبات الشائعة لتنظيم قوائم المهام في Rust
يقدم نظام Rust البيئي العديد من الحلول الناضجة. سننظر في ثلاثة أساليب رئيسية.
1. Celery-rs (Rust Celery)
Celery-rs هو تطبيق Rust لـ Python Celery الشهير. تسمح المكتبة باستخدام وسطاء Redis أو RabbitMQ. إنها مثالية إذا كنت تنتقل من Python إلى Rust أو تريد التوافق مع البنية التحتية الحالية لـ Celery.
مثال على عامل أساسي:
use celery::Celery;use celery::task::Task;
#[celery::task]fn add(x: i32, y: i32) -> i32 { x + y}
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let celery = Celery::builder("redis://localhost:6379/0") .with_task(add) .build().await?;
// تشغيل العامل celery.run().await?;
// إرسال المهمة let task_id = add.delay(&celery, 1, 2).await?; println!("Task ID: {}", task_id);
Ok(())}الإيجابيات: واجهة API مألوفة لمطوري Python، دعم السيناريوهات المعقدة (إعادة المحاولة، ETA، المهام المتسلسلة).
السلبيات: الاعتماد على Redis/RabbitMQ، ثقيلة بعض الشيء للحالات البسيطة.
2. Lapin + Tokio (العمل المباشر مع RabbitMQ)
Lapin هو عميل AMQP عالي الأداء لـ Rust يعمل على Tokio. إذا كنت بحاجة إلى تحكم كامل في قوائم المهام وتبادلاتها ومفاتيح التوجيه، فإن Lapin هو الخيار الأفضل. لا يوفر تجريدات للمهام "جاهزة"، لكنه يمنحك المرونة.
مثال على إرسال واستقبال رسالة:
use lapin::{options::*, types::FieldTable, Connection, ConnectionProperties};use tokio_amqp::*;
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let conn = Connection::connect( "amqp://guest:guest@localhost:5672", ConnectionProperties::default().with_tokio(), ).await?;
let channel = conn.create_channel().await?; let queue = channel.queue_declare( "task_queue", QueueDeclareOptions::default(), FieldTable::default(), ).await?;
// إرسال المهمة let payload = b"Hello, task!"; channel.basic_publish( "", "task_queue", BasicPublishOptions::default(), payload, BasicProperties::default(), ).await?;
// استقبال المهمة (العامل) let consumer = channel.basic_consume( "task_queue", "consumer", BasicConsumeOptions::default(), FieldTable::default(), ).await?;
while let Some(delivery) = consumer.next().await { let delivery = delivery?; println!("Received task: {:?}", delivery.data); delivery.a