Backend Development 12 min read

Graceful Shutdown Library for Go (G.S): Design, Usage, and Code Walkthrough

This article introduces the Go‑based G.S library for graceful application shutdown, analyzes common container‑related pitfalls, explains its lightweight architecture with TerminateSignal and WaitingUnit modules, provides installation steps, a complete usage example, and detailed code explanations to help developers implement reliable signal handling.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Graceful Shutdown Library for Go (G.S): Design, Usage, and Code Walkthrough

Graceful Shutdown Library for Go (G.S)

Author Lee, a veteran with 17 years in IT, observes that many projects nearing release suffer from poor container‑design practices, such as ignoring proper signal handling (SIGINT/SIGTERM/SIGQUIT) and lacking unified standards, which leads to unstable pod restarts and scaling.

Pain‑point Analysis

Insufficient awareness of container design principles Developers often overlook container design guidelines. Container issues are mistakenly treated as operations‑only problems.

Oversimplified coding attitude Some believe container functionality is trivial and avoid writing robust code. This leads to accepting any code that merely runs, regardless of quality.

Lack of unified standards affecting code reuse Without common standards, developers implement features independently. Resulting code is hard to reuse across projects.

The author extracted, refactored, and open‑sourced a solution to address these widespread issues.

Introduction

G.S – https://github.com/shengyanli1982/gs

G.S is a Go library that provides an easy way to achieve graceful shutdown of applications, supporting multiple signals, various shutdown methods, concurrent object termination, and customizable timeout handling. It has been used in production for over two years.

Architecture Design

The library consists of two modules:

TerminateSignal

WaitingUnit

Its core principles are "lightweight, easy‑to‑use, fast".

Module Overview

1. TerminateSignal

TerminateSignal is a struct that registers callbacks to be executed when a termination signal arrives. It supports a timeout mechanism and can accept an external context.Context to synchronize with other logic.

Key methods:

CancelCallbacksRegistry(...func()) – registers callbacks for cancellation.

GetStopCtx() – returns the context associated with the stop signal.

Close(wg *sync.WaitGroup) – executes all registered callbacks, sends the shutdown signal, and waits for completion.

2. WaitingUnit

WaitingUnit blocks until all termination signals have finished executing their callbacks. It listens for SIGINT, SIGTERM, and SIGQUIT, making it an event‑driven design.

A single WaitingUnit can control multiple TerminateSignal instances, enabling coordinated graceful shutdown of many resources.

Installation

go get github.com/shengyanli1982/gs

Usage Example

The following example demonstrates how to use G.S with three simulated services, each exposing a different shutdown method.

package main

import (
    "fmt"
    "os"
    "time"
    "github.com/shengyanli1982/gs"
)

// simulate a service
type testTerminateSignal struct{}
func (t *testTerminateSignal) Close() { fmt.Println("testTerminateSignal.Close()") }

// simulate another service
type testTerminateSignal2 struct{}
func (t *testTerminateSignal2) Shutdown() { fmt.Println("testTerminateSignal2.Shutdown()") }

// simulate a third service
type testTerminateSignal3 struct{}
func (t *testTerminateSignal3) Terminate() { fmt.Println("testTerminateSignal3.Terminate()") }

func main() {
    // Create TerminateSignal instance
    s := gs.NewDefaultTerminateSignal()

    // Resources to be closed on termination
    t1 := &testTerminateSignal{}
    t2 := &testTerminateSignal2{}
    t3 := &testTerminateSignal3{}

    // Register their shutdown methods
    s.CancelCallbacksRegistry(t1.Close, t2.Shutdown, t3.Terminate)

    // Send an interrupt signal after 2 seconds
    go func() {
        time.Sleep(2 * time.Second)
        p, err := os.FindProcess(os.Getpid())
        if err != nil { fmt.Println(err.Error()) }
        err = p.Signal(os.Interrupt)
        if err != nil { fmt.Println(err.Error()) }
    }()

    // Wait for graceful shutdown
    gs.WaitingForGracefulShutdown(s)

    fmt.Println("shutdown gracefully")
}

Running the program produces:

# go run main.go
testTerminateSignal3.Terminate()
testTerminateSignal.Close()
testTerminateSignal2.Shutdown()
shutdown gracefully

Code Analysis

The project mainly consists of two files: terminal.go and gracefull.go . Key excerpts are shown below.

terminal.go – TerminateSignal implementation // Register callbacks to be canceled func (s *TerminateSignal) CancelCallbacksRegistry(callbacks ...func()) { s.exec = append(s.exec, callbacks...) } // Get the stop signal's Context func (s *TerminateSignal) GetStopCtx() context.Context { return s.ctx } // Close the TerminateSignal instance func (s *TerminateSignal) Close(wg *sync.WaitGroup) { s.once.Do(func() { for _, cb := range s.exec { if cb != nil { s.wg.Add(1) go s.worker(cb) } } s.cancel() s.wg.Wait() if wg != nil { wg.Done() } }) } func (s *TerminateSignal) worker(callback func()) { defer s.wg.Done() <-s.ctx.Done() callback() }

gracefull.go – WaitingUnit implementation // Wait for all shutdown signals func WaitingForGracefulShutdown(sigs ...*TerminateSignal) { quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) <-quit signal.Stop(quit) close(quit) if len(sigs) > 0 { var wg sync.WaitGroup wg.Add(len(sigs)) for _, s := range sigs { go s.Close(&wg) } wg.Wait() } }

The WaitingForGracefulShutdown function blocks until a termination signal is received, then triggers all registered callbacks via the associated TerminateSignal instances.

Conclusion

By designing and implementing the G.S library, the author provides a minimal‑cost solution for graceful shutdown that can be reused across multiple services without extensive code changes. The library demonstrates a practical approach to reliable signal handling in Go backend applications, and the author invites feedback and contributions.

backendConcurrencyGolibraryGraceful ShutdownSignal Handling
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.