mirror of
https://github.com/cubixle/l1.git
synced 2026-04-30 16:58:45 +01:00
more things
This commit is contained in:
@@ -8,16 +8,15 @@ import (
|
||||
|
||||
func main() {
|
||||
r, err := l1.NewRunner(
|
||||
l1.WithTarget("http://google.com"),
|
||||
l1.WithRunFunc(func(target string) error {
|
||||
return nil
|
||||
}),
|
||||
l1.WithTarget("https://remoteukjobs.com"),
|
||||
l1.WithRunFunc(l1.DefaultHTTPTester),
|
||||
l1.WithMaxParrellConns(10),
|
||||
l1.WithMaxConns(6000),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = r.Start()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
r.Execute()
|
||||
results := r.Results()
|
||||
results.Print()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
)
|
||||
|
||||
// F defines the function type for runners.
|
||||
type F func(target string) error
|
||||
type F func(target string) *Result
|
||||
|
||||
// Runner
|
||||
type Runner struct {
|
||||
@@ -15,6 +15,7 @@ type Runner struct {
|
||||
RunTime time.Duration
|
||||
RunFunc F
|
||||
Target string
|
||||
results []*Result
|
||||
}
|
||||
|
||||
func NewRunner(opts ...Opt) (*Runner, error) {
|
||||
@@ -39,3 +40,25 @@ func NewRunner(opts ...Opt) (*Runner, error) {
|
||||
func (r *Runner) SetOpt(o Opt) {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,21 +1,54 @@
|
||||
package l1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Start starts the runner.
|
||||
func (r *Runner) Start() error {
|
||||
jobChan := make(chan string, r.MaxParrellConnections)
|
||||
// resultsChan := make(chan struct{})
|
||||
|
||||
for i := 0; i < r.MaxParrellConnections; i++ {
|
||||
go func(jobChan chan string) {
|
||||
for t := range jobChan {
|
||||
r.RunFunc(t)
|
||||
}
|
||||
}(jobChan)
|
||||
type Task struct {
|
||||
Target string
|
||||
Result *Result
|
||||
F F
|
||||
}
|
||||
|
||||
return fmt.Errorf("unimplemented")
|
||||
type pool struct {
|
||||
tasks []*Task
|
||||
concurrency int
|
||||
|
||||
tasksChan chan *Task
|
||||
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()
|
||||
}
|
||||
|
||||
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
@@ -1,26 +1,104 @@
|
||||
package l1
|
||||
|
||||
type Results struct {
|
||||
import "fmt"
|
||||
|
||||
type results struct {
|
||||
Target string
|
||||
Count int
|
||||
Results []Result
|
||||
Results []*Result
|
||||
|
||||
totalCompletedTime float64
|
||||
successfulCount int
|
||||
errorCount int
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
// CompletedIn is in seconds
|
||||
CompletedIn int
|
||||
RunTime int64
|
||||
CompletedIn float64
|
||||
Error error
|
||||
StatusCode int
|
||||
}
|
||||
|
||||
func (r *Results) RequestsPerMin() int {
|
||||
totalCompletedIn := 0
|
||||
func (r *results) RequestPerSecond() float64 {
|
||||
totalCompletedIn := float64(0)
|
||||
for _, res := range r.Results {
|
||||
if res.Error != nil {
|
||||
continue
|
||||
}
|
||||
totalCompletedIn += res.CompletedIn
|
||||
}
|
||||
return totalCompletedIn / r.Count
|
||||
|
||||
return float64(len(r.Results)) / totalCompletedIn
|
||||
}
|
||||
|
||||
func (r *Results) AvgCompletionTime() int {
|
||||
return 0
|
||||
func (r *results) PeakResponseTime() {
|
||||
|
||||
}
|
||||
|
||||
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("")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
// }
|
||||
Reference in New Issue
Block a user