Skip to content

Commit f1b5c22

Browse files
authored
Merge pull request #18 from PouuleT/asyncSignal
Async signal
2 parents 710e263 + 61db116 commit f1b5c22

File tree

2 files changed

+84
-7
lines changed

2 files changed

+84
-7
lines changed

http_stats.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func (s *HTTPStats) Render() {
8989
strconv.Itoa(s.nbOfRequests),
9090
s.minDuration.String(),
9191
s.maxDuration.String(),
92-
(s.totalDuration / time.Duration(s.nbOfRequests)).String(),
92+
getAvgDuration(s.totalDuration, s.nbOfRequests),
9393
s.execDuration.String(),
9494
fmt.Sprintf("%s/s", humanize.Bytes(uint64(float64(time.Duration(s.totalSize))/float64(s.execDuration)*math.Pow10(9)))),
9595
fmt.Sprintf("%s", humanize.Bytes(uint64(s.totalSize))),
@@ -112,19 +112,19 @@ func (s *HTTPStats) Render() {
112112
timeTable.SetHeader([]string{"Step", "Average duration"})
113113
timeTable.SetAlignment(tablewriter.ALIGN_CENTER)
114114
timeTable.Append([]string{
115-
"DNSLookup", (s.responseTimeline.DNSLookup / time.Duration(s.successRequests)).String(),
115+
"DNSLookup", getAvgDuration(s.responseTimeline.DNSLookup, s.successRequests),
116116
})
117117
timeTable.Append([]string{
118-
"TCPConnection", (s.responseTimeline.TCPConnection / time.Duration(s.successRequests)).String(),
118+
"TCPConnection", getAvgDuration(s.responseTimeline.TCPConnection, s.successRequests),
119119
})
120120
timeTable.Append([]string{
121-
"EstablishingConnection", (s.responseTimeline.EstablishingConnection / time.Duration(s.successRequests)).String(),
121+
"EstablishingConnection", getAvgDuration(s.responseTimeline.EstablishingConnection, s.successRequests),
122122
})
123123
timeTable.Append([]string{
124-
"ServerProcessing", (s.responseTimeline.ServerProcessing / time.Duration(s.successRequests)).String(),
124+
"ServerProcessing", getAvgDuration(s.responseTimeline.ServerProcessing, s.successRequests),
125125
})
126126
timeTable.Append([]string{
127-
"ContentTransfer", (s.responseTimeline.ContentTransfer / time.Duration(s.successRequests)).String(),
127+
"ContentTransfer", getAvgDuration(s.responseTimeline.ContentTransfer, s.successRequests),
128128
})
129129

130130
fmt.Printf("\nRequest details :\n")

traffic_generator.go

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import (
55
"log"
66
"math"
77
"os"
8+
"os/signal"
89
"sync"
10+
"syscall"
911
"time"
1012
)
1113

@@ -32,6 +34,8 @@ var statsMap = map[string]func() Stats{
3234
"dns": newDNSStats,
3335
}
3436

37+
var exitChan = make(chan struct{})
38+
3539
// NewTrafficGenerator will return a new TrafficGenerator object
3640
func NewTrafficGenerator(trafficType string) (*TrafficGenerator, error) {
3741
tFunc, ok := trafficMap[trafficType]
@@ -50,11 +54,53 @@ func NewTrafficGenerator(trafficType string) (*TrafficGenerator, error) {
5054

5155
// Generate generates traffic
5256
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+
5362
for i := 1; i <= nbOfClients; i++ {
5463
trafficGen.wg.Add(1)
64+
// Launch the workers in a go routine
5565
go trafficGen.NewWorker(i).work()
5666
}
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+
}
58104
}
59105

60106
// NewWorker creates a new worker for traffic generation
@@ -71,16 +117,40 @@ func (trafficGen *TrafficGenerator) DisplayStats() {
71117
}
72118

73119
func (w *Worker) work() {
120+
var exit bool
74121
defer w.trafficGen.wg.Done()
75122
start := time.Now()
76123

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+
77142
workerFmt := fmt.Sprintf("worker#%%0%dd", getPadding(nbOfClients))
78143
counterFmt := fmt.Sprintf(" - %%0%dd/%%d ", getPadding(nbOfRequests))
79144

80145
prefix := fmt.Sprintf(workerFmt, w.id)
81146
logger := log.New(os.Stdout, prefix, 0)
147+
82148
// Repeat nbOfRequests requests
83149
for i := 1; i <= nbOfRequests; i++ {
150+
// If we got an exit signal, quit
151+
if exit {
152+
return
153+
}
84154
logger.SetPrefix(prefix + fmt.Sprintf(counterFmt, i, nbOfRequests))
85155
// Find an URL
86156
url := findRandomURL()
@@ -101,3 +171,10 @@ func getPadding(nb int) int {
101171
// Get the padding size : floor(log10(nb)) + 1
102172
return int(math.Log10(float64(nb))) + 1
103173
}
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

Comments
 (0)