Graceful shutdown adalah mekanisme ketika sebuah mesin atau aplikasi dimatikan, namun proses pematikan tersebut dilakukan secara bertahap dan terkontrol. Sistem akan menunggu hingga semua service atau request yang sedang diproses selesai terlebih dahulu sebelum benar-benar mematikan server. Dengan demikian, tidak ada request yang terputus di tengah jalan, sehingga pengalaman pengguna tetap terjaga dan data tidak hilang.
Mengapa Graceful Shutdown Penting?
Pernahkah Anda berpikir, ketika kita mematikan server, apakah request yang sedang diproses akan tetap dilanjutkan atau langsung terhenti? Jika tidak menggunakan mekanisme graceful shutdown, maka request yang sedang berjalan akan langsung dihentikan begitu server dimatikan. Hal ini tentu saja dapat menyebabkan data corrupt, kehilangan data, atau pengalaman pengguna yang buruk.
Dengan menggunakan graceful shutdown, ketika server menerima perintah untuk dimatikan (misalnya melalui sinyal SIGTERM atau Interrupt), server akan menunggu hingga semua request yang sedang berjalan selesai diproses. Setelah itu, barulah server benar-benar dimatikan. Ini sangat penting terutama pada aplikasi yang menangani transaksi penting atau data sensitif.
Contoh Implementasi di Go
Pada contoh ini, kita akan menggunakan bahasa pemrograman Go untuk membuat sebuah endpoint API sederhana dan mengimplementasikan mekanisme graceful shutdown.
Membuat Handler Sederhana
Pertama, buat handler untuk endpoint root:
func index(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]any{
"code": http.StatusOK,
"message": "server up",
})
}
Fungsi Main Tanpa Graceful Shutdown
Berikut adalah contoh fungsi main
tanpa menggunakan graceful shutdown:
func main() {
srv := &http.Server{
Addr: "localhost:3000",
Handler: nil,
}
http.HandleFunc("/", index)
log.Println("server running at", srv.Addr)
if err := srv.ListenAndServe(); err != nil {
log.Fatal("error listening", err)
}
}
Endpoint ini akan mengembalikan response JSON sederhana.
curl http://localhost:3000
# Output:
# {"code":200,"message":"server up"}
Skenario Tanpa Graceful Shutdown
Sekarang, mari kita lihat apa yang terjadi jika server dimatikan tanpa mekanisme graceful shutdown. Misalnya, kita tambahkan delay pada handler:
func index(w http.ResponseWriter, r *http.Request) {
time.Sleep(10 * time.Second) // simulasi delay 10 detik
json.NewEncoder(w).Encode(map[string]any{
"code": http.StatusOK,
"message": "server up",
})
}
Pada GIF di atas, request ke endpoint root membutuhkan waktu 10 detik. Jika server dimatikan saat request sedang diproses, maka request tersebut akan langsung terputus.
Terlihat bahwa server langsung mati dan request yang sedang berjalan juga terhenti. Ini tentu saja tidak ideal, terutama jika aplikasi Anda menangani transaksi penting.
Skenario Menggunakan Graceful Shutdown
Untuk mengatasi masalah di atas, kita bisa mengimplementasikan graceful shutdown. Berikut langkah-langkahnya:
- Jalankan server menggunakan goroutine.
- Buat channel untuk menerima sinyal SIGTERM atau Interrupt.
- Shutdown server menggunakan context dengan timeout (misal 30 detik).
Mengapa menggunakan channel? Karena channel bersifat blocking, sehingga program akan menunggu hingga menerima sinyal sebelum melanjutkan proses shutdown.
Berikut contoh implementasinya:
func main() {
srv := &http.Server{
Addr: "localhost:3000",
Handler: nil,
}
http.HandleFunc("/", index)
// Jalankan server di goroutine
go func() {
log.Println("server running at", srv.Addr)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatal("error listening", err)
}
}()
// Buat channel untuk menerima sinyal interrupt atau SIGTERM
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
// Tunggu sinyal masuk ke channel
sig := <-c
log.Println("got signal", sig)
defer close(c)
// Buat context dengan timeout 30 detik
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// Proses shutdown server
log.Println("shutting down server..")
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("error shutting down server", err)
}
log.Println("server shutted down successfully")
}
Penjelasan kode di atas:
- Server dijalankan di goroutine agar proses utama tidak terblokir.
- Channel digunakan untuk menangkap sinyal interrupt (Ctrl+C) atau SIGTERM.
- Setelah sinyal diterima, server akan melakukan shutdown dengan context timeout, sehingga server masih memberi kesempatan request yang sedang berjalan untuk selesai dalam waktu maksimal 30 detik.
Pada GIF di atas, ketika server menerima perintah shutdown saat ada request yang sedang diproses, server akan menunggu hingga request tersebut selesai sebelum benar-benar dimatikan.
Perbandingan
Jika dibandingkan dengan skenario tanpa graceful shutdown, perbedaannya sangat jelas. Dengan graceful shutdown, server akan memastikan semua proses yang sedang berjalan selesai dengan baik sebelum dimatikan, sehingga tidak ada request yang terputus secara tiba-tiba.
Tanpa Graceful Shutdown | Dengan Graceful Shutdown |
---|---|
Request terputus tiba-tiba saat server dimatikan | Request yang sedang berjalan diberi kesempatan untuk selesai |
Potensi data corrupt atau hilang | Data lebih aman dan integritas terjaga |
Pengalaman pengguna buruk | Pengalaman pengguna lebih baik |
Kesimpulan
Graceful shutdown sangat penting untuk menjaga integritas data dan pengalaman pengguna, terutama pada aplikasi backend yang menangani banyak request secara bersamaan. Mekanisme ini tidak hanya bisa diimplementasikan di Go, tetapi juga di bahasa pemrograman lain seperti Node.js, Java Spring Boot, Python (dengan signal dan context), dan sebagainya.
Dengan menerapkan graceful shutdown, Anda dapat memastikan aplikasi tetap andal dan profesional, bahkan saat harus dimatikan atau di-restart.