5
5
"log"
6
6
"math"
7
7
"os"
8
+ "os/signal"
8
9
"sync"
10
+ "syscall"
9
11
"time"
10
12
)
11
13
@@ -32,6 +34,8 @@ var statsMap = map[string]func() Stats{
32
34
"dns" : newDNSStats ,
33
35
}
34
36
37
+ var exitChan = make (chan struct {})
38
+
35
39
// NewTrafficGenerator will return a new TrafficGenerator object
36
40
func NewTrafficGenerator (trafficType string ) (* TrafficGenerator , error ) {
37
41
tFunc , ok := trafficMap [trafficType ]
@@ -50,11 +54,53 @@ func NewTrafficGenerator(trafficType string) (*TrafficGenerator, error) {
50
54
51
55
// Generate generates traffic
52
56
func (trafficGen * TrafficGenerator ) Generate () {
57
+ // Create a channel that will listen to SIGINT / SIGTERM
58
+ c := make (chan os.Signal , 1 )
59
+ signal .Notify (c , syscall .SIGINT )
60
+ signal .Notify (c , syscall .SIGTERM )
61
+
53
62
for i := 1 ; i <= nbOfClients ; i ++ {
54
63
trafficGen .wg .Add (1 )
64
+ // Launch the workers in a go routine
55
65
go trafficGen .NewWorker (i ).work ()
56
66
}
57
- trafficGen .wg .Wait ()
67
+
68
+ // Done channel to stop the loop when all the workers are done
69
+ var done = make (chan struct {})
70
+ go func () {
71
+ // Wait for the workerss
72
+ trafficGen .wg .Wait ()
73
+ // All the workers are done
74
+ done <- struct {}{}
75
+ }()
76
+
77
+ // Wait for the workers to end
78
+ // or a signal in the loop
79
+ var forceShutdown bool
80
+ for {
81
+ select {
82
+ case <- done :
83
+ // All the workers are done, we quit
84
+ return
85
+ case sig := <- c :
86
+ // We listen for signals
87
+ switch sig {
88
+ case syscall .SIGINT , syscall .SIGKILL :
89
+ // If it's the second time we get a signal, quit
90
+ if forceShutdown {
91
+ os .Exit (1 )
92
+ }
93
+
94
+ // Notify all the workers that they need to stop
95
+ for i := 1 ; i <= nbOfClients ; i ++ {
96
+ exitChan <- struct {}{}
97
+ }
98
+
99
+ // Next time we get a signal, need to quit directly
100
+ forceShutdown = true
101
+ }
102
+ }
103
+ }
58
104
}
59
105
60
106
// NewWorker creates a new worker for traffic generation
@@ -71,16 +117,40 @@ func (trafficGen *TrafficGenerator) DisplayStats() {
71
117
}
72
118
73
119
func (w * Worker ) work () {
120
+ var exit bool
74
121
defer w .trafficGen .wg .Done ()
75
122
start := time .Now ()
76
123
124
+ var done = make (chan struct {})
125
+ // When the work is done, notify the watching go routine
126
+ defer func () { done <- struct {}{} }()
127
+
128
+ // Create the watching go routine that will watch if we need to quit early
129
+ go func () {
130
+ for {
131
+ select {
132
+ // If we need to exit
133
+ case <- exitChan :
134
+ exit = true
135
+ // If everything is done
136
+ case <- done :
137
+ return
138
+ }
139
+ }
140
+ }()
141
+
77
142
workerFmt := fmt .Sprintf ("worker#%%0%dd" , getPadding (nbOfClients ))
78
143
counterFmt := fmt .Sprintf (" - %%0%dd/%%d " , getPadding (nbOfRequests ))
79
144
80
145
prefix := fmt .Sprintf (workerFmt , w .id )
81
146
logger := log .New (os .Stdout , prefix , 0 )
147
+
82
148
// Repeat nbOfRequests requests
83
149
for i := 1 ; i <= nbOfRequests ; i ++ {
150
+ // If we got an exit signal, quit
151
+ if exit {
152
+ return
153
+ }
84
154
logger .SetPrefix (prefix + fmt .Sprintf (counterFmt , i , nbOfRequests ))
85
155
// Find an URL
86
156
url := findRandomURL ()
@@ -101,3 +171,10 @@ func getPadding(nb int) int {
101
171
// Get the padding size : floor(log10(nb)) + 1
102
172
return int (math .Log10 (float64 (nb ))) + 1
103
173
}
174
+
175
+ func getAvgDuration (total time.Duration , number int ) string {
176
+ if number == 0 {
177
+ return "NaN"
178
+ }
179
+ return (total / time .Duration (number )).String ()
180
+ }
0 commit comments