Graceful Shutdown
Graceful shutdown menghentikan server menerima koneksi baru sambil membiarkan request yang sedang berjalan selesai, jadi deploy atau restart kontainer tidak pernah memotong response di tengah jalan. Deserve menangani ini secara bawaan, dan sebuah AbortSignal membuka jalan untuk memicunya dari kode.
Penanganan Sinyal Bawaan
router.serve() polos sudah mendengarkan sinyal yang dikirim process manager saat berhenti. Pada SIGHUP, SIGINT (Ctrl+C di terminal), atau SIGTERM (yang dikirim Docker dan kebanyakan orchestrator), server berhenti menerima request baru, menguras yang masih berjalan, lalu meresolusi promise serve():
import { Router } from '@neabyte/deserve'
const router = new Router({
routes: { directory: './routes' }
})
// Resolusi setelah pengurasan selesai
await router.serve(8000)
// Tercapai hanya setelah shutdown bersih
console.log('Server stopped')Windows menggantinya dengan SIGBREAK dan SIGINT, karena sinyal POSIX tidak dikirim di sana. Tidak ada penyetelan yang diperlukan untuk jalur ini, jadi server dalam kontainer sudah keluar dengan bersih saat docker stop. Tiap sinyal yang diterima juga memancarkan event process:failed dengan origin: 'process:signal' tepat sebelum pengurasan mulai, jadi alasan berhenti mendarat di bus yang sama dengan setiap kesalahan lain.
Memicu Shutdown Dari Kode
Meneruskan AbortSignal sebagai argumen ketiga menyerahkan pemicu ke aplikasi, yang cocok untuk test yang perlu menghentikan server atau route admin yang mengakhiri proses. Membatalkan controller menguras server dengan cara yang sama seperti sinyal:
const controller = new AbortController()
// Hentikan server setelah tiga puluh detik
setTimeout(() => controller.abort(), 30_000)
// Abort menguras, lalu serve resolusi
await router.serve(8000, '0.0.0.0', controller.signal)Sebuah AbortSignal berjalan berdampingan dengan listener bawaan alih-alih menggantikannya, jadi SIGTERM dari host dan abort() dari kode keduanya mencapai pengurasan yang sama. Mana pun yang menyala lebih dulu menghentikan server, dan yang lain menjadi no-op begitu pengurasan berlangsung. Menyambungkan listener sinyal untuk memanggil controller.abort() adalah cara melipat kedua pemicu ke satu jalur saat itu tujuannya.
Menjalankan Kerja Saat Shutdown
Pembersihan seperti menutup pool basis data atau membilas buffer berada setelah pengurasan, bukan di dalamnya. Event server:stopped menyala setelah server selesai dikuras, jadi satu listener observability menjaga kerja shutdown tetap di satu tempat:
router.on((event) => {
// Berjalan setelah pengurasan selesai
if (event.kind === 'server:stopped') {
console.log('Closing resources')
}
})
await router.serve(8000)Event pasangannya server:started menyala saat server mengikat port, jadi kait startup dan shutdown berdampingan di bus yang sama.
Arti Pengurasan Bagi Sebuah Request
Request yang sedang berjalan saat pengurasan mulai akan tuntas sampai selesai, dan response-nya tetap terkirim. Koneksi yang tiba setelah pengurasan dimulai ditolak, karena listener sudah berhenti menerima. Response berumur panjang adalah satu hal yang perlu diperhatikan, karena stream atau WebSocket yang terbuka menahan pengurasan sampai koneksi itu ditutup. Membatasi berapa lama satu request boleh berjalan dengan timeoutMs menjaga pengurasan tidak menunggu selamanya pada handler yang lambat.