আপনি কি কখনও হতাশার মধ্যে আপনার কম্পিউটার থেকে পাওয়ার কর্ডটি ফেলে দিয়েছেন? যদিও এটি একটি দ্রুত সমাধান বলে মনে হতে পারে, এটি ডেটা ক্ষতি এবং সিস্টেমের অস্থিরতার দিকে নিয়ে যেতে পারে। সফ্টওয়্যারের জগতে, একটি অনুরূপ ধারণা বিদ্যমান: হার্ড শাটডাউন। এই আকস্মিক সমাপ্তি তার শারীরিক প্রতিরূপের মতোই সমস্যা সৃষ্টি করতে পারে। সৌভাগ্যক্রমে, একটি ভাল উপায় আছে: করুণ শাটডাউন।
সুদৃশ্য শাটডাউন সংহত করে, আমরা পরিষেবাতে অগ্রিম বিজ্ঞপ্তি প্রদান করি। এটি এটিকে চলমান অনুরোধগুলি সম্পূর্ণ করতে, সম্ভাব্যভাবে ডিস্কে রাষ্ট্রীয় তথ্য সংরক্ষণ করতে এবং শেষ পর্যন্ত শাটডাউনের সময় ডেটা দুর্নীতি এড়াতে সক্ষম করে।
এই নির্দেশিকাটি আকর্ষণীয় শাটডাউনের জগতের সন্ধান করবে, বিশেষ করে Kubernetes-এ চলমান Go অ্যাপ্লিকেশনগুলিতে তাদের বাস্তবায়নের উপর ফোকাস করবে।
ইউনিক্স-ভিত্তিক সিস্টেমে আকর্ষণীয় শাটডাউনগুলি অর্জনের অন্যতম প্রধান হাতিয়ার হল সংকেতের ধারণা, যা সহজ ভাষায়, একটি নির্দিষ্ট জিনিসকে একটি প্রক্রিয়া থেকে অন্য প্রক্রিয়ার সাথে যোগাযোগ করার একটি সহজ উপায়। সিগন্যাল কীভাবে কাজ করে তা বোঝার মাধ্যমে, আমরা একটি মসৃণ এবং ডেটা-নিরাপদ শাটডাউন প্রক্রিয়া নিশ্চিত করে আমাদের অ্যাপ্লিকেশনগুলির মধ্যে নিয়ন্ত্রিত সমাপ্তি পদ্ধতিগুলি বাস্তবায়ন করতে তাদের সুবিধা নিতে পারি।
অনেক সংকেত আছে, এবং আপনি সেগুলি এখানে খুঁজে পেতে পারেন, কিন্তু আমাদের উদ্বেগ শুধুমাত্র শাটডাউন সংকেত:
এই সংকেতগুলি ব্যবহারকারীর কাছ থেকে (Ctrl+C / Ctrl+D), অন্য একটি প্রোগ্রাম/প্রসেস বা সিস্টেম থেকে (কার্নেল/OS) পাঠানো যেতে পারে, উদাহরণস্বরূপ, একটি SIGSEGV ওরফে সেগমেন্টেশন ফল্ট OS দ্বারা পাঠানো হয়।
একটি ব্যবহারিক সেটিংয়ে মনোমুগ্ধকর শাটডাউনের বিশ্ব অন্বেষণ করতে, আসুন একটি সাধারণ পরিষেবা তৈরি করি যা আমরা পরীক্ষা করতে পারি৷ এই "গিনিপিগ" পরিষেবাটির একটি একক শেষ পয়েন্ট থাকবে যা Redis-এর INCR কমান্ডকে কল করে কিছু বাস্তব-বিশ্বের কাজ (আমরা সামান্য বিলম্ব যোগ করব) অনুকরণ করে৷ প্ল্যাটফর্মটি কীভাবে সমাপ্তির সংকেতগুলি পরিচালনা করে তা পরীক্ষা করার জন্য আমরা একটি মৌলিক Kubernetes কনফিগারেশনও প্রদান করব।
চূড়ান্ত লক্ষ্য: নিশ্চিত করুন যে আমাদের পরিষেবাটি কোনো অনুরোধ/ডেটা হারানো ছাড়াই শাটডাউনগুলিকে সুন্দরভাবে পরিচালনা করে। Redis-এ চূড়ান্ত কাউন্টার মানের সাথে সমান্তরালভাবে পাঠানো অনুরোধের সংখ্যার তুলনা করে, আমরা আমাদের সুন্দর শাটডাউন বাস্তবায়ন সফল কিনা তা যাচাই করতে সক্ষম হব।
আমরা Kubernetes ক্লাস্টার এবং Redis সেট আপ করার বিশদ বিবরণে যাব না, তবে আপনি আমাদের Github সংগ্রহস্থলে সম্পূর্ণ সেটআপটি খুঁজে পেতে পারেন।
যাচাইকরণ প্রক্রিয়াটি নিম্নরূপ:
আমাদের বেস গো HTTP সার্ভার দিয়ে শুরু করা যাক।
hard-shutdown/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-এর কম হয় (প্রতিবারে সংখ্যাটি আলাদা হতে পারে)।
যার স্পষ্ট অর্থ হল রোলিং আপডেটের সময় আমরা কিছু ডেটা হারিয়েছি। 😢
Go একটি সিগন্যাল প্যাকেজ প্রদান করে যা আপনাকে ইউনিক্স সিগন্যাল পরিচালনা করতে দেয়। এটি লক্ষ্য করা গুরুত্বপূর্ণ যে ডিফল্টরূপে, SIGINT এবং SIGTERM সংকেতগুলি Go প্রোগ্রাম থেকে প্রস্থান করে। এবং আমাদের গো অ্যাপ্লিকেশন যাতে হঠাৎ করে প্রস্থান না করে, তার জন্য আমাদের আগত সংকেতগুলি পরিচালনা করতে হবে।
এটি করার জন্য দুটি বিকল্প রয়েছে।
চ্যানেল ব্যবহার করে:
c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGTERM)
প্রসঙ্গ ব্যবহার করে (আজকাল পছন্দের পদ্ধতি):
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM) defer stop()
NotifyContext মূল প্রসঙ্গটির একটি অনুলিপি প্রদান করে যাকে সম্পন্ন হিসাবে চিহ্নিত করা হয়েছে (এর সম্পন্ন চ্যানেল বন্ধ) যখন তালিকাভুক্ত সংকেতগুলির মধ্যে একটি আসে, যখন ফেরানো স্টপ() ফাংশনটি কল করা হয়, বা যখন অভিভাবক প্রসঙ্গটির সম্পন্ন চ্যানেল বন্ধ করা হয়, যেটি প্রথমে ঘটবে .
HTTP সার্ভারের আমাদের বর্তমান বাস্তবায়নে কিছু সমস্যা রয়েছে:
এর আবার লিখুন.
graceful-shutdown/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) }
যেহেতু আমরা আমাদের পরিষেবা স্থাপনের জন্য কুবারনেটস ব্যবহার করেছি, আসুন এটি কীভাবে পডগুলিকে শেষ করে তা আরও গভীরে ঢোকা যাক৷ একবার কুবারনেটস পডটি বন্ধ করার সিদ্ধান্ত নিলে, নিম্নলিখিত ঘটনাগুলি ঘটবে:
আপনি দেখতে পাচ্ছেন, যদি আপনার একটি দীর্ঘ-চলমান সমাপ্তি প্রক্রিয়া থাকে, তাহলে সমাপ্তি গ্রেসপিরিয়ড সেকেন্ড সেটিং বাড়ানোর প্রয়োজন হতে পারে, যাতে আপনার আবেদনটি ভালোভাবে বন্ধ করার জন্য যথেষ্ট সময় দেয়।
গ্রেসফুল শাটডাউনগুলি ডেটা অখণ্ডতা রক্ষা করে, একটি নিরবচ্ছিন্ন ব্যবহারকারীর অভিজ্ঞতা বজায় রাখে এবং সম্পদ ব্যবস্থাপনাকে অপ্টিমাইজ করে। এর সমৃদ্ধ স্ট্যান্ডার্ড লাইব্রেরি এবং একযোগে জোর দিয়ে, Go বিকাশকারীদের অনায়াসে আকর্ষণীয় শাটডাউন অনুশীলনগুলিকে একীভূত করার ক্ষমতা দেয় – কুবারনেটসের মতো কন্টেইনারাইজড বা অর্কেস্ট্রেটেড পরিবেশে মোতায়েন করা অ্যাপ্লিকেশনগুলির জন্য একটি প্রয়োজনীয়তা।
আপনি আমাদের Github সংগ্রহস্থলে Go কোড এবং Kubernetes ম্যানিফেস্টগুলি খুঁজে পেতে পারেন।