How the Graceful Shutdown Mechanism Works in Kratos Application Lifecycle
Kratos orchestrates graceful shutdown through the App type defined in app.go, triggering an ordered sequence of pre-stop hooks, service deregistration, context cancellation, post-stop hooks, and coordinated server stops when the process receives SIGTERM, SIGINT, or SIGQUIT signals.
The go-kratos/kratos microservices framework provides a sophisticated lifecycle management system that ensures applications terminate cleanly without dropping active connections or leaving orphaned service registrations. Understanding the graceful shutdown mechanism is essential for building production-ready Go services that handle deployment rollbacks, container orchestration signals, and maintenance windows without data loss.
Signal Handling and Shutdown Triggers
The shutdown process begins in the Run() method of the App type, located in [app.go](https://github.com/go-kratos/kratos/blob/main/app.go). When the application starts, it creates a signal channel and registers the OS signals specified in the Signal option—defaulting to SIGTERM, SIGQUIT, and SIGINT—via signal.Notify.
A goroutine spawned at lines 33‑42 waits for either context cancellation (triggered by internal errors) or an incoming signal. Upon detection, it invokes a.Stop(), initiating the graceful shutdown sequence. Alternatively, developers can trigger shutdown programmatically by calling App.Stop() directly.
The Ordered Shutdown Sequence
Inside Stop(), the framework executes a precise six-step choreography to ensure resources release in the correct order:
1. Pre-Stop Hooks (BeforeStop)
Before any network operations cease, Stop() executes all functions registered with the BeforeStop option. Defined in [options.go](https://github.com/go-kratos/kratos/blob/main/options.go) at lines 11‑15, these hooks run sequentially to perform early cleanup tasks such as flushing metrics, closing database connections, or stopping background workers.
2. Service Deregistration
If a service registrar is configured, the application deregisters itself from the service registry (lines 63‑69 in app.go). This step prevents new traffic from routing to the terminating instance. The operation respects the RegistrarTimeout option, ensuring the deregistration attempt does not hang indefinitely.
3. Root Context Cancellation
The application cancels its root context via a.cancel() (line 71), propagating the cancellation signal to all downstream goroutines and components that utilize the App context via NewContext. This notifies every part of the system that shutdown is in progress, allowing in-flight requests to complete naturally.
4. Post-Stop Hooks (AfterStop)
After context cancellation, Stop() executes all AfterStop functions (lines 47‑50). These hooks run when the application has already stopped accepting new work but before transport servers fully terminate, making them ideal for final logging or releasing resources that depend on the context being closed.
5. Server Shutdown Coordination
Each transport server added via the Server option participates in an errgroup. During Run(), a dedicated stop routine registers for every server within the inner eg.Go block (lines 4‑12). When the root context cancels, these routines unblock and apply the StopTimeout duration (lines 6‑10) before invoking server.Stop(stopCtx), giving each protocol handler (HTTP, gRPC, etc.) time to drain connections.
6. Error Propagation
The errgroup waits for all start and stop operations to complete. If any component returns an error other than context.Canceled, it bubbles up from Run() (lines 43‑45), allowing the main function to distinguish between graceful termination and actual failures.
Configuring Shutdown Behavior
The framework exposes several functional options to tune the graceful shutdown mechanism:
- Custom Signals: Override the default signal list using
kratos.Signal(os.Interrupt, os.Kill). - Stop Timeout: Set
kratos.StopTimeout(5 * time.Second)to limit how long each server spends draining connections. - Registrar Timeout: Configure
kratos.RegistrarTimeout(10 * time.Second)to control service deregistration deadlines. - Lifecycle Hooks: Use
kratos.BeforeStop(func(ctx context.Context) error { ... })andkratos.AfterStop(...)to inject custom logic.
Complete Implementation Example
The following example demonstrates a Kratos application configured with custom timeouts and cleanup hooks:
package main
import (
"context"
"net/url"
"os"
"time"
"github.com/go-kratos/kratos/v2"
"github.com/go-kratos/kratos/v2/transport/http"
)
func main() {
// Create an HTTP server
srv := http.NewServer(
http.Address(":8080"),
)
// Build the application with lifecycle hooks
app := kratos.New(
kratos.Name("demo.service"),
kratos.Version("v1.0.0"),
kratos.Server(srv),
// Optional: custom signal list
kratos.Signal(os.Interrupt, os.Kill),
// Hook executed before service deregistration
kratos.BeforeStop(func(ctx context.Context) error {
// Flush metrics, close DB connections
return nil
}),
// Hook executed after context cancellation
kratos.AfterStop(func(ctx context.Context) error {
// Final log message
return nil
}),
// Graceful-stop timeout for each server
kratos.StopTimeout(5 * time.Second),
)
// Run blocks until shutdown signal received
if err := app.Run(); err != nil {
// Handle non-cancellation errors
}
}
In this implementation, app.Run() blocks indefinitely until the process receives an interrupt signal, at which point the graceful shutdown mechanism executes the configured hooks and timeouts in sequence.
Summary
- The
Apptype inapp.goorchestrates shutdown viaRun()andStop()methods. - Signal handling defaults to
SIGTERM,SIGQUIT, andSIGINT, customizable via theSignaloption. - BeforeStop hooks execute first for resource cleanup, followed by service deregistration from the registry.
- Context cancellation propagates to all components, after which AfterStop hooks run.
- Transport servers shut down concurrently with individual
StopTimeoutdeadlines enforced viaerrgroup. - Errors other than
context.Canceledpropagate upward for visibility into shutdown failures.
Frequently Asked Questions
What signals trigger graceful shutdown in Kratos?
By default, Kratos registers SIGTERM, SIGQUIT, and SIGINT via signal.Notify in the Run() method. You can customize this list by passing kratos.Signal() with specific os.Signal values when constructing the application.
How do I add custom cleanup logic during shutdown?
Use the BeforeStop and AfterStop functional options. BeforeStop functions execute before service deregistration and context cancellation, making them suitable for closing database pools. AfterStop functions run after context cancellation, ideal for final logging or releasing context-dependent resources.
What happens if a server exceeds the StopTimeout?
If a transport server does not stop within the duration specified by StopTimeout, the context passed to server.Stop() will expire. The specific behavior depends on the server implementation, but the errgroup will continue and eventually return, ensuring the process does not hang indefinitely.
How does Kratos handle service registry deregistration failures?
The deregistration step respects the RegistrarTimeout option. If the registry client cannot complete deregistration within this window, the operation times out and the shutdown proceeds to context cancellation and subsequent steps, preventing a hanging registry from blocking application termination.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →