Graceful Shutdown in a Go HTTP Server
Graceful shutdown is crucial for production applications to avoid data loss and ensure a smooth user experience. This challenge asks you to implement a Go HTTP server that gracefully handles shutdown signals (SIGINT and SIGTERM), allowing it to finish processing in-flight requests before exiting. This is vital for preventing incomplete transactions and ensuring data consistency.
Problem Description
You are tasked with creating a simple HTTP server that listens on port 8080 and responds with "Hello, World!". The server must gracefully shut down when it receives either a SIGINT (Ctrl+C) or SIGTERM signal. Graceful shutdown means the server should:
- Stop accepting new connections.
- Wait for all currently in-flight requests to complete.
- Close any open connections.
- Exit cleanly.
To simulate in-flight requests, the server will include a delay of 2 seconds in its handler. This allows you to observe the graceful shutdown behavior while requests are still being processed.
Key Requirements:
- Use the
net/httppackage for creating the HTTP server. - Use the
contextpackage for managing request contexts and timeouts. - Use the
osandsignalpackages to listen for SIGINT and SIGTERM signals. - Implement a
Shutdownfunction that handles the shutdown process. - The server should log messages indicating when it starts, stops accepting new connections, and shuts down completely.
Expected Behavior:
When the server is running, sending a SIGINT or SIGTERM signal should trigger the graceful shutdown sequence. The server should continue to serve existing requests until they complete (after a 2-second delay), then log a shutdown message and exit. New requests received after the signal is sent should not be processed.
Edge Cases to Consider:
- What happens if a request takes longer than the timeout to complete? (While not explicitly required, consider how your solution might handle this).
- How does the server handle multiple signals received in quick succession?
- What happens if the server is already shutting down when another signal is received?
Examples
Example 1:
Input: Server running, send SIGINT (Ctrl+C)
Output:
- Server logs: "Server started on :8080"
- Server logs: "Shutting down..."
- Server logs: "Server stopped accepting new connections"
- After 2 seconds (simulated request processing time): Server logs: "Server shutting down"
- Server exits cleanly.
Example 2:
Input: Server running, send SIGTERM
Output:
- Server logs: "Server started on :8080"
- Server logs: "Shutting down..."
- Server logs: "Server stopped accepting new connections"
- After 2 seconds (simulated request processing time): Server logs: "Server shutting down"
- Server exits cleanly.
Example 3: (Multiple Signals)
Input: Server running, send SIGINT, then immediately send SIGTERM
Output:
- Server logs: "Server started on :8080"
- Server logs: "Shutting down..."
- Server logs: "Server stopped accepting new connections"
- After 2 seconds (simulated request processing time): Server logs: "Server shutting down"
- Server exits cleanly. (The second signal should not interrupt the first shutdown sequence)
Constraints
- The server must listen on port 8080.
- The request handler must include a 2-second delay.
- The shutdown process must complete within a reasonable timeframe (e.g., 5 seconds). While not strictly enforced, excessively long shutdown times are undesirable.
- The code should be well-structured and readable.
- Error handling should be present, but concise. Focus on the graceful shutdown logic.
Notes
- Consider using a
context.Contextwith a timeout to manage the lifetime of requests. - The
http.Servertype provides methods for graceful shutdown. Explore these methods. - Think about how to signal to the server that it should stop accepting new connections.
- The goal is to demonstrate a robust and reliable shutdown mechanism, not to build a fully featured HTTP server. Keep the code focused on the core shutdown functionality.