add graceful shutdown for bin mode

This commit is contained in:
Alexander Baryshnikov 2020-10-09 19:37:41 +08:00
parent a183d116a1
commit 6d3d90b0e2
5 changed files with 85 additions and 30 deletions

18
_docs/modes.md Normal file
View File

@ -0,0 +1,18 @@
## BIN
Binary modes just executes any script in shell (`/bin/sh` by default). You can override shell per-unit
by `shell: /path/to/shell` configuration param.
To handle a graceful timeout, child should be able to forward signal: basically, use `exec` before last command.
Danger (but will work), signals may not be handled by foo
```yaml
command: "V=1 RAIL=2 foo bar -c -y -z"
```
Good
```yaml
command: "V=1 RAIL=2 exec foo bar -c -y -z"
```

View File

@ -3,6 +3,7 @@ package server
import (
"context"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
@ -38,6 +39,7 @@ type binHandler struct {
shell string
environment []string
timeout time.Duration
gracefulTimeout time.Duration
}
func (bh *binHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
@ -75,7 +77,29 @@ func (bh *binHandler) ServeHTTP(writer http.ResponseWriter, request *http.Reques
cmd.Stdout = marker
cmd.Env = env
api.SetBinFlags(cmd)
var done bool
if bh.gracefulTimeout > 0 {
graceCtx, graceCancel := context.WithTimeout(ctx, bh.gracefulTimeout)
defer graceCancel()
go func() {
<-graceCtx.Done()
proc := cmd.Process
if proc == nil || done {
return
}
err := proc.Signal(os.Interrupt)
if err != nil {
log.Println("failed send signal to process:", err)
} else {
log.Println("sent graceful shutdown to proces")
}
}()
}
err := cmd.Run()
done = true
if codeReset, ok := writer.(interface{ Status(status int) }); ok && err != nil {
codeReset.Status(http.StatusBadGateway)

File diff suppressed because one or more lines are too long

View File

@ -33,6 +33,7 @@ type Unit struct {
WorkDir string `yaml:"workdir,omitempty"` // working directory for the worker. if empty - temporary one will generated automatically
Command string `yaml:"command"` // command in a shell to execute
Timeout time.Duration `yaml:"timeout,omitempty"` // maximum execution timeout (enabled only for bin mode and only if positive)
GracefulTimeout time.Duration `yaml:"graceful_timeout,omitempty"` // maximum execution timeout after which SIGINT will be sent (enabled only for bin mode and only if positive)
Shell string `yaml:"shell,omitempty"` // shell to execute command in bin mode (default - /bin/sh)
Environment map[string]string `yaml:"environment,omitempty"` // custom environment for executable (in addition to system)
MaxRequest int64 `yaml:"max_request,omitempty"` // optional maximum HTTP body size (enabled if positive)
@ -246,6 +247,7 @@ func (cfg Unit) createRunner() (http.Handler, error) {
workDir: cfg.WorkDir,
shell: cfg.Shell,
timeout: cfg.Timeout,
gracefulTimeout: cfg.GracefulTimeout,
environment: append(os.Environ(), makeEnvList(cfg.Environment)...),
}, nil
case "cgi":

View File

@ -50,7 +50,8 @@
<dd class="col-sm-9">{{.Unit.Attempts}}</dd>
<dt class="col-sm-3">Interval</dt>
<dd class="col-sm-9">{{.Unit.Interval}}</dd>
<dt class="col-sm-3">Timeout</dt>
{{if eq .Unit.Mode "bin"}}
<dt class="col-sm-3" title="Timeout after which process will be sent">Timeout</dt>
<dd class="col-sm-9">
{{with .Unit.Timeout}}
{{.}}
@ -58,6 +59,16 @@
{{end}}
</dd>
<dt class="col-sm-3" title="Timeout after which SIGINT will be sent">Graceful timeout
</dt>
<dd class="col-sm-9">
{{with .Unit.GracefulTimeout}}
{{.}}
{{else}}
{{end}}
</dd>
{{end}}
<dt class="col-sm-3">Max request size</dt>
<dd class="col-sm-9">
{{with .Unit.MaxRequest}}