From db20f44b0d100ad042c2f86b8baf394665d8ec63 Mon Sep 17 00:00:00 2001 From: Leah Neukirchen Date: Sun, 12 Apr 2020 18:28:11 +0200 Subject: allow parallel pinging of multiple urls --- README | 4 +-- htping.1 | 4 +-- htping.go | 120 +++++++++++++++++++++++++++++++++++--------------------------- 3 files changed, 71 insertions(+), 57 deletions(-) diff --git a/README b/README index 1b710c9..c56f7fe 100644 --- a/README +++ b/README @@ -5,10 +5,10 @@ NAME SYNOPSIS htping [-4] [-6] [-H field:value] [-X method] [-c count] [-i interval] - [-f] [-k] [--http1.1] [--keepalive] [-l addr] [-q] host + [-f] [-k] [--http1.1] [--keepalive] [-l addr] [-q] urls ... DESCRIPTION - The htping utility periodically sends HTTP requests to host, prints the + The htping utility periodically sends HTTP requests to urls, prints the results and computes some statistics at exit. Use Ctrl-C to quit htping. The options are as follows: diff --git a/htping.1 b/htping.1 index 5ca4582..2fec57e 100644 --- a/htping.1 +++ b/htping.1 @@ -18,12 +18,12 @@ .Op Fl -keepalive .Op Fl l Ar addr .Op Fl q -.Ar host +.Ar urls ... .Sh DESCRIPTION The .Nm utility periodically sends HTTP requests to -.Ar host , +.Ar urls , prints the results and computes some statistics at exit. Use Ctrl-C to quit .Nm . diff --git a/htping.go b/htping.go index 600ad50..5010c39 100644 --- a/htping.go +++ b/htping.go @@ -25,6 +25,7 @@ import ( "os/signal" "strconv" "strings" + "sync" "sync/atomic" "time" @@ -101,8 +102,6 @@ type transport struct { addr string } -var myTransport *transport - func newTransport() *transport { tr := &transport{} @@ -174,7 +173,7 @@ type result struct { code int } -func ping(url string, seq int, results chan result) { +func ping(url string, seq int, myTransport *transport, results chan result) { start := time.Now() requestCounter.WithLabelValues(url).Inc() @@ -260,13 +259,15 @@ func stats(results chan result, done chan bool) { case <-done: stop := time.Now() - fmt.Printf("\n%d requests sent, %d (%d%%) responses, %d (%d%%) successful, time %dms\n", - ntotal, - nrecv, - (100*nrecv)/int(ntotal), - nsucc, - (100*nsucc)/int(ntotal), - int64(stop.Sub(start)/time.Millisecond)) + if ntotal > 0 { + fmt.Printf("\n%d requests sent, %d (%d%%) responses, %d (%d%%) successful, time %dms\n", + ntotal, + nrecv, + (100*nrecv)/int(ntotal), + nsucc, + (100*nsucc)/int(ntotal), + int64(stop.Sub(start)/time.Millisecond)) + } if nrecv > 0 { mdev := math.Sqrt(sum2/float64(nrecv) - sum/float64(nrecv)*sum/float64(nrecv)) @@ -323,24 +324,10 @@ func main() { flag.Parse() args := flag.Args() - if len(args) != 1 { + if len(args) < 1 { flag.Usage() os.Exit(2) } - u := args[0] - - u2, err := url.ParseRequestURI(u) - if (err != nil && strings.HasSuffix(err.Error(), - "invalid URI for request")) || - (u2.Scheme != "http" && u2.Scheme != "https") { - u = "http://" + u - } - - _, err = url.ParseRequestURI(u) - if err != nil { - fmt.Fprintf(os.Stderr, "%s\n", err.Error()) - os.Exit(1) - } if *listenAddr != "" { prometheus.MustRegister(requestCounter) @@ -369,8 +356,6 @@ func main() { }() } - fmt.Printf("%s %s\n", method, u) - interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt) @@ -380,36 +365,65 @@ func main() { count := 0 - myTransport = newTransport() + var wg sync.WaitGroup + wg.Add(len(args)) - if *flood { - flood_loop: - for { - select { - default: - ping(u, count, results) - count++ - case <-interrupt: - break flood_loop - } + for _, mu := range args { + u := mu + + u2, err := url.ParseRequestURI(u) + if (err != nil && strings.HasSuffix(err.Error(), + "invalid URI for request")) || + (u2.Scheme != "http" && u2.Scheme != "https") { + u = "http://" + u } - } else { - pingTicker := time.NewTicker(*sleep) - go ping(u, count, results) - count++ - ping_loop: - for { - if *maxCount > 0 && count > *maxCount { - break - } - select { - case <-pingTicker.C: - go ping(u, count, results) + + _, err = url.ParseRequestURI(u) + if err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err.Error()) + os.Exit(1) + } + + fmt.Printf("%s %s\n", method, u) + + go func() { + myTransport := newTransport() + defer wg.Done() + + if *flood { + for { + select { + default: + ping(u, count, myTransport, results) + count++ + } + } + } else { + pingTicker := time.NewTicker(*sleep) + go ping(u, count, myTransport, results) count++ - case <-interrupt: - break ping_loop + for { + if *maxCount > 0 && count > *maxCount { + break + } + select { + case <-pingTicker.C: + go ping(u, count, myTransport, results) + count++ + } + } } - } + }() + } + + waitCh := make(chan struct{}) + go func() { + wg.Wait() + }() + + select { + case <-waitCh: + case <-interrupt: } done <- true -- cgit 1.4.1