Skip to content

Commit 930415c

Browse files
committed
first commit
0 parents  commit 930415c

11 files changed

+873
-0
lines changed

.gitignore

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Compiled Object files, Static and Dynamic libs (Shared Objects)
2+
*.o
3+
*.a
4+
*.so
5+
6+
# Folders
7+
_obj
8+
_test
9+
10+
# Architecture specific extensions/prefixes
11+
*.[568vq]
12+
[568vq].out
13+
14+
*.cgo1.go
15+
*.cgo2.c
16+
_cgo_defun.c
17+
_cgo_gotypes.go
18+
_cgo_export.*
19+
20+
_testmain.go
21+
22+
*.exe
23+
*.test
24+
*.prof
25+
*.pyc

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2016 myzhan
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Boomer
2+
3+
## Links
4+
5+
* Website: <a href="http://locust.io">locust.io</a>
6+
* Documentation: <a href="http://docs.locust.io">docs.locust.io</a>
7+
8+
## Description
9+
10+
Boomer is a locust slave runner written in golang. Just write your test scenarios in golang functions, and tell Boomer to run them.
11+
Boomer will report to the locust master automatically, and your test result will be displayed on the master's web UI.
12+
13+
14+
## Install
15+
```bash
16+
go get github.com/myzhan/boomer
17+
```
18+
19+
20+
## Sample(main.go)
21+
```go
22+
package main
23+
24+
25+
import "github.com/myzhan/boomer"
26+
import "time"
27+
28+
29+
func foo(){
30+
time.Sleep(100 * time.Millisecond)
31+
/*
32+
Report your test result as a failure, if you write it in python, it will looks like this
33+
events.request_success.fire(request_type="http", name="foo", response_time=100.0, response_length=10)
34+
*/
35+
boomer.Events.Publish("request_success", "foo", "http", 100.0, int64(10))
36+
}
37+
38+
39+
func bar(){
40+
time.Sleep(100 * time.Millisecond)
41+
/*
42+
Report your test result as a failure, if you write it in python, it will looks like this
43+
events.request_failure.fire(request_type="udp", name="bar", response_time=100.0, exception=Exception("udp error"))
44+
*/
45+
boomer.Events.Publish("request_failure", "bar", "udp", 100.0, "udp error")
46+
}
47+
48+
49+
func main(){
50+
51+
task1 := &boomer.Task{
52+
Weight: 10,
53+
Fn: foo,
54+
}
55+
56+
task2 := &boomer.Task{
57+
Weight: 20,
58+
Fn: bar,
59+
}
60+
61+
62+
boomer.Run(task1, task2)
63+
64+
}
65+
```
66+
67+
## Usage
68+
```bash
69+
locust -f dummy.py --master --master-bind-host=127.0.0.1 --master-bind-port=5557
70+
go run main.go --master-host=127.0.0.1 --master-port=5557
71+
```
72+
73+
## License
74+
75+
Open source licensed under the MIT license (see _LICENSE_ file for details).

boomer.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package boomer
2+
3+
import (
4+
"flag"
5+
"os"
6+
"os/signal"
7+
"syscall"
8+
"log"
9+
"runtime"
10+
)
11+
12+
13+
func Run(tasks ... *Task) {
14+
15+
16+
// support go version below 1.5
17+
runtime.GOMAXPROCS(runtime.NumCPU())
18+
19+
masterHost := flag.String("master-host", "127.0.0.1", "Host or IP address of locust master for distributed load testing. Defaults to 127.0.0.1.")
20+
masterPort := flag.Int("master-port", 5557, "The port to connect to that is used by the locust master for distributed load testing. Defaults to 5557.")
21+
22+
flag.Parse()
23+
24+
client := NewSocketClient(*masterHost, *masterPort)
25+
26+
runner := &Runner{
27+
Tasks: tasks,
28+
Client: client,
29+
NodeId: GetNodeId(),
30+
}
31+
32+
Events.Subscribe("boomer:report_to_master", runner.onReportToMaster)
33+
Events.Subscribe("boomer:quit", runner.onQuiting)
34+
35+
runner.GetReady()
36+
37+
c := make(chan os.Signal)
38+
signal.Notify(c, syscall.SIGINT)
39+
40+
<- c
41+
Events.Publish("boomer:quit")
42+
43+
// wait for quit message is sent to master
44+
<- DisconnectedFromServer
45+
log.Println("shut down")
46+
47+
}

client.go

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package boomer
2+
3+
import (
4+
"net"
5+
"log"
6+
"encoding/binary"
7+
"bytes"
8+
"fmt"
9+
)
10+
11+
12+
type Client interface {
13+
recv()
14+
send()
15+
}
16+
17+
18+
var FromServer = make(chan *Message, 100)
19+
var ToServer = make(chan *Message, 100)
20+
var DisconnectedFromServer = make(chan bool)
21+
22+
23+
type SocketClient struct {
24+
25+
conn *net.TCPConn
26+
}
27+
28+
29+
func NewSocketClient(masterHost string, masterPort int) *SocketClient{
30+
serverAddr := fmt.Sprintf("%s:%d", masterHost, masterPort)
31+
tcpAddr, _ := net.ResolveTCPAddr("tcp", serverAddr)
32+
conn, err := net.DialTCP("tcp", nil, tcpAddr)
33+
if err != nil {
34+
log.Fatalf("Failed to connect to the Locust master: %s %s", serverAddr, err)
35+
}
36+
conn.SetNoDelay(true)
37+
newClient := &SocketClient{
38+
conn: conn,
39+
}
40+
go newClient.recv()
41+
go newClient.send()
42+
return newClient
43+
}
44+
45+
46+
func (this *SocketClient) recvBytes(length int) ([]byte) {
47+
buf := make([]byte, length)
48+
for length > 0 {
49+
n, err := this.conn.Read(buf)
50+
if err != nil {
51+
log.Fatal(err)
52+
}
53+
length = length - n
54+
}
55+
return buf
56+
}
57+
58+
59+
func (this *SocketClient) recv() {
60+
for {
61+
h := this.recvBytes(4)
62+
msgLength := binary.BigEndian.Uint32(h)
63+
msg := this.recvBytes(int(msgLength))
64+
msgFromMaster := NewMessageFromBytes(msg)
65+
FromServer <- msgFromMaster
66+
}
67+
68+
}
69+
70+
71+
func (this *SocketClient) send() {
72+
for {
73+
select {
74+
case msg := <- ToServer:
75+
this.sendMessage(msg)
76+
if msg.Type == "quit"{
77+
DisconnectedFromServer <- true
78+
}
79+
}
80+
}
81+
}
82+
83+
84+
func (this *SocketClient) sendMessage(msg *Message){
85+
packed := msg.Serialize()
86+
buf := new(bytes.Buffer)
87+
/*
88+
use a fixed length header that indicates the length of the body
89+
-----------------
90+
| length | body |
91+
-----------------
92+
*/
93+
binary.Write(buf, binary.BigEndian, int32(len(packed)))
94+
buf.Write(packed)
95+
this.conn.Write(buf.Bytes())
96+
}

dummy.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# coding: utf8
2+
3+
from locust import Locust, TaskSet, task
4+
5+
class MyTaskSet(TaskSet):
6+
@task(20)
7+
def hello(self):
8+
pass
9+
10+
class Dummy(Locust):
11+
task_set = MyTaskSet

events.go

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package boomer
2+
3+
4+
import (
5+
"github.com/asaskevich/EventBus"
6+
)
7+
8+
9+
var Events = EventBus.New()
10+
11+
12+
func requestSuccessHandler(requestType string, name string, responseTime float64, responseLength int64) {
13+
RequestSuccessChannel <- &RequestSuccess{
14+
requestType: requestType,
15+
name: name,
16+
responseTime: responseTime,
17+
responseLength: responseLength,
18+
}
19+
}
20+
21+
22+
func requestFailureHandler(requestType string, name string, responseTime float64, exception string) {
23+
RequestFailureChannel <- &RequestFailure{
24+
requestType: requestType,
25+
name: name,
26+
responseTime: responseTime,
27+
error: exception,
28+
}
29+
}
30+
31+
32+
func init(){
33+
Events.Subscribe("request_success", requestSuccessHandler)
34+
Events.Subscribe("request_failure", requestFailureHandler)
35+
}

message.go

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package boomer
2+
3+
4+
import (
5+
"github.com/ugorji/go/codec"
6+
"log"
7+
)
8+
9+
10+
var (
11+
mh codec.MsgpackHandle
12+
)
13+
14+
15+
type Message struct {
16+
Type string `codec: "type"`
17+
Data map[string] interface{} `codec: "data"`
18+
NodeId string `codec: "node_id"`
19+
}
20+
21+
22+
func (this *Message) Serialize() (out []byte){
23+
mh.StructToArray = true
24+
enc := codec.NewEncoderBytes(&out, &mh)
25+
err := enc.Encode(this)
26+
if err != nil {
27+
log.Fatal("[msgpack] encode fail")
28+
}
29+
return
30+
}
31+
32+
33+
func NewMessageFromBytes(raw []byte) (*Message) {
34+
mh.StructToArray = true
35+
dec := codec.NewDecoderBytes(raw, &mh)
36+
var newMsg = &Message{}
37+
err := dec.Decode(newMsg)
38+
if err != nil {
39+
log.Fatal("[msgpack] decode fail")
40+
}
41+
return newMsg
42+
}

0 commit comments

Comments
 (0)