قوائم المهام لـ Rust: نظرة عامة على الأدوات والمكتبات
Rust هي لغة برمجة أنظمة تُستخدم بشكل متزايد لبناء خدمات الويب عالية التحميل والخدمات المصغرة. إحدى المهام الرئيسية في مثل هذه المشاريع هي تنظيم المعالجة غير المتزامنة للمهام باستخدام قوائم الانتظار. في هذه المقالة، سنستعرض المكتبات والأساليب الرئيسية للعمل مع قوائم المهام في Rust، بما في ذلك الآليات المدمجة والتكامل مع وسطاء الرسائل الشائعين.
لماذا نحتاج إلى قوائم المهام؟
تسمح قوائم المهام (task queues) بفصل تنفيذ العمليات الطويلة أو الخلفية عن التدفق الرئيسي لمعالجة الطلبات. حالات الاستخدام النموذجية:
- إرسال إشعارات البريد الإلكتروني
- معالجة الصور والفيديو
- تنفيذ المهام الدورية (مهام cron)
- الحوسبة الموزعة
في نظام Rust البيئي، توجد عدة أساليب لهذه الأغراض: من استخدام بيئات التشغيل غير المتزامنة المدمجة إلى التكامل مع الوسطاء الخارجيين.
1. Tokio + async-std: أساس عدم التزامن
قبل الحديث عن قوائم انتظار محددة، يجدر ذكر بيئتي التشغيل غير المتزامنتين الرئيسيتين في Rust: Tokio و async-std. توفران آليات أساسية للعمل مع المهام:
tokio::spawn— تشغيل مهمة خلفيةasync_std::task::spawn— النظير لـ async-std- القنوات (channels) لنقل الرسائل بين المهام
مثال على قائمة انتظار بسيطة باستخدام قنوات Tokio:
use tokio::sync::mpsc;use tokio::time::{sleep, Duration};
#[tokio::main]async fn main() { let (tx, mut rx) = mpsc::channel::<String>(32);
// إرسال المهام tokio::spawn(async move { for i in 0..5 { tx.send(format!("المهمة {}", i)).await.unwrap(); sleep(Duration::from_millis(100)).await; } });
// معالجة المهام while let Some(task) = rx.recv().await { println!("جارٍ معالجة: {}", task); }}هذا الأسلوب مناسب للحالات البسيطة، لكنه لا يوفر الاستمرارية (persistence) أو قابلية التوسع بين العمليات.
2. lapin — عميل AMQP لـ RabbitMQ
lapin هي واحدة من أشهر المكتبات للعمل مع RabbitMQ في Rust. تدعم مجموعة كاملة من إمكانيات AMQP 0-9-1:
- قوائم الانتظار، المبادلات (exchanges)، الارتباطات (bindings)
- تأكيدات التسليم (publisher confirms)
- الرسائل الميتة (dead letter exchanges)
- استهلاك الرسائل غير المتزامن
مثال على نشر واستهلاك الرسائل:
use lapin::{options::*, types::FieldTable, Connection, ConnectionProperties};use futures_util::stream::StreamExt;
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let conn = Connection::connect( "amqp://guest:guest@localhost:5672", ConnectionProperties::default(), ).await?;
let channel = conn.create_channel().await?; channel.queue_declare( "task_queue", QueueDeclareOptions::default(), FieldTable::default(), ).await?;
// نشر مهمة channel.basic_publish( "", "task_queue", BasicPublishOptions::default(), b"Hello, RabbitMQ!", BasicProperties::default(), ).await?;
// استهلاك المهام let mut consumer = channel.basic_consume( "task_queue", "consumer", BasicConsumeOptions::default(), FieldTable::default(), ).await?;
while let Some(delivery) = consumer.next().await { let delivery = delivery?; println!("تم الاستلام: {:?}", delivery.data); delivery.ack(BasicAckOptions::default()).await?; } Ok(())}lapin مدعومة بنشاط وموثقة بشكل جيد.