init
This commit is contained in:
commit
c8814d7177
|
|
@ -0,0 +1,29 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2016, vadv
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
BINARY=./bin/zabbix-bench
|
||||
SOURCEDIR=./src
|
||||
SOURCES := $(shell find $(SOURCEDIR) -name '*.go')
|
||||
GOPATH := ${PWD}:${GOPATH}
|
||||
export GOPATH
|
||||
.DEFAULT_GOAL: $(BINARY)
|
||||
$(BINARY): $(SOURCES)
|
||||
go build -o ${BINARY} $(SOURCEDIR)/main.go
|
||||
run: clean $(BINARY)
|
||||
${BINARY}
|
||||
clean:
|
||||
rm -f $(BINARY)
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
Benchmarking and stress testing tool for the Zabbix server
|
||||
|
||||
```bash
|
||||
Usage of zabbix-bench:
|
||||
-client int
|
||||
number of concurrent clients (default 200)
|
||||
-client-format string
|
||||
format of client name (default "client-%d")
|
||||
-metric-format string
|
||||
format of metric name in packet (default "metric-%d")
|
||||
-packet-delay duration
|
||||
delay of send packet (default 100ms)
|
||||
-packet-send-timeout duration
|
||||
packet send timeout (default 10ms)
|
||||
-packet-size int
|
||||
count of metric in packet (default 100)
|
||||
-zabbix string
|
||||
address of zabbix server (default "127.0.0.1:10051")
|
||||
```
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"zabbix"
|
||||
)
|
||||
|
||||
var (
|
||||
argClientCount = flag.Int("client", 200, "number of concurrent clients")
|
||||
argClinetName = flag.String("client-format", "client-%d", "format of client name")
|
||||
argPacketSize = flag.Int("packet-size", 100, "count of metric in packet")
|
||||
argMetricName = flag.String("metric-format", "metric-%d", "format of metric name in packet")
|
||||
argPacketDelay = flag.Duration("packet-delay", 100*time.Millisecond, "delay of send packet")
|
||||
argSendTimeout = flag.Duration("packet-send-timeout", 10*time.Millisecond, "packet send timeout")
|
||||
argZabbix = flag.String("zabbix", "127.0.0.1:10051", "address of zabbix server")
|
||||
|
||||
errorChannel = make(chan error, 10)
|
||||
completedChannel = make(chan int, 10)
|
||||
signalChannel = make(chan os.Signal, 1)
|
||||
|
||||
mutex = &sync.Mutex{}
|
||||
counter, total, sec = 0, 0, 1
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
runtime.GOMAXPROCS(runtime.NumCPU() * 4)
|
||||
signal.Notify(signalChannel, os.Interrupt)
|
||||
signal.Notify(signalChannel, syscall.SIGTERM)
|
||||
|
||||
if !flag.Parsed() {
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
for i := 0; i < *argClientCount; i++ {
|
||||
go StartClient(i)
|
||||
}
|
||||
|
||||
ticker := time.Tick(time.Second)
|
||||
for {
|
||||
select {
|
||||
case <-ticker:
|
||||
mutex.Lock()
|
||||
sec += 1
|
||||
os.Stdout.WriteString(fmt.Sprintf("Metric sended: %d\n", counter))
|
||||
counter = 0
|
||||
mutex.Unlock()
|
||||
case count := <-completedChannel:
|
||||
mutex.Lock()
|
||||
counter += count
|
||||
total += count
|
||||
mutex.Unlock()
|
||||
case err := <-errorChannel:
|
||||
os.Stderr.WriteString(fmt.Sprintf("Error write package:\t%s\n", err.Error()))
|
||||
case <-signalChannel:
|
||||
os.Stdout.WriteString(fmt.Sprintf("Total: %d (%d metric/s)\n", total, int(total/sec)))
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// client of zabbix server
|
||||
type client struct {
|
||||
id int
|
||||
host string
|
||||
sender *zabbix.Sender
|
||||
}
|
||||
|
||||
// generate and send zabbix packet
|
||||
func (c *client) send() error {
|
||||
now := time.Now().Unix()
|
||||
metrics := make([]*zabbix.Metric, 0)
|
||||
for i := 0; i < *argPacketSize; i++ {
|
||||
metrics = append(metrics, zabbix.NewMetric(c.host, fmt.Sprintf(*argMetricName, i), string(i), now))
|
||||
}
|
||||
return c.sender.Send(zabbix.NewPacket(metrics, now))
|
||||
}
|
||||
|
||||
func StartClient(id int) {
|
||||
c := &client{
|
||||
id: id,
|
||||
host: fmt.Sprintf(*argClinetName, id),
|
||||
sender: zabbix.NewSender(*argZabbix),
|
||||
}
|
||||
ticker := time.Tick(*argPacketDelay)
|
||||
for {
|
||||
select {
|
||||
case <-ticker:
|
||||
if err := c.send(); err != nil {
|
||||
errorChannel <- err
|
||||
} else {
|
||||
completedChannel <- *argPacketSize
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package zabbix
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Metric struct {
|
||||
Host string `json:"host"`
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
Clock int64 `json:"clock"`
|
||||
}
|
||||
|
||||
func NewMetric(host, key, value string, clock ...int64) *Metric {
|
||||
m := &Metric{Host: host, Key: key, Value: value}
|
||||
if len(clock) > 0 {
|
||||
m.Clock = clock[0]
|
||||
} else {
|
||||
m.Clock = time.Now().Unix()
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package zabbix
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Packet struct {
|
||||
Request string `json:"request"`
|
||||
Data []*Metric `json:"data"`
|
||||
Clock int64 `json:"clock"`
|
||||
jsonData []byte // cached json data
|
||||
dataLen []byte // cached length data
|
||||
}
|
||||
|
||||
func NewPacket(data []*Metric, clock ...int64) *Packet {
|
||||
p := &Packet{Request: `sender data`, Data: data}
|
||||
if len(clock) > 0 {
|
||||
p.Clock = clock[0]
|
||||
} else {
|
||||
p.Clock = time.Now().Unix()
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// cache json data
|
||||
func (p *Packet) Json() []byte {
|
||||
if len(p.jsonData) != 0 {
|
||||
return p.jsonData
|
||||
}
|
||||
jsonData, _ := json.Marshal(p)
|
||||
p.jsonData = jsonData
|
||||
return p.jsonData
|
||||
}
|
||||
|
||||
// cached length data
|
||||
func (p *Packet) DataLen() []byte {
|
||||
if len(p.dataLen) > 0 {
|
||||
return p.dataLen
|
||||
}
|
||||
dataLen := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint32(dataLen, uint32(len(p.Json())))
|
||||
p.dataLen = dataLen
|
||||
return p.dataLen
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package zabbix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
var zbxHeader = []byte("ZBXD\x01")
|
||||
|
||||
type Sender struct {
|
||||
address string
|
||||
iaddr *net.TCPAddr
|
||||
}
|
||||
|
||||
func NewSender(address string) *Sender {
|
||||
s := &Sender{address: address}
|
||||
if err := s.resolv(); err != nil {
|
||||
os.Stderr.WriteString(fmt.Sprintf("Can't resolv zabbix address: %s\n", err.Error()))
|
||||
os.Exit(1)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Sender) resolv() error {
|
||||
if iaddr, err := net.ResolveTCPAddr("tcp", s.address); err != nil {
|
||||
return err
|
||||
} else {
|
||||
s.iaddr = iaddr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Sender) connect() (*net.TCPConn, error) {
|
||||
if conn, err := net.DialTCP("tcp", nil, s.iaddr); err != nil { // TODO: timeout
|
||||
return nil, err
|
||||
} else {
|
||||
return conn, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Sender) read(conn *net.TCPConn) ([]byte, error) {
|
||||
return ioutil.ReadAll(conn) // TODO: timeout
|
||||
}
|
||||
|
||||
func (s *Sender) Send(packet *Packet) error {
|
||||
|
||||
conn, err := s.connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buffer := append(zbxHeader, packet.DataLen()...)
|
||||
buffer = append(buffer, packet.Json()...)
|
||||
|
||||
_, err = conn.Write(buffer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.read(conn)
|
||||
if err != nil {
|
||||
os.Stderr.WriteString(fmt.Sprintf("Read zabbix response error: %s\n", err.Error()))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue