जब इवेंट कोड को नियंत्रित करते हैं: Rust में Event-driven आर्किटेक्चर
\n\nकल्पना करें कि आपका कोड एक ऑर्केस्ट्रा का कंडक्टर है। क्लासिकल सिंक्रोनस प्रोग्रामिंग में, कंडक्टर खुद तय करता है कि कब कौन सा नोट बजना चाहिए। Event-driven आर्किटेक्चर में, कंडक्टर सिर्फ इस बात पर प्रतिक्रिया करता है कि संगीतकार पहले ही बजाना शुरू कर चुके हैं। अजीब लगता है? केवल पहली नज़र में।
\n\nEvent-driven (इवेंट-संचालित) आर्किटेक्चर एक ऐसा दृष्टिकोण है, जहां प्रोग्राम का प्रवाह इवेंट्स द्वारा निर्धारित होता है: माउस क्लिक, HTTP रिक्वेस्ट प्राप्त करना, I/O पूरा होना, टाइमर। Rust अपनी ownership प्रणाली और zero-cost abstractions के साथ इस आर्किटेक्चर के लिए एक आदर्श जमीन है। खासकर यदि आप Python से आए हैं, जहां event loop (जैसे asyncio में) काम करता है, लेकिन अक्सर GIL और डायनामिक टाइपिंग के ओवरहेड में फंस जाता है।
\n\nइस लेख में हम सिर्फ थ्योरी नहीं करेंगे। हम रियल कोड लिखेंगे, लोकप्रिय क्रेट्स का उपयोग करके Rust में event-driven आर्किटेक्चर को समझेंगे, और दिखाएंगे कि यह आपके एप्लिकेशन को कई गुना तेज कैसे कर सकता है।
\n\nइवेंट्स के लिए Rust, Python क्यों नहीं?
\n\nPython प्रोटोटाइपिंग के लिए एक शानदार भाषा है। लेकिन जब आपको हजारों समवर्ती कनेक्शनों के साथ रीयल-टाइम इवेंट प्रोसेसिंग की आवश्यकता होती है, तो Python धीमा पड़ने लगता है। Rust देता है:
\n\n- \n
- Zero-cost abstractions — async/await कोई ओवरहेड नहीं जोड़ता, जब तक आप इसकी क्षमताओं का उपयोग नहीं करते \n
- कोई garbage collector नहीं — इवेंट प्रोसेसिंग के दौरान कोई अचानक रुकावट नहीं \n
- टाइप सिस्टम जो कंपाइल टाइम पर ही एरर पकड़ लेता है \n
- असली पैरेललिज़्म — को-ऑपरेटिव मल्टीटास्किंग नहीं, बल्कि GIL के बिना रियल थ्रेड्स \n
यदि आप Rust में event-driven एप्लिकेशन लिखते हैं, तो आपको हर बाइट मेमोरी और हर प्रोसेसर साइकिल पर नियंत्रण मिलता है। और यह मार्केटिंग नहीं है — यह एक तथ्य है।
\n\nबुनियादी बातें: Event Loop और इवेंट हैंडलिंग
\n\nकिसी भी इवेंट-संचालित सिस्टम की नींव event loop (इवेंट लूप) होता है। यह एक अनंत लूप है जो:
\n\n- \n
- नए इवेंट के आने की प्रतीक्षा करता है \n
- इसे कतार से निकालता है \n
- हैंडलर को भेजता है \n
- दोहराता है \n
Rust में, मानक event loop tokio क्रेट द्वारा प्रदान किया जाता है। आइए एक सरल उदाहरण लिखें:
use tokio::sync::mpsc;\nuse tokio::time::{sleep, Duration};\n\n#[tokio::main]\nasync fn main() {\n // इवेंट ट्रांसफर करने के लिए चैनल बनाएं\n let (tx, mut rx) = mpsc::channel::<String>(32);\n\n // प्रोड्यूसर: इवेंट जनरेट करता है\n tokio::spawn(async move {\n for i in 0..5 {\n let event = format!("इवेंट #{}", i);\n tx.send(event).await.unwrap();\n sleep(Duration::from_millis(500)).await;\n }\n });\n\n // कंज़्यूमर: इवेंट प्रोसेस करता है\n while let Some(event) = rx.recv().await {\n println!("प्रोसेस किया गया: {}", event);\n // यहां आपकी बिज़नेस लॉजिक हो सकती है\n }\n}\n\nयह कोड Rust में event-driven प्रोग्रामिंग के बुनियादी पैटर्न को दर्शाता है: एक कंपोनेंट इवेंट जनरेट करता है, दूसरा उन्हें प्रोसेस करता है। mpsc (multi-producer, single-consumer) चैनल टास्क के बीच सुरक्षित डेटा ट्रांसफर सुनिश्चित करता है।
एडवांस्ड उदाहरण: ऑर्डर प्रोसेसिंग सिस्टम
\n\nअब कार्य को थोड़ा जटिल बनाते हैं। कल्पना करें कि हम एक ई-कॉमर्स साइट का बैकएंड लिख रहे हैं। जब कोई नया ऑर्डर आता है, तो हमें:
\n\n- \n
- उत्पाद की उपलब्धता जांचनी होती है \n
- इसे रिज़र्व करना होता है \n
- नोटिफिकेशन भेजना होता है \n
- स्टैटिस्टिक्स अपडेट करनी होती है \n
क्लासिकल दृष्टिकोण — यह सब सिंक्रोनस रूप से करना। Event-driven दृष्टिकोण — इसे इवेंट्स में तोड़ना:
\n\nuse tokio::sync::broadcast;\nuse tokio::time::sleep;\nuse std::time::Duration;\n\n#[derive(Debug, Clone)]\nenum OrderEvent {\n NewOrder { id: u32, items: Vec<String>