mirror of
https://github.com/cubixle/l1.git
synced 2026-04-30 15:18:38 +01:00
more things
This commit is contained in:
@@ -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()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
// 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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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++ {
|
|
||||||
go func(jobChan chan string) {
|
|
||||||
for t := range jobChan {
|
|
||||||
r.RunFunc(t)
|
|
||||||
}
|
|
||||||
}(jobChan)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
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("")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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