-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathapp.go
136 lines (120 loc) · 2.81 KB
/
app.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package kratos
import (
"context"
"os"
"os/signal"
"syscall"
"time"
"golang.org/x/sync/errgroup"
)
// Hook is a pair of start and stop callbacks.
type Hook struct {
OnStart func(context.Context) error
OnStop func(context.Context) error
}
// Option is a life cycle option.
type Option func(o *options)
// options is a life cycle options.
type options struct {
startTimeout time.Duration
stopTimeout time.Duration
signals []os.Signal
signalFn func(*App, os.Signal)
}
// StartTimeout with the start timeout.
func StartTimeout(d time.Duration) Option {
return func(o *options) { o.startTimeout = d }
}
// StopTimeout with the start timeout.
func StopTimeout(d time.Duration) Option {
return func(o *options) { o.stopTimeout = d }
}
// Signal with os signals.
func Signal(sig ...os.Signal) Option {
return func(o *options) { o.signals = sig }
}
// SignalFn with signals handler.
func SignalFn(fn func(*App, os.Signal)) Option {
return func(o *options) { o.signalFn = fn }
}
// App is manage the application component life cycle.
type App struct {
opts options
hooks []Hook
cancel func()
}
// New new a application manage.
func New(opts ...Option) *App {
options := options{
startTimeout: time.Second * 30,
stopTimeout: time.Second * 30,
signals: []os.Signal{
syscall.SIGTERM,
syscall.SIGQUIT,
syscall.SIGINT,
},
signalFn: func(a *App, sig os.Signal) {
switch sig {
case syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM:
a.Stop()
default:
}
},
}
for _, o := range opts {
o(&options)
}
return &App{opts: options}
}
// Append register callbacks that are executed on application start and stop.
func (a *App) Append(hook Hook) {
a.hooks = append(a.hooks, hook)
}
// Run executes all OnStart hooks registered with the application's Lifecycle.
func (a *App) Run() error {
var ctx context.Context
ctx, a.cancel = context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
for _, hook := range a.hooks {
hook := hook
if hook.OnStop != nil {
g.Go(func() error {
<-ctx.Done() // wait for stop signal
stopCtx, cancel := context.WithTimeout(context.Background(), a.opts.stopTimeout)
defer cancel()
return hook.OnStop(stopCtx)
})
}
if hook.OnStart != nil {
g.Go(func() error {
startCtx, cancel := context.WithTimeout(context.Background(), a.opts.startTimeout)
defer cancel()
return hook.OnStart(startCtx)
})
}
}
if len(a.opts.signals) == 0 {
return g.Wait()
}
c := make(chan os.Signal, 1)
signal.Notify(c, a.opts.signals...)
g.Go(func() error {
for {
select {
case <-ctx.Done():
return ctx.Err()
case sig := <-c:
if a.opts.signalFn != nil {
a.opts.signalFn(a, sig)
}
}
}
})
return g.Wait()
}
// Stop gracefully stops the application.
func (a *App) Stop() {
if a.cancel != nil {
a.cancel()
}
}