परिचय: Go में कार्य कतारों की आवश्यकता क्यों है?
आधुनिक बैकएंड विकास में, अक्सर ऐसे कार्य उत्पन्न होते हैं जिनके लिए उपयोगकर्ता को तत्काल प्रतिक्रिया की आवश्यकता नहीं होती है: ईमेल भेजना, रिपोर्ट तैयार करना, छवियों का प्रसंस्करण, बाहरी API के साथ सिंक्रनाइज़ेशन। HTTP अनुरोध के दौरान ऐसे कार्यों को सिंक्रोनस रूप से निष्पादित करना एक खराब प्रथा है, क्योंकि इससे प्रतिक्रिया धीमी हो जाती है और सर्वर पर लोड बढ़ जाता है।
कार्य कतारें (job queues) इस समस्या का समाधान करती हैं। Go (Golang) में निर्मित समवर्तीता तंत्र — गोरूटीन और चैनल — बाहरी निर्भरताओं के बिना विश्वसनीय और कुशल कतारें बनाने की अनुमति देते हैं। इस लेख में, हम समझेंगे कि शुरू से कार्य कतार कैसे बनाएं, worker pool का उपयोग करके इसे अनुकूलित करें और सामान्य गलतियों से कैसे बचें।
मूल बातें: गोरूटीन और चैनल
Go कतारों के साथ काम करने के लिए दो प्रमुख प्रिमिटिव प्रदान करता है:
- गोरूटीन — हल्के थ्रेड, जो
goकीवर्ड के साथ लॉन्च किए जाते हैं। - चैनल — गोरूटीन के बीच डेटा स्थानांतरित करने के लिए टाइप किए गए "पाइप"।
सबसे सरल कतार एक गोरूटीन है जो चैनल से पढ़ती है और कार्यों को संसाधित करती है:
package main
import ( "fmt" "time")
func worker(jobs <-chan int) { for job := range jobs { fmt.Printf("कार्य %d का प्रसंस्करण", job) time.Sleep(1 * time.Second) // कार्य का अनुकरण }}
func main() { jobs := make(chan int, 10)
// वर्कर लॉन्च करें go worker(jobs)
// कार्य भेजें for i := 1; i <= 5; i++ { jobs <- i }
close(jobs) // सभी कार्य भेजने के बाद चैनल बंद करें time.Sleep(2 * time.Second)}यह कोड एक वर्कर बनाता है जो कार्यों को क्रमिक रूप से संसाधित करता है। लेकिन वास्तविक परियोजनाओं के लिए स्केलेबिलिटी की आवश्यकता होती है — कई वर्कर।
Go में Worker Pool का कार्यान्वयन
Worker pool (वर्कर पूल) एक पैटर्न है जिसमें हम गोरूटीन-वर्कर की एक निश्चित संख्या लॉन्च करते हैं, प्रत्येक एक सामान्य चैनल से कार्य लेता है। यह समानता की डिग्री और सिस्टम पर लोड को नियंत्रित करने की अनुमति देता है।
मूल कार्यान्वयन
package main
import ( "fmt" "sync" "time")
func worker(id int, jobs <-chan int, wg *sync.WaitGroup) { defer wg.Done() for job := range jobs { fmt.Printf("वर्कर %d: कार्य %d", id, job) time.Sleep(500 * time.Millisecond) }}
func main() { const numWorkers = 3 const numJobs = 10
jobs := make(chan int, numJobs) var wg sync.WaitGroup
// वर्कर लॉन्च करें for i := 1; i <= numWorkers; i++ { wg.Add(1) go worker(i, jobs, &wg) }
// कार्य भेजें for j := 1; j <= numJobs; j++ { jobs <- j } close(jobs)
wg.Wait() // सभी वर्कर के पूरा होने की प्रतीक्षा करें fmt.Println("सभी कार्य पूर्ण हो गए")}यहां हम सभी वर्कर के पूरा होने की प्रतीक्षा करने के लिए sync.WaitGroup का उपयोग करते हैं। numJobs बफर वाला चैनल वर्कर द्वारा उन्हें लेने से पहले सभी कार्यों को बिना ब्लॉक किए भेजने की अनुमति देता है।
गतिशील रूप से कार्य जोड़ना
वास्तविक अनुप्रयोगों में, कार्य एसिंक्रोनस रूप से आते हैं — उदाहरण के लिए, HTTP के माध्यम से। यहां हैंडलर के साथ एक उदाहरण है:
package main
import ( "fmt" "net/http" "sync" "time")
type Task struct { ID int Data string}
var ( taskQueue = make(chan Task, 100) wg sync.WaitGroup)
func worker(id int) { defer wg.Done() for task := range taskQueue { fmt.Printf("[वर्कर %d] प्रसंस्करण: %s (ID=%d)", id, task.Data, task.ID) time.Sleep(1 * time.Second) }}
func handler(w http.ResponseWriter, r *http.Request) { // अनुरोध से कार्य प्राप्त करें (सरलीकृत) task := Task{ID: time.Now().Nanosecond(), Data: "नया कार्य"} taskQueue <- task w.W