Signal Handling
Go menyediakan package os/signal untuk menangani sinyal sistem operasi seperti SIGTERM, SIGINT, dll. Ini penting untuk graceful shutdown dan manajemen resource.
Contoh Masalah
Bagaimana cara:
- Menangani sinyal sistem
- Implementasi graceful shutdown
- Cleanup resources
- Handle multiple signals
Penyelesaian
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"sync"
"syscall"
"time"
)
// 1. Basic worker
type Worker struct {
id int
done chan struct{}
logger *log.Logger
}
func NewWorker(id int) *Worker {
return &Worker{
id: id,
done: make(chan struct{}),
logger: log.New(os.Stdout, fmt.Sprintf("[Worker %d] ", id),
log.Ltime),
}
}
func (w *Worker) Start() {
w.logger.Println("Starting worker...")
go w.run()
}
func (w *Worker) Stop() {
w.logger.Println("Stopping worker...")
close(w.done)
}
func (w *Worker) run() {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
w.logger.Println("Working...")
case <-w.done:
w.logger.Println("Stopped")
return
}
}
}
// 2. Resource manager
type ResourceManager struct {
workers []*Worker
server *http.Server
wg sync.WaitGroup
logger *log.Logger
}
func NewResourceManager() *ResourceManager {
return &ResourceManager{
logger: log.New(os.Stdout, "[Manager] ", log.Ltime),
}
}
func (rm *ResourceManager) StartWorkers(count int) {
rm.logger.Printf("Starting %d workers...\n", count)
for i := 0; i < count; i++ {
worker := NewWorker(i + 1)
rm.workers = append(rm.workers, worker)
worker.Start()
}
}
func (rm *ResourceManager) StartServer() {
rm.logger.Println("Starting HTTP server...")
// Create server
rm.server = &http.Server{
Addr: ":8080",
Handler: http.HandlerFunc(func(w http.ResponseWriter,
r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
}),
}
// Start server in goroutine
rm.wg.Add(1)
go func() {
defer rm.wg.Done()
if err := rm.server.ListenAndServe(); err != http.ErrServerClosed {
rm.logger.Printf("HTTP server error: %v\n", err)
}
}()
}
func (rm *ResourceManager) Shutdown(ctx context.Context) error {
rm.logger.Println("Shutting down...")
// Stop HTTP server
if rm.server != nil {
rm.logger.Println("Stopping HTTP server...")
if err := rm.server.Shutdown(ctx); err != nil {
return fmt.Errorf("server shutdown: %v", err)
}
}
// Stop workers
rm.logger.Println("Stopping workers...")
for _, worker := range rm.workers {
worker.Stop()
}
// Wait for all goroutines to finish
rm.wg.Wait()
return nil
}
// 3. Signal handler
type SignalHandler struct {
signals chan os.Signal
rm *ResourceManager
logger *log.Logger
shutdownCtx context.Context
cancel context.CancelFunc
}
func NewSignalHandler(rm *ResourceManager) *SignalHandler {
ctx, cancel := context.WithTimeout(context.Background(),
10*time.Second)
return &SignalHandler{
signals: make(chan os.Signal, 1),
rm: rm,
logger: log.New(os.Stdout, "[Signal] ", log.Ltime),
shutdownCtx: ctx,
cancel: cancel,
}
}
func (sh *SignalHandler) Start() {
// Register for signals
signal.Notify(sh.signals,
syscall.SIGINT,
syscall.SIGTERM,
syscall.SIGHUP)
// Handle signals
go func() {
for sig := range sh.signals {
sh.logger.Printf("Received signal: %v\n", sig)
switch sig {
case syscall.SIGHUP:
// Reload configuration
sh.logger.Println("Reloading configuration...")
case syscall.SIGINT, syscall.SIGTERM:
// Graceful shutdown
sh.logger.Println("Starting graceful shutdown...")
if err := sh.rm.Shutdown(sh.shutdownCtx); err != nil {
sh.logger.Printf("Shutdown error: %v\n", err)
}
sh.cancel()
return
}
}
}()
}
func (sh *SignalHandler) Wait() {
<-sh.shutdownCtx.Done()
if err := sh.shutdownCtx.Err(); err == context.DeadlineExceeded {
sh.logger.Println("Shutdown timed out")
}
}
func main() {
// Setup logging
log.SetFlags(log.Ltime | log.Lmicroseconds)
log.Println("Starting application...")
// Create resource manager
rm := NewResourceManager()
// Start workers
rm.StartWorkers(3)
// Start HTTP server
rm.StartServer()
// Setup signal handling
sh := NewSignalHandler(rm)
sh.Start()
log.Println("Application is ready")
log.Println("Press Ctrl+C to shutdown")
// Wait for shutdown
sh.Wait()
log.Println("Application stopped")
}
Penjelasan Kode
Signal Types
- SIGTERM
- SIGINT
- SIGHUP
Graceful Shutdown
- Context timeout
- Resource cleanup
- Wait groups
Best Practices
- Error handling
- Timeout management
- Resource management
Output
11:57:22 Starting application...
11:57:22 [Manager] Starting 3 workers...
11:57:22 [Worker 1] Starting worker...
11:57:22 [Worker 2] Starting worker...
11:57:22 [Worker 3] Starting worker...
11:57:22 [Manager] Starting HTTP server...
11:57:22 Application is ready
11:57:22 Press Ctrl+C to shutdown
11:57:23 [Worker 1] Working...
11:57:23 [Worker 2] Working...
11:57:23 [Worker 3] Working...
11:57:24 [Worker 1] Working...
11:57:24 [Worker 2] Working...
11:57:24 [Worker 3] Working...
^C
11:57:25 [Signal] Received signal: interrupt
11:57:25 [Signal] Starting graceful shutdown...
11:57:25 [Manager] Shutting down...
11:57:25 [Manager] Stopping HTTP server...
11:57:25 [Manager] Stopping workers...
11:57:25 [Worker 1] Stopping worker...
11:57:25 [Worker 2] Stopping worker...
11:57:25 [Worker 3] Stopping worker...
11:57:25 [Worker 1] Stopped
11:57:25 [Worker 2] Stopped
11:57:25 [Worker 3] Stopped
11:57:25 Application stopped
Tips
- Handle sinyal yang relevan
- Implementasi timeout
- Cleanup semua resources
- Log semua events
- Test shutdown scenarios