more things

This commit is contained in:
cubixle
2021-07-27 21:09:29 +01:00
parent 2ddeeea267
commit cb8d79fca3
7 changed files with 196 additions and 46 deletions
+4
View File
@@ -0,0 +1,4 @@
# L1
L1 is a load testing framework written in Go for Go.
+7 -8
View File
@@ -8,16 +8,15 @@ import (
func main() { func main() {
r, err := l1.NewRunner( r, err := l1.NewRunner(
l1.WithTarget("http://google.com"), l1.WithTarget("https://remoteukjobs.com"),
l1.WithRunFunc(func(target string) error { l1.WithRunFunc(l1.DefaultHTTPTester),
return nil l1.WithMaxParrellConns(10),
}), l1.WithMaxConns(6000),
) )
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
err = r.Start() r.Execute()
if err != nil { results := r.Results()
log.Fatal(err) results.Print()
}
} }
+29
View File
@@ -0,0 +1,29 @@
package l1
import (
"net/http"
"time"
)
func DefaultHTTPTester(target string) *Result {
result := &Result{}
client := http.Client{
Timeout: 30 * time.Second,
}
req, err := http.NewRequest(http.MethodGet, target, nil)
if err != nil {
result.Error = err
return result
}
startTime := time.Now()
rsp, err := client.Do(req)
if err != nil {
result.Error = err
}
result.CompletedIn = time.Since(startTime).Seconds()
result.StatusCode = rsp.StatusCode
return result
}
+24 -1
View File
@@ -5,7 +5,7 @@ import (
) )
// F defines the function type for runners. // F defines the function type for runners.
type F func(target string) error type F func(target string) *Result
// Runner // Runner
type Runner struct { type Runner struct {
@@ -15,6 +15,7 @@ type Runner struct {
RunTime time.Duration RunTime time.Duration
RunFunc F RunFunc F
Target string Target string
results []*Result
} }
func NewRunner(opts ...Opt) (*Runner, error) { func NewRunner(opts ...Opt) (*Runner, error) {
@@ -39,3 +40,25 @@ func NewRunner(opts ...Opt) (*Runner, error) {
func (r *Runner) SetOpt(o Opt) { func (r *Runner) SetOpt(o Opt) {
o(r) o(r)
} }
func (r *Runner) Execute() {
tasks := []*Task{}
for i := 0; i < r.MaxConnections; i++ {
tasks = append(tasks, &Task{Target: r.Target, F: r.RunFunc})
}
// create the pool and process the tasks.
pool := newPool(tasks, r.MaxParrellConnections)
// the tasks are updated in memory so we don't expect a return here.
pool.run()
for _, t := range tasks {
r.results = append(r.results, t.Result)
}
}
func (r *Runner) Results() *results {
res := &results{
Results: r.results,
Target: r.Target,
}
return res
}
+45 -12
View File
@@ -1,21 +1,54 @@
package l1 package l1
import ( import (
"fmt" "sync"
) )
// Start starts the runner. type Task struct {
func (r *Runner) Start() error { Target string
jobChan := make(chan string, r.MaxParrellConnections) Result *Result
// resultsChan := make(chan struct{}) F F
}
for i := 0; i < r.MaxParrellConnections; i++ { type pool struct {
go func(jobChan chan string) { tasks []*Task
for t := range jobChan { concurrency int
r.RunFunc(t)
} tasksChan chan *Task
}(jobChan) wg sync.WaitGroup
}
func newPool(tasks []*Task, concurrency int) *pool {
return &pool{
tasks: tasks,
concurrency: concurrency,
tasksChan: make(chan *Task),
}
}
// run will run all work within the pool.
func (p *pool) run() {
for i := 0; i < p.concurrency; i++ {
go p.work()
} }
return fmt.Errorf("unimplemented") p.wg.Add(len(p.tasks))
for _, t := range p.tasks {
p.tasksChan <- t
}
close(p.tasksChan)
p.wg.Wait()
}
func (p *pool) work() {
for t := range p.tasksChan {
if t.F == nil {
continue
}
res := t.F(t.Target)
t.Result = res
p.wg.Done()
}
} }
+87 -9
View File
@@ -1,26 +1,104 @@
package l1 package l1
type Results struct { import "fmt"
type results struct {
Target string Target string
Count int Results []*Result
Results []Result
totalCompletedTime float64
successfulCount int
errorCount int
} }
type Result struct { type Result struct {
// CompletedIn is in seconds // CompletedIn is in seconds
CompletedIn int RunTime int64
CompletedIn float64
Error error Error error
StatusCode int StatusCode int
} }
func (r *Results) RequestsPerMin() int { func (r *results) RequestPerSecond() float64 {
totalCompletedIn := 0 totalCompletedIn := float64(0)
for _, res := range r.Results { for _, res := range r.Results {
if res.Error != nil {
continue
}
totalCompletedIn += res.CompletedIn totalCompletedIn += res.CompletedIn
} }
return totalCompletedIn / r.Count
return float64(len(r.Results)) / totalCompletedIn
} }
func (r *Results) AvgCompletionTime() int { func (r *results) PeakResponseTime() {
return 0
}
func (r *results) ErrorCount() int {
if r.errorCount > 0 {
return r.errorCount
}
for _, res := range r.Results {
if res.Error == nil {
continue
}
r.errorCount++
}
return r.errorCount
}
func (r *results) SuccessfulCount() int {
if r.successfulCount > 0 {
return r.successfulCount
}
for _, res := range r.Results {
if res.Error != nil {
continue
}
r.successfulCount++
}
return r.successfulCount
}
func (r *results) AvgResponseTime() float64 {
totalCompletedIn := float64(0)
for _, res := range r.Results {
if res.Error != nil {
continue
}
totalCompletedIn += res.CompletedIn
}
return totalCompletedIn / float64(len(r.Results))
}
func (r *results) CompletedTime() float64 {
if r.totalCompletedTime > 0 {
return r.totalCompletedTime
}
for _, res := range r.Results {
if res.Error != nil {
continue
}
r.totalCompletedTime += res.CompletedIn
}
return r.totalCompletedTime
}
func (r *results) Print() {
fmt.Println("-----------------------------------------------------------")
fmt.Println("| L1 load tester.")
fmt.Println("| Default result printer.")
fmt.Println("-----------------------------------------------------------")
fmt.Println("")
fmt.Printf("Load testing %s\n", r.Target)
fmt.Println("")
fmt.Printf("Request per second: %.2f\n", r.RequestPerSecond())
fmt.Printf("Average response time: %.2f seconds\n", r.AvgResponseTime())
fmt.Printf("Success count: %d\n", r.SuccessfulCount())
fmt.Printf("Error count: %d\n", r.ErrorCount())
fmt.Println("")
} }
-16
View File
@@ -1,16 +0,0 @@
package l1_test
// func TestRequestsPerMin(t *testing.T) {
// rs := l1.Results{
// Count: 4,
// Results: []l1.Result{
// {CompletedIn: 10},
// {CompletedIn: 10},
// {CompletedIn: 10},
// {CompletedIn: 10},
// },
// }
// rpm := rs.RequestsPerMin()
// log.Println(rpm)
// }