क्या आपने कभी निराशा में अपने कंप्यूटर से पावर कॉर्ड को खींचकर बाहर निकाला है? हालांकि यह एक त्वरित समाधान की तरह लग सकता है, लेकिन इससे डेटा हानि और सिस्टम अस्थिरता हो सकती है। सॉफ़्टवेयर की दुनिया में, एक समान अवधारणा मौजूद है: हार्ड शटडाउन। यह अचानक समाप्ति इसके भौतिक समकक्ष की तरह ही समस्याएँ पैदा कर सकती है। शुक्र है, एक बेहतर तरीका है: शालीन शटडाउन।
सुंदर शटडाउन को एकीकृत करके, हम सेवा को अग्रिम सूचना प्रदान करते हैं। यह इसे चल रहे अनुरोधों को पूरा करने, संभावित रूप से डिस्क पर स्थिति की जानकारी को सहेजने और अंततः शटडाउन के दौरान डेटा भ्रष्टाचार से बचने में सक्षम बनाता है।
यह मार्गदर्शिका सुंदर शटडाउन की दुनिया में प्रवेश करेगी, विशेष रूप से कुबेरनेट्स पर चलने वाले गो अनुप्रयोगों में उनके कार्यान्वयन पर ध्यान केंद्रित करेगी।
यूनिक्स-आधारित सिस्टम में सुंदर शटडाउन प्राप्त करने के लिए प्रमुख उपकरणों में से एक सिग्नल की अवधारणा है, जो सरल शब्दों में, एक प्रक्रिया से दूसरी प्रक्रिया तक एक विशिष्ट चीज़ को संप्रेषित करने का एक सरल तरीका है। सिग्नल कैसे काम करते हैं, यह समझकर हम अपने अनुप्रयोगों के भीतर नियंत्रित समाप्ति प्रक्रियाओं को लागू करने के लिए उनका लाभ उठा सकते हैं, जिससे एक सुचारू और डेटा-सुरक्षित शटडाउन प्रक्रिया सुनिश्चित होती है।
कई संकेत हैं, और आप उन्हें यहां पा सकते हैं, लेकिन हमारी चिंता केवल शटडाउन संकेत हैं:
ये संकेत उपयोगकर्ता (Ctrl+C / Ctrl+D), किसी अन्य प्रोग्राम/प्रक्रिया से, या स्वयं सिस्टम (कर्नेल/OS) से भेजे जा सकते हैं, उदाहरण के लिए, OS द्वारा एक SIGSEGV उर्फ सेगमेंटेशन फॉल्ट भेजा जाता है।
व्यावहारिक सेटिंग में सुंदर शटडाउन की दुनिया का पता लगाने के लिए, आइए एक सरल सेवा बनाएं जिसके साथ हम प्रयोग कर सकते हैं। इस "गिनी पिग" सेवा में एक एकल समापन बिंदु होगा जो Redis के INCR कमांड को कॉल करके कुछ वास्तविक दुनिया के काम का अनुकरण करता है (हम थोड़ी देरी जोड़ेंगे)। हम यह परीक्षण करने के लिए एक बुनियादी Kubernetes कॉन्फ़िगरेशन भी प्रदान करेंगे कि प्लेटफ़ॉर्म समाप्ति संकेतों को कैसे संभालता है।
अंतिम लक्ष्य: सुनिश्चित करें कि हमारी सेवा बिना किसी अनुरोध/डेटा को खोए शटडाउन को सुचारू रूप से संभालती है। Redis में अंतिम काउंटर मान के साथ समानांतर में भेजे गए अनुरोधों की संख्या की तुलना करके, हम यह सत्यापित करने में सक्षम होंगे कि हमारा सुचारू शटडाउन कार्यान्वयन सफल है या नहीं।
हम Kubernetes क्लस्टर और Redis की स्थापना के विवरण में नहीं जाएंगे, लेकिन आप हमारे Github रिपॉजिटरी में पूर्ण सेटअप पा सकते हैं।
सत्यापन प्रक्रिया निम्नलिखित है:
आइये अपने बेस गो HTTP सर्वर से शुरुआत करें।
हार्ड-शटडाउन/main.go
package main import ( "net/http" "os" "time" "github.com/go-redis/redis" ) func main() { redisdb := redis.NewClient(&redis.Options{ Addr: os.Getenv("REDIS_ADDR"), }) server := http.Server{ Addr: ":8080", } http.HandleFunc("/incr", func(w http.ResponseWriter, r *http.Request) { go processRequest(redisdb) w.WriteHeader(http.StatusOK) }) server.ListenAndServe() } func processRequest(redisdb *redis.Client) { // simulate some business logic here time.Sleep(time.Second * 5) redisdb.Incr("counter") }
जब हम इस कोड का उपयोग करके अपनी सत्यापन प्रक्रिया चलाते हैं तो हम देखते हैं कि कुछ अनुरोध विफल हो जाते हैं और काउंटर 1000 से कम है (प्रत्येक बार संख्या भिन्न हो सकती है)।
इसका स्पष्ट अर्थ है कि हमने रोलिंग अपडेट के दौरान कुछ डेटा खो दिया है।
गो एक सिग्नल पैकेज प्रदान करता है जो आपको यूनिक्स सिग्नल को संभालने की अनुमति देता है। यह ध्यान रखना महत्वपूर्ण है कि डिफ़ॉल्ट रूप से, SIGINT और SIGTERM सिग्नल गो प्रोग्राम को बाहर निकलने का कारण बनते हैं। और हमारे गो एप्लिकेशन को अचानक से बाहर न निकलने के लिए, हमें आने वाले सिग्नल को संभालने की आवश्यकता है।
ऐसा करने के दो विकल्प हैं।
चैनल का उपयोग:
c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGTERM)
संदर्भ का उपयोग करना (आजकल पसंदीदा दृष्टिकोण):
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM) defer stop()
NotifyContext, मूल संदर्भ की एक प्रतिलिपि लौटाता है, जिसे पूर्ण के रूप में चिह्नित किया जाता है (इसका पूर्ण चैनल बंद हो जाता है), जब सूचीबद्ध संकेतों में से एक आता है, जब लौटाए गए stop() फ़ंक्शन को कॉल किया जाता है, या जब मूल संदर्भ का पूर्ण चैनल बंद हो जाता है, जो भी पहले हो।
HTTP सर्वर के हमारे वर्तमान कार्यान्वयन में कुछ समस्याएं हैं:
आइये इसे पुनः लिखें।
ग्रेसफुल-शटडाउन/main.go
package main // imports var wg sync.WaitGroup func main() { ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM) defer stop() // redisdb, server http.HandleFunc("/incr", func(w http.ResponseWriter, r *http.Request) { wg.Add(1) go processRequest(redisdb) w.WriteHeader(http.StatusOK) }) // make it a goroutine go server.ListenAndServe() // listen for the interrupt signal <-ctx.Done() // stop the server if err := server.Shutdown(context.Background()); err != nil { log.Fatalf("could not shutdown: %v\n", err) } // wait for all goroutines to finish wg.Wait() // close redis connection redisdb.Close() os.Exit(0) } func processRequest(redisdb *redis.Client) { defer wg.Done() // simulate some business logic here time.Sleep(time.Second * 5) redisdb.Incr("counter") }
अपडेट का सारांश इस प्रकार है:
अब, यदि हम अपनी सत्यापन प्रक्रिया दोहराते हैं तो हम देखेंगे कि सभी 1000 अनुरोध सही ढंग से संसाधित किए गए हैं।
इको, जिन, फाइबर और अन्य जैसे फ्रेमवर्क प्रत्येक आने वाले अनुरोधों के लिए एक गोरूटीन बनाएंगे, इसे एक संदर्भ देंगे और फिर आपके द्वारा तय किए गए रूटिंग के आधार पर आपके फ़ंक्शन / हैंडलर को कॉल करेंगे। हमारे मामले में यह "/incr" पथ के लिए HandleFunc को दिया गया अनाम फ़ंक्शन होगा।
जब आप SIGTERM सिग्नल को रोकते हैं और अपने फ्रेमवर्क को सुचारू रूप से बंद करने के लिए कहते हैं, तो 2 महत्वपूर्ण चीजें होती हैं (सरलीकृत करने के लिए):
नोट: Kubernetes लोडबैलेंसर को समाप्त होने वाला लेबल देने के बाद, लोडबैलेंसर से आने वाले ट्रैफ़िक को आपके पॉड पर भेजना भी बंद कर देता है।
किसी प्रक्रिया को समाप्त करना जटिल हो सकता है, खासकर अगर इसमें कई चरण शामिल हों जैसे कि कनेक्शन बंद करना। यह सुनिश्चित करने के लिए कि चीजें सुचारू रूप से चले, आप टाइमआउट सेट कर सकते हैं। यह टाइमआउट एक सुरक्षा जाल के रूप में कार्य करता है, अगर प्रक्रिया में अपेक्षा से अधिक समय लगता है तो प्रक्रिया से शालीनता से बाहर निकल जाता है।
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() go func() { if err := server.Shutdown(shutdownCtx); err != nil { log.Fatalf("could not shutdown: %v\n", err) } }() select { case <-shutdownCtx.Done(): if shutdownCtx.Err() == context.DeadlineExceeded { log.Fatalln("timeout exceeded, forcing shutdown") } os.Exit(0) }
चूँकि हमने अपनी सेवा को तैनात करने के लिए Kubernetes का उपयोग किया है, तो आइए इस बात पर गहराई से विचार करें कि यह पॉड्स को कैसे समाप्त करता है। एक बार जब Kubernetes पॉड को समाप्त करने का फैसला करता है, तो निम्नलिखित घटनाएँ घटित होंगी:
जैसा कि आप देख सकते हैं, यदि आपके पास लंबे समय तक चलने वाली समाप्ति प्रक्रिया है, तो endingGracePeriodSeconds सेटिंग को बढ़ाना आवश्यक हो सकता है, जिससे आपके एप्लिकेशन को सुचारू रूप से बंद होने के लिए पर्याप्त समय मिल सके।
ग्रेसफुल शटडाउन डेटा अखंडता की रक्षा करते हैं, एक सहज उपयोगकर्ता अनुभव बनाए रखते हैं, और संसाधन प्रबंधन को अनुकूलित करते हैं। अपने समृद्ध मानक पुस्तकालय और समवर्तीता पर जोर देने के साथ, Go डेवलपर्स को सहजता से ग्रेसफुल शटडाउन प्रथाओं को एकीकृत करने में सक्षम बनाता है - जो Kubernetes जैसे कंटेनरीकृत या ऑर्केस्ट्रेटेड वातावरण में तैनात अनुप्रयोगों के लिए एक आवश्यकता है।
आप हमारे गिटहब रिपॉजिटरी में गो कोड और कुबेरनेट्स मैनिफ़ेस्ट पा सकते हैं।