Browse Source

Initial version

master
Nicolas Massé 10 years ago
parent
commit
e2bdd5f365
  1. 2
      README.md
  2. 48
      src/itix.fr/loadsprinter/cmd/main.go
  3. 59
      src/itix.fr/loadsprinter/cmd/test.go
  4. 113
      src/itix.fr/loadsprinter/core/Controller.go
  5. 51
      src/itix.fr/loadsprinter/core/Scenario.go
  6. 48
      src/itix.fr/loadsprinter/core/Step.go
  7. 42
      src/itix.fr/loadsprinter/core/VirtualUser.go
  8. 17
      src/itix.fr/loadsprinter/steps/FailStep.go
  9. 16
      src/itix.fr/loadsprinter/steps/LogStep.go
  10. 19
      src/itix.fr/loadsprinter/steps/WaitStep.go

2
README.md

@ -1,2 +1,2 @@
# loadrunner
# loadsprinter
A Load Test tool written in Go

48
src/itix.fr/loadsprinter/cmd/main.go

@ -0,0 +1,48 @@
package main
import "fmt"
import "os"
import "time"
import "itix.fr/loadsprinter/core"
import "itix.fr/loadsprinter/steps"
type MyVirtualUserFactory struct {
count int
scenario *core.Scenario
}
func (factory *MyVirtualUserFactory) CreateVirtualUser() *core.VirtualUser {
name := fmt.Sprintf("vu-%03d", factory.count)
factory.count++
return core.NewVirtualUser(name, factory.scenario)
}
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
/*vulogwr, err := os.Create("/tmp/vu.log")
check(err)
defer vulogwr.Close()*/
csvwr, err := os.Create("/tmp/vu.csv")
check(err)
defer csvwr.Close()
s1 := steps.NewWaitStep(300 * time.Millisecond)
s2 := steps.NewLogStep("Hello World !")
s3 := steps.NewFailStep("BLAAAAAAH")
step1 := core.NewStep("wait_3s", true, s1)
step2 := core.NewStep("log_hello", true, s2)
step3 := core.NewStep("fail", false, s3)
steps := []core.Step{ *step1, *step2, *step3 }
scenario := core.NewScenario(steps, "test")
f := &MyVirtualUserFactory{0, scenario}
c := core.NewController(os.Stdout, os.Stdout, csvwr)
wg, err := c.StartWith(5, f)
wg.Wait()
}

59
src/itix.fr/loadsprinter/cmd/test.go

@ -0,0 +1,59 @@
package main
import "fmt"
import "time"
//import "sync"
type Thing struct {
a int
b string
}
func (t Thing) String() string {
return fmt.Sprintf("%v-%v", t.b, t.a)
}
func Do(a int, b string, ch chan Thing) {
var t Thing
t.a = a
t.b = b
ch <- t
}
func Doit(id string, chan1 chan Thing, chan2 chan Thing) {
for {
var t Thing
t.a = 666
t.b = fmt.Sprintf("%v-evil", id)
Do(1, fmt.Sprintf("%v-one", id), chan1)
time.Sleep(300 * time.Millisecond)
Do(2, fmt.Sprintf("%v-two", id), chan1)
Do(3, fmt.Sprintf("%v-three", id), chan1)
time.Sleep(50 * time.Millisecond)
Do(4, fmt.Sprintf("%v-four", id), chan1)
chan2 <- t
}
}
func main() {
chan1 := make(chan Thing)
chan2 := make(chan Thing)
go Doit("A", chan1, chan2);
go Doit("B", chan1, chan2);
go Doit("C", chan1, chan2);
go Doit("D", chan1, chan2);
go Doit("E", chan1, chan2);
for {
time.Sleep(1*time.Second)
select {
case x := <- chan1:
fmt.Printf("1: %v\n", x)
case x := <- chan2:
fmt.Printf("2: %v\n", x)
}
}
}

113
src/itix.fr/loadsprinter/core/Controller.go

@ -0,0 +1,113 @@
package core
import "io"
import "log"
import "fmt"
import "sync"
import "time"
import "encoding/csv"
type Controller struct {
vuserLog *log.Logger
controllerLog *log.Logger
users []*VirtualUser
wg sync.WaitGroup
csv *csv.Writer
stepStats chan StepIteration
scenarioStats chan ScenarioIteration
}
type VirtualUserFactory interface {
CreateVirtualUser() *VirtualUser
}
type StepIteration struct {
vuser string
scenario string
step string
success bool
elapsed time.Duration
}
func (s StepIteration) String() string {
return fmt.Sprintf("step: %v > %v > %v: success = %v, elapsed = %v", s.vuser, s.scenario, s.step, s.success, s.elapsed)
}
type ScenarioIteration struct {
vuser string
scenario string
success bool
elapsed time.Duration
}
func (s ScenarioIteration) String() string {
return fmt.Sprintf("scenario: %v > %v: success = %v, elapsed = %v", s.vuser, s.scenario, s.success, s.elapsed)
}
func NewController(vuserLogFile io.Writer, controllerLogFile io.Writer, csvfile io.Writer) *Controller {
c := Controller{}
c.vuserLog = log.New(vuserLogFile, "", log.Lshortfile | log.LstdFlags)
c.controllerLog = log.New(controllerLogFile, "", log.Lshortfile | log.LstdFlags)
c.csv = csv.NewWriter(csvfile)
c.stepStats = make(chan StepIteration)
c.scenarioStats = make(chan ScenarioIteration)
return &c;
}
func (c *Controller) StartWith(n int, f VirtualUserFactory) (wg *sync.WaitGroup, err error) {
c.controllerLog.Printf("--> Controller::StartWith(%v, %v)", n, f)
c.wg.Add(n) // Initialize the WaitGroup with the number of routines to create
// Create a channel that will be used to start all VirtualUsers together
start := make(chan int)
for i := 0; i < n; i++ {
log := *c.vuserLog
vu := f.CreateVirtualUser()
log.SetPrefix(fmt.Sprintf("%v: ", vu.name))
c.users = append(c.users, vu)
vu.Init(&log)
go vu.Run(&log, &c.wg, start, c.stepStats, c.scenarioStats)
c.controllerLog.Printf("Added one more VirtualUser with name = %v", vu.name)
}
// Start collecting results as soon as virtual users are started
go func() { <- start; c.GatherResults() }()
c.controllerLog.Println("Ready ? Set !")
time.Sleep(1 * time.Second)
// Start all VirtualUsers together
close(start)
c.controllerLog.Println("Go !")
c.controllerLog.Println("<-- Controller::StartWith()")
return &c.wg, nil
}
func (c *Controller) GatherResults() {
c.controllerLog.Println("--> Controller::GatherResults()")
// Gather results every second
timer := make(chan int)
go func() {
for {
time.Sleep(1 * time.Second)
timer <- 0
}
}()
for {
c.controllerLog.Println("Waiting for message")
select {
case <- timer:
c.controllerLog.Println("Time's up !")
case stepStat := <- c.stepStats:
c.controllerLog.Println(stepStat)
case scenarioStat := <- c.scenarioStats:
c.controllerLog.Println(scenarioStat)
}
c.controllerLog.Println("Received a message")
}
c.controllerLog.Println("<-- Controller::GatherResults()")
}

51
src/itix.fr/loadsprinter/core/Scenario.go

@ -0,0 +1,51 @@
package core
import "log"
import "time"
type Scenario struct {
steps []Step
name string
}
func NewScenario(steps []Step, name string) *Scenario{
return &Scenario{ steps: steps, name: name };
}
func (s *Scenario) Do(log *log.Logger, vuser *VirtualUser, stepsToController chan<- StepIteration, scenarioToController chan<- ScenarioIteration) error {
log.Println("--> Scenario::Do()")
// Prepare the statistics structure
var stat ScenarioIteration
stat.success = true;
stat.vuser = vuser.name
stat.scenario = s.name
// Measure the beginning of the scenario
start := time.Now()
// Run the scenario
var err error = nil
for _, step := range s.steps {
err = step.Do(log, vuser, s, stepsToController)
if (err != nil) {
if (step.required) {
log.Printf("Mandatory step %v ended with error %v, stopping scenario !", step.name, err)
stat.success = false;
break
} else {
log.Printf("Optional step %v ended with error %v, continuing...", step.name, err)
}
}
}
// Compute the elapsed time since the beginning of this scenario
elapsed := time.Since(start)
stat.elapsed = elapsed
// Send it to the controller
scenarioToController <- stat
log.Printf("<-- Scenario::Do() : err = %v", err)
return err
}

48
src/itix.fr/loadsprinter/core/Step.go

@ -0,0 +1,48 @@
package core
import "log"
import "time"
type StepImpl interface {
Do(log *log.Logger) error
}
type Step struct {
name string
required bool
impl StepImpl
}
func NewStep(name string, required bool, impl StepImpl) *Step {
return &Step{ name: name, required: required, impl: impl }
}
func (s *Step) Do(log *log.Logger, vuser *VirtualUser, scenario *Scenario, stepsToController chan<- StepIteration) error {
log.Printf("--> Step::Do(%v)", s.name)
// Prepare the statistics structure
var stat StepIteration
stat.success = true
stat.vuser = vuser.name
stat.scenario = scenario.name
stat.step = s.name
// Measure the beginning of this step
start := time.Now()
err := s.impl.Do(log)
if (err != nil) {
log.Printf("Step %v ended with error %v", s.name, err)
stat.success = false;
}
// Compute the elapsed time since the beginning of this step
elapsed := time.Since(start)
stat.elapsed = elapsed
// Send it to the controller
stepsToController <- stat
log.Printf("<-- Step::Do(%v)", s.name)
return err;
}

42
src/itix.fr/loadsprinter/core/VirtualUser.go

@ -0,0 +1,42 @@
package core;
import "log"
import "sync"
type VirtualUser struct {
name string
scenario *Scenario
}
func NewVirtualUser(name string, scenario *Scenario) *VirtualUser {
return &VirtualUser{ name: name, scenario: scenario }
}
func (vu *VirtualUser) Init(log *log.Logger) error {
log.Println("--> VirtualUser::Init()")
// TODO
log.Println("<-- VirtualUser::Init()")
return nil
}
func (vu *VirtualUser) Run(log *log.Logger, wg *sync.WaitGroup, start <-chan int, stepsToController chan<- StepIteration, scenarioToController chan<- ScenarioIteration) error {
log.Println("--> VirtualUser::Run()")
// Make sure we notify we are done when this method is finished
defer wg.Done()
// Wait for the signal to start
log.Println("Waiting for the signal to start")
<- start
log.Println("GOOOOOO !")
for {
err := vu.scenario.Do(log, vu, stepsToController, scenarioToController);
if (err != nil) {
log.Printf("Scenario ended with error %v, ", err)
}
}
log.Println("<-- VirtualUser::Run()")
return nil
}

17
src/itix.fr/loadsprinter/steps/FailStep.go

@ -0,0 +1,17 @@
package steps;
import "log"
import "errors"
type FailStep struct {
message string
}
func NewFailStep(message string) *FailStep {
return &FailStep{ message: message };
}
func (fs *FailStep) Do(log *log.Logger) error {
log.Printf("error: %v", fs.message)
return errors.New(fs.message);
}

16
src/itix.fr/loadsprinter/steps/LogStep.go

@ -0,0 +1,16 @@
package steps;
import "log"
type LogStep struct {
message string
}
func NewLogStep(message string) *LogStep {
return &LogStep{ message: message };
}
func (ls *LogStep) Do(log *log.Logger) error {
log.Println(ls.message)
return nil;
}

19
src/itix.fr/loadsprinter/steps/WaitStep.go

@ -0,0 +1,19 @@
package steps;
import "time"
import "log"
type WaitStep struct {
duration time.Duration
}
func NewWaitStep(duration time.Duration) *WaitStep {
return &WaitStep{ duration: duration };
}
func (ws *WaitStep) Do(log *log.Logger) error {
log.Printf("Sleeping during %v", ws.duration)
time.Sleep(ws.duration)
log.Println("Woken up !")
return nil
}
Loading…
Cancel
Save