forked from ctripcorp/nephele
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp.go
146 lines (128 loc) · 3.27 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
137
138
139
140
141
142
143
144
145
146
package app
import (
"fmt"
"github.com/ctripcorp/nephele/context"
"github.com/ctripcorp/nephele/service"
"github.com/urfave/cli"
"log"
"os"
"os/signal"
"syscall"
"time"
)
// App represents nephele application.
type App struct {
conf Config
server *Server
internal *cli.App
}
// Define configurator by runtime environment
type Configurator func(string) Config
// Return new nephele application with given configuration.
func New(configure Configurator) *App {
app := new(App)
app.internal = cli.NewApp()
app.internal.Name = "Nephele"
app.internal.Usage = "Powerful image service!"
app.internal.Flags = []cli.Flag{
cli.StringFlag{
Name: "config, c",
Usage: "load configuration from given path.",
},
cli.StringFlag{
Name: "env, e",
Value: "dev",
Usage: "set app environment.",
EnvVar: "NEPHELE_ENV",
},
cli.BoolFlag{
Name: "open, o",
Usage: "open server to serve image",
},
}
app.internal.Action = func(ctx *cli.Context) error {
if ctx.Bool("open") {
if err := app.open(ctx, configure); err != nil {
return cli.NewExitError(err.Error(), 1)
}
}
return nil
}
return app
}
// Return server to make initialization or configure service router.
func (app *App) Server() *Server {
return app.server
}
// Run nephele application.
func (app *App) Run() {
if err := app.internal.Run(os.Args); err != nil {
log.Fatal(err.Error())
}
}
// Open server
func (app *App) open(ctx *cli.Context, configure Configurator) error {
var err error
env := ctx.String("env")
path := ctx.String("config")
app.conf = configure(env)
if err = app.conf.LoadFrom(env, path); err != nil {
return err
}
s := app.buildServer(app.conf)
if err = s.init(); err != nil {
return err
}
select {
case err = <-app.acceptSignals():
case err = <-s.open():
}
return err
}
func (app *App) reload() error {
return app.conf.Reload()
}
// Close server gracefully.
func (app *App) quit() error {
return app.server.quit()
}
// Build a new server.
func (app *App) buildServer(conf Config) *Server {
// create root context.
ctx := context.New(conf.Env(), time.Duration(conf.Service().RequestTimeout)*time.Millisecond)
app.server = &Server{
conf: conf,
service: service.New(ctx, conf.Service()),
}
return app.server
}
// register signal to c.
// when syscall.SIGINT is received, error log should be written.
// when syscall.SIGHUP is received, app will be reload.
// when syscall.SIGKILL is received, app will forcibly closed.
// when syscall.SIGTERM is received, server will quit gracefully.
func (app *App) acceptSignals() <-chan error {
c := make(chan error)
go func() {
sc := make(chan os.Signal)
signal.Notify(sc, syscall.SIGINT, syscall.SIGHUP, syscall.SIGTERM)
sig := <-sc
switch sig {
case syscall.SIGHUP:
if err := app.reload(); err != nil {
c <- fmt.Errorf("app closed unexpectedly for reload failed. error:%s", err.Error())
}
case syscall.SIGTERM:
{
if err := app.quit(); err != nil {
c <- fmt.Errorf("app recevied signal:%s, but quit failed. error:%s", sig.String(), err.Error())
} else {
c <- fmt.Errorf("app closed gracefully for receiving signal:%s", sig.String())
}
}
case syscall.SIGINT:
c <- fmt.Errorf("app closed unexpectedly for receiving signal:%s", sig.String())
}
}()
return c
}