package loic import ( "context" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/metric" "log" "net/http" "sync" "time" ) var meter = otel.Meter("loic") var requestsSent metric.Int64Counter var currentTest *Test var isRunning bool func init() { var err error requestsSent, err = meter.Int64Counter("requests_sent", metric.WithDescription("Number of requests sent"), metric.WithUnit("1"), ) if err != nil { log.Fatalf("failed to create counter: %v", err) } } type Test struct { TargetURL string Concurrency int Duration time.Duration RampUpTime time.Duration ctx context.Context cancel context.CancelFunc wg sync.WaitGroup } func StartTest(targetURL string, concurrency int, duration time.Duration, rampUp time.Duration) { ctx, cancel := context.WithCancel(context.Background()) currentTest := &Test{ TargetURL: targetURL, Concurrency: concurrency, Duration: duration, RampUpTime: rampUp, ctx: ctx, cancel: cancel, } go currentTest.run() } func StopTest() { if currentTest != nil && isRunning { currentTest.cancel() currentTest.wg.Wait() currentTest = nil isRunning = false log.Println("Test stopped") } } func IsTestRunning() bool { return isRunning } func (t *Test) run() { defer func() { isRunning = false }() ticker := time.NewTicker(t.RampUpTime / time.Duration(t.Concurrency)) defer ticker.Stop() currentConcurrency := 1 for i := 0; i < t.Concurrency; i++ { select { case <-ticker.C: if currentConcurrency < t.Concurrency { currentConcurrency++ t.startWorker() } case <-time.After(t.Duration): t.cancel() case <-t.ctx.Done(): return } } t.wg.Wait() } func (t *Test) startWorker() { t.wg.Add(1) go func() { defer t.wg.Done() for { select { case <-t.ctx.Done(): return default: resp, err := http.Get(t.TargetURL) if err != nil { log.Println("Error:", err) } else { resp.Body.Close() requestsSent.Add(t.ctx, 1) } time.Sleep(time.Second / time.Duration(t.Concurrency)) } } }() }