NoPaste

request0r.go

von paedubucher
SNIPPET_DESC:
Nebenläufige HTTP-Requests in Go absetzen
SNIPPET_CREATION_TIME:
05.12.2023 07:00:27
SNIPPET_PRUNE_TIME:
Unendlich

SNIPPET_TEXT:
  1. package main
  2.  
  3. import (
  4.         "flag"
  5.         "fmt"
  6.         "math"
  7.         "net/http"
  8.         "os"
  9.         "sort"
  10.         "sync"
  11.         "time"
  12. )
  13.  
  14. type WorkerResult struct {
  15.         OK   bool
  16.         Time time.Duration
  17. }
  18.  
  19. var Percentiles = []int{0, 25, 50, 75, 100}
  20.  
  21. type Stats struct {
  22.         Total  int
  23.         Passed int
  24.         Failed int
  25.         Mean   time.Duration
  26.         Percs  map[int]time.Duration
  27. }
  28.  
  29. func main() {
  30.         workers := flag.Int("w", 1, "number of concurrent workers")
  31.         requests := flag.Int("r", 1, "number of requests per worker")
  32.         state := flag.Int("s", http.StatusOK, "response state considered success")
  33.         flag.Parse()
  34.  
  35.         if len(flag.Args()) < 1 {
  36.                 fmt.Fprintln(os.Stderr, "missing URL")
  37.                 os.Exit(1)
  38.         }
  39.         if *workers < 1 {
  40.                 fmt.Fprintln(os.Stderr, "must use at least one worker")
  41.                 os.Exit(1)
  42.         }
  43.         if *requests < 1 {
  44.                 fmt.Fprintln(os.Stderr, "must perform at least one request")
  45.                 os.Exit(1)
  46.         }
  47.  
  48.         results := run(flag.Args()[0], *workers, *requests, *state)
  49.  
  50.         s := stats(results)
  51.         fmt.Println("Requests:")
  52.         fmt.Printf("%15s %15s %15s %15s\n", "Total", "Passed", "Failed", "Mean")
  53.         fmt.Printf("%15d %15d %15d %15s\n", s.Total, s.Passed, s.Failed, s.Mean)
  54.         fmt.Println("Percentiles:")
  55.         for _, k := range Percentiles {
  56.                 fmt.Printf("%14d%% ", k)
  57.         }
  58.         fmt.Println()
  59.         for _, k := range Percentiles {
  60.                 fmt.Printf("%15s ", s.Percs[k])
  61.         }
  62.         fmt.Println()
  63. }
  64.  
  65. func run(url string, workers, requests, okState int) []WorkerResult {
  66.         collector := make(chan []WorkerResult)
  67.         results := make(chan WorkerResult)
  68.  
  69.         go collect(collector, results)
  70.  
  71.         var wg sync.WaitGroup
  72.         for w := 0; w < workers; w++ {
  73.                 wg.Add(requests)
  74.                 go func(ch chan WorkerResult, id int) {
  75.                         for r := 0; r < requests; r++ {
  76.                                 ch <- get(url, okState, w)
  77.                                 wg.Done()
  78.                         }
  79.                 }(results, w)
  80.         }
  81.         wg.Wait()
  82.         close(results)
  83.  
  84.         return <-collector
  85. }
  86.  
  87. func collect(whole chan<- []WorkerResult, parts <-chan WorkerResult) {
  88.         overall := make([]WorkerResult, 0)
  89.         for result := range parts {
  90.                 overall = append(overall, result)
  91.         }
  92.         whole <- overall
  93. }
  94.  
  95. func get(url string, okState, workerID int) WorkerResult {
  96.         req, err := http.NewRequest(http.MethodGet, url, nil)
  97.         if err != nil {
  98.                 fmt.Fprintf(os.Stderr, "create request for %s: %v", url, err)
  99.                 return WorkerResult{false, 0.0}
  100.         }
  101.  
  102.         start := time.Now()
  103.         res, err := http.DefaultClient.Do(req)
  104.         if err != nil {
  105.                 fmt.Fprintf(os.Stderr, "perform request for %s: %v", url, err)
  106.                 return WorkerResult{false, time.Since(start)}
  107.         }
  108.         defer res.Body.Close()
  109.  
  110.         return WorkerResult{res.StatusCode == okState, time.Since(start)}
  111. }
  112.  
  113. func stats(results []WorkerResult) Stats {
  114.         var total time.Duration
  115.         var stats Stats
  116.         var durations []time.Duration
  117.         var ok int
  118.  
  119.         for _, r := range results {
  120.                 if r.OK {
  121.                         durations = append(durations, r.Time)
  122.                         total += r.Time
  123.                         ok++
  124.                 }
  125.         }
  126.  
  127.         stats.Total = len(results)
  128.         stats.Passed = ok
  129.         stats.Failed = stats.Total - stats.Passed
  130.         if ok > 0 {
  131.                 stats.Mean = total / time.Duration(ok)
  132.                 sort.Slice(durations, func(l, r int) bool {
  133.                         return durations[l] < durations[r]
  134.                 })
  135.                 n := len(durations)
  136.                 stats.Percs = make(map[int]time.Duration)
  137.                 for _, p := range Percentiles {
  138.                         if p == 0 {
  139.                                 stats.Percs[p] = durations[0]
  140.                         } else {
  141.                                 ratio := float64(p) / 100.0
  142.                                 index := int(math.Round(float64(n-1) * ratio))
  143.                                 stats.Percs[p] = durations[index]
  144.                         }
  145.                 }
  146.         }
  147.  
  148.         return stats
  149. }

Quellcode

Hier kannst du den Code kopieren und ihn in deinen bevorzugten Editor einfügen. PASTEBIN_DOWNLOAD_SNIPPET_EXPLAIN