Skip to content

Commit a05c78e

Browse files
Dean KarnDean Karn
authored andcommitted
Update Docs + README, Adde more examples
- updates docs, cleanup up a bit of code. - added more examples
1 parent dc02b62 commit a05c78e

File tree

5 files changed

+165
-93
lines changed

5 files changed

+165
-93
lines changed

README.md

Lines changed: 18 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
##KMS
22

3-
![Project status](https://img.shields.io/badge/version-1.1.0-green.svg)
3+
![Project status](https://img.shields.io/badge/version-1.2.0-green.svg)
44
[![Build Status](https://semaphoreci.com/api/v1/joeybloggs/kms/branches/master/badge.svg)](https://semaphoreci.com/joeybloggs/kms)
55
[![Coverage Status](https://coveralls.io/repos/github/go-playground/kms/badge.svg)](https://coveralls.io/github/go-playground/kms)
66
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/kms)](https://goreportcard.com/report/github.com/go-playground/kms)
@@ -15,38 +15,16 @@ Sure there are other libraries, but they are focused on handling graceful shutdo
1515
code such as graceful shutdown of an http server, but this library allows you to glue together multiple
1616
unrelated(or related) services running within the same process/application.
1717

18-
eg.
18+
Doesn't Go 1.8 handle graceful shutdown?
19+
------------
20+
Yes and No, it does not yet handle Hijacked connections, like WebSockets, see:
21+
- https://github.com/golang/go/issues/4674#issuecomment-257549871
22+
- https://github.com/golang/go/issues/17721#issuecomment-257572027
1923

20-
```go
21-
22-
// start CRON service running tasks..
23-
go startCRON()
24-
25-
// listen for shutdown signal(s), non-blocking
26-
kms.Listen(false)
27-
28-
// serve http site ( using built in http listener)
29-
// this site also has WebSocket functionality
30-
kmshttp.ListenAndServe(":3007", nil)
24+
but this package does; furthermore this package isn't just for graceful http shutdown but graceful shutdown of anything and everything.
3125

32-
```
33-
34-
This library will allow you to gracefully shutdown the http server, any WebSockets and any CRON jobs that may be running using the following:
35-
36-
```go
37-
38-
kms.Wait()
39-
kms.Done()
40-
41-
// chaining is also supported eg. defer kms.Wait().Done()
42-
43-
<-kms.ShutdownInitiated()
44-
45-
and is some cases
46-
47-
<-kms.ShutdownComplete()
48-
49-
```
26+
When Go 1.8 comes out I will transparently make changes to let the std lib handle idle connections as
27+
it has lower level access to them, but continue to handle Hijacked connections.
5028

5129
Installation
5230
-----------
@@ -59,77 +37,36 @@ go get -u github.com/go-playground/kms
5937

6038
Built In
6139
--------
62-
There are a few built in graceful shutdown helpers using the kms package for TCP, Unix Sockets and HTTP graceful shutdown.
40+
There are a few built in graceful shutdown helpers using the kms package for:
41+
- TCP
42+
- Unix Sockets
43+
- HTTP(S) graceful shutdown.
6344

64-
Example
45+
Examples
6546
-------
47+
[see here](https://github.com/go-playground/kms/tree/master/examples) for more
48+
6649
```go
6750
package main
6851

6952
import (
70-
"fmt"
7153
"net/http"
7254
"time"
7355

7456
"github.com/go-playground/kms"
7557
"github.com/go-playground/kms/kmsnet/kmshttp"
7658
)
7759

78-
var (
79-
// simple variable to prove the CRON job finishes gracefully.
80-
complete bool
81-
)
82-
8360
func main() {
8461

85-
go fakeWebsocketHandler()
86-
go cronJob()
87-
88-
kms.Listen(false)
62+
// listen for shutdown signal(s) with timeout, non-blocking
63+
kms.ListenTimeout(false, time.Minute*3)
8964

9065
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
9166
w.Write([]byte("Home"))
9267
})
9368

9469
kmshttp.ListenAndServe(":3007", nil)
95-
96-
fmt.Println("CRON completed gracefully? ", complete)
97-
}
98-
99-
// crude CRON job examples
100-
func cronJob() {
101-
102-
// a long running job that fires every x minutes
103-
for {
104-
time.Sleep(time.Second * 1)
105-
kms.Wait()
106-
complete = false
107-
108-
// long running DB calls
109-
time.Sleep(time.Second * 10)
110-
111-
complete = true
112-
kms.Done()
113-
}
114-
}
115-
116-
// faking a single WebSocket, just to show how
117-
func fakeWebsocketHandler() {
118-
119-
message := make(chan []byte)
120-
121-
FOR:
122-
for {
123-
select {
124-
case <-kms.ShutdownInitiated():
125-
close(message)
126-
// close WebSocket connection here
127-
break FOR
128-
case b := <-message:
129-
fmt.Println(string(b))
130-
}
131-
}
132-
13370
}
13471
```
13572

doc.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
Pacakge kms (Killing Me Softly) is a library that aids in graceful shutdown of a process/application.
3+
4+
Example:
5+
package main
6+
7+
import (
8+
"net/http"
9+
"time"
10+
11+
"github.com/go-playground/kms"
12+
"github.com/go-playground/kms/kmsnet/kmshttp"
13+
)
14+
15+
func main() {
16+
17+
// listen for shutdown signal(s) with timeout, non-blocking
18+
kms.ListenTimeout(false, time.Minute*3)
19+
20+
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
21+
w.Write([]byte("Home"))
22+
})
23+
24+
kmshttp.ListenAndServe(":3007", nil)
25+
}
26+
*/
27+
package kms

examples/http/main.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package main
2+
3+
import (
4+
"net/http"
5+
"time"
6+
7+
"github.com/go-playground/kms"
8+
"github.com/go-playground/kms/kmsnet/kmshttp"
9+
)
10+
11+
func main() {
12+
13+
// listen for shutdown signal(s) with timeout, non-blocking
14+
kms.ListenTimeout(false, time.Minute*3)
15+
16+
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
17+
w.Write([]byte("Home"))
18+
})
19+
20+
kmshttp.ListenAndServe(":3007", nil)
21+
}

examples/rpc/main.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"net/rpc"
7+
"syscall"
8+
"time"
9+
10+
"github.com/go-playground/kms"
11+
"github.com/go-playground/kms/kmsnet"
12+
)
13+
14+
// RPCListener ...
15+
type RPCListener struct {
16+
}
17+
18+
// Hello ...
19+
func (l *RPCListener) Hello(in *string, out *string) (err error) {
20+
res := fmt.Sprintf("Hello %s", *in)
21+
*out = *(&res)
22+
return nil
23+
}
24+
25+
func main() {
26+
27+
go testAndKill()
28+
29+
inbound, err := kmsnet.NewTCPListener("tcp", ":4444")
30+
if err != nil {
31+
log.Fatal(err)
32+
}
33+
defer inbound.Close()
34+
35+
// for TLS just wrap 'inbound'
36+
// newInbound := tls.NewListener(inbound, tlsConfig)
37+
// defer newInbound.Close()
38+
//
39+
// ...
40+
// s.Accept(newInbound)
41+
// ...
42+
43+
s := rpc.NewServer()
44+
45+
err = s.Register(new(RPCListener))
46+
if err != nil {
47+
log.Fatal(err)
48+
}
49+
50+
// listen for shutdown signal(s), non-blocking with timeout
51+
kms.ListenTimeout(false, time.Minute*3)
52+
53+
FOR:
54+
for {
55+
select {
56+
case <-kms.ShutdownInitiated():
57+
break FOR
58+
default:
59+
s.Accept(inbound)
60+
}
61+
}
62+
63+
<-kms.ShutdownComplete()
64+
}
65+
66+
func testAndKill() {
67+
68+
time.Sleep(time.Second * 1)
69+
70+
var in, out string
71+
72+
in = "Joeybloggs"
73+
74+
client, err := rpc.Dial("tcp", "localhost:4444")
75+
if err != nil {
76+
log.Fatal(err)
77+
}
78+
defer client.Close()
79+
80+
err = client.Call("RPCListener.Hello", &in, &out)
81+
if err != nil {
82+
log.Fatal(err)
83+
}
84+
85+
fmt.Printf("Server returned '%s'\n", out)
86+
87+
syscall.Kill(syscall.Getpid(), syscall.SIGTERM)
88+
}

kms.go

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package kms
22

33
import (
44
"fmt"
5-
"log"
65
"os"
76
"os/signal"
87
"sync"
@@ -21,7 +20,7 @@ type killingMeSoftly struct {
2120
}
2221

2322
// SignalFn is the function type used to signal kms of a shutdown siganl.
24-
// by default this library listens for syscall.SIGINT, syscall.SIGTERMand syscall.SIGHUP
23+
// by default this library listens for syscall.SIGINT, syscall.SIGTERM and syscall.SIGHUP
2524
// os.Signal's but you can override with whatever signals or logic you wish.
2625
type SignalFn func() <-chan os.Signal
2726

@@ -138,19 +137,19 @@ func Listen(block bool) {
138137

139138
close(notify)
140139

141-
log.Printf("signal %s received, attempting soft shutdown...\n", sig)
140+
fmt.Printf("Gracefully stopping (signal: %s)... ", sig)
142141

143142
if hardShutdown.Load().(bool) {
144143
// listen for another signal, if another happens.. force shutdown
145144
go func() {
146-
sig := <-s
147-
fmt.Printf("received additional %s, hard shutdown initiated\n", sig)
145+
<-s
146+
fmt.Println("done")
148147
exit(1)
149148
}()
150149
}
151150

152151
killMeSoftly.wg.Wait()
153-
log.Println("soft shutdown complete, ending process")
152+
fmt.Println("done")
154153
close(done)
155154
}()
156155

@@ -176,17 +175,17 @@ func ListenTimeout(block bool, wait time.Duration) {
176175

177176
close(notify)
178177

179-
log.Printf("signal %s received, attempting soft shutdown for %s...\n", sig, wait)
178+
fmt.Printf("Gracefully stopping (signal: %s, timeout: %s)... ", sig, wait)
180179

181180
if hardShutdown.Load().(bool) {
182181
go func() {
183182
select {
184183

185184
case <-time.After(wait):
186-
fmt.Println("timeout reached, hard shutdown initiated")
185+
fmt.Println("timed out")
187186
exit(1)
188-
case sig := <-s:
189-
fmt.Printf("received additional %s, hard shutdown initiated\n", sig)
187+
case <-s:
188+
fmt.Println("done")
190189
exit(1)
191190
case <-done:
192191
}
@@ -197,7 +196,7 @@ func ListenTimeout(block bool, wait time.Duration) {
197196
select {
198197

199198
case <-time.After(wait):
200-
fmt.Println("timeout reached, hard shutdown initiated")
199+
fmt.Println("timed out")
201200
exit(1)
202201
case <-done:
203202
}
@@ -206,7 +205,7 @@ func ListenTimeout(block bool, wait time.Duration) {
206205
}
207206

208207
killMeSoftly.wg.Wait()
209-
log.Println("soft shutdown complete, ending process")
208+
fmt.Println("done")
210209
close(done)
211210
}()
212211

0 commit comments

Comments
 (0)