Skip to content

Grace manages long-running goroutines gracefully.

License

Notifications You must be signed in to change notification settings

beiping96/grace

Repository files navigation

Grace

Build Status GoDoc Go Report Card CI On Push

Grace manages long-running goroutines gracefully by trapping system signals and canceling context.Context.

import "github.com/beiping96/grace"

The core of Grace is type Goroutine func(ctx context.Context).

Usage

Simple

package main

import(
    "context"
    "time"
    "github.com/beiping96/grace"
)

func main() {
    // Register worker
    grace.Go(worker)

    // Start worker and hold main goroutine.
    // After process received system signal, 
    // worker should exit in two seconds,
    // otherwise, be killed.
    grace.Run(time.Second * time.Duration(2)) 
}

func worker(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            // receive stop signal
            return
        default:
        }
        // do some job costs second
        time.Sleep(time.Second)      
    }
}

Pub/Sub

package main

import(
    "context"
    "time"
    "github.com/beiping96/grace"
)

func main() {
    // Register puber
    grace.Go(puber)
    // Register 2 suber
    for i := 0; i < 2; i++ { 
        grace.Go(suber) 
    }

    grace.Run(time.Second) // start
}

var (
    jobChan = make(chan struct{}, 1)
)

func puber(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            // receive stop signal
            close(jobChan)
            return
        default:
        }
        // load job
        var (
            job struct{}
        )
        // pub job
        jobChan <- job 
    }
}

func suber(ctx context.Context) {
    for job := range jobChan {
        // handle job
    	println(job)
    }
}

HTTPD

package main

import (
    "context"
    "net/http"
    "time"
    "github.com/beiping96/grace"
)

func httpd(ctx context.Context) {
    server := &http.Server{}
    
    grace.Go(func(ctx context.Context) {
        <-ctx.Done()
        server.Shutdown(context.Background())
    })
    
    server.ListenAndServe()
}

func main() {
    // Register httpd
    grace.Go(httpd)
    
    // Start
    // After process received system signal, 
    // wait all http connection closed is ten seconds
    grace.Run(time.Second * time.Duration(10)) 
}

Option

package main

import (
    "context"
    "time"
    "github.com/beiping96/grace"
)

func main() {
    // After one minute, the worker's ctx will be canceled
    grace.Go(worker, grace.OptionExpire(time.Minute))    
    
    // If worker stopped, it will be restart
    // The max number of restart is 2
    grace.Go(worker, grace.OptionRestart(2))
    
    // If worker stopped, it will be restart
    // The max number of restart is 2
    // Each worker can executed one minute
    grace.Go(worker, grace.OptionRestart(2),
                     grace.OptionExpire(time.Minute))
    
    // If worker stopped, it will be restart
    // The max number of restart is 2
    // All workers only have one minute to executed
    grace.Go(worker, grace.OptionExpire(time.Minute),
                     grace.OptionRestart(2))

    grace.Run(time.Second)
}


func worker(ctx context.Context) {}

Custom

package main

import (
    "log"
    "context"
    "syscall"
    "time"
    "github.com/beiping96/grace"
)

func main() {
    // Declare stop signals
    // Default is syscall.SIGINT, syscall.SIGQUIT or syscall.SIGTERM
    grace.Signal(syscall.SIGQUIT, syscall.SIGTERM)

    // Declare logger method
    // Default is os.Stdout
    grace.Log(log.Printf)

    // Declare process id folder
    // Default is unable
    grace.PID("./")
    
    // Register goroutine
    grace.Go(worker)
    
    // Register dynamic goroutine
    grace.Go(func(ctx context.Context) { grace.Go(worker) })

    // Never return
    // Stopped when receiving stop signal
    // or all goroutines are exit
    grace.Run(time.Second)
}

func worker(ctx context.Context) {}