从零开始用golang编写一个分布式测试工具

起源

当开发http 接口的时候,每每咱们会关心开发的server能承受多少压力,这时候一个比较经常使用的工具是 apache bench。一部分状况下ab工具确实能知足需求,可是不少时候并不能,须要分布式式测试工具。python

压力测试比较关心的是产生压力获取测试对象的如TPS、响应时延等性能数据主机资源如cpu,memory消耗数据以定位性能瓶颈,简单的单机测试工具并不能很好的知足这些需求。nginx

咱们能够选择云平台的分布式测试工具,好比腾讯的wetest,阿里云也有相似产品。可是这种产品每每收费不菲。也能够选择相似的开源产品,好比locust。可是调研发现,这种开源产品每每比较简单,或者过于陈旧。google官网有一个压力测试例子,用的就是locust。你们能够看一下这个工具,基于python,功能很是简陋,master,slave模式,不支持在线编辑脚本,修改测试要重启。git

如何设计这样的压力测试工具

k8s是目前比较流行的容器编排系统,是否能够在k8s上本身作一个分布式测试工具呢。固然能够用google推荐的作法,在k8s上运行master slave 模式的locust,又或者本身动手作一个。
既然运行在k8s上,那么这个测试工具实际上关心的事就比较简单了:k8s已经实现了调度,资源监控,咱们的工具只须要定义脚本,运行脚本,统计测试结果,收集测试过程的资源消耗。
须要说明的是,测试工具运行在k8s上只是为了利用k8s的基础设施,简化工具设计,事实上测试的对象能够是运行在k8s上或者在k8s外的任何服务。github

使用golang作测试脚本

选择golang做为测试脚本的缘由一是语言成熟,语法简单,二是goroutine很方便,很容易把压力打上去,三是即有编译型语言的高性能,同时又像脚本同样可以快速运行(编译很快)。golang

golang自己是编译型语言,不是脚本语言,运行要先编译,可是由于编译很快,实际上很容易当成一个脚原本执行,好比这个例子。或者显式的运行go build,go run,像docker/distribution项目的dockerfile这样。那么要实现一个"动态"运行golang的工具要作的就是:定义一种类型的任务,用户的测试脚本只要实现这种任务的interface,客户端就能够装载这种脚本编译运行。具体举例以下。docker

// 1. 定义一个TTask做为interface
type TTask interface {
    Name() string
    Run() int
}

// 2. 用户的测试脚本实现这个interface
// New ....
func newhello() task.TTask {
    return &hellotask{}
}

type hellotask struct {
}

func (h *hellotask) Name() string {
    return "task"
}

func (h *hellotask) Run() int {
    time.Sleep(time.Microsecond * 10)
    return 0
}

// 3. 实现一种注册式的插件机制,让用户的任务注册进来
var tasksets = make(map[string]NewFunc, 0)
func Register(newfunc NewFunc) {
    t := newfunc()
    _, registered := tasksets[t.Name()]
    if registered {
        panic(fmt.Sprintf("TTask named %s already registered", t.Name()))
    }
    tasksets[t.Name()] = newfunc
}

// 用户脚本中要注册他的脚本
Register(task.NewFunc(newhello))

// 4. 测试客户端不关心用户脚本的实现细节,运行tasksets里面TTask就能够了
func main() {
    for _, f := range tasksets {
        f().Run()
        ...
    }
}复制代码

一个更复杂一点的例子在这里apache

使用k8s的基础设施

首先实现测试任务,使用job是很合适不过的,设置parallelism并发运行。bash

其次要实现任务的动态添加和挂载,可使用k8s的configmap来实现。使用configmap来保存用户的脚本,运行agent的时候将脚本自动挂载到agent的容器对应路径,容器启动脚本中加入build流程,这样就能很方便的实现一种"动态"的运行golang脚本的效果了。并发

当压力测试使用多个节点的时候,咱们每每须要同时观测测试客户端和服务端的cpu等资源监控,由于客户端已经自然的运行在k8s上了,能够直接使用k8s的监控设施。
另外测试工具的设计并无局限在测试运行在k8s上的server,可是若是恰好,被测试的对象也运行在k8s上,那么也能够很方便的或者server的宿主机metrics,若是不是,那么server端就须要装一个收集metric的deamon实现一样的效果了。分布式

效果

dashboard支持查看测试任务,每一个任务有一个最近运行的记录和建立时间。

编辑一个测试项目,测试脚本是用golang编辑的,须要实现一个TTaskSet 的interface。支持设置任务的goroutine和运行时间,权重,设置权重以后goroutine数量会在多个taskset之间分配,一个taskset又能够添加多个task。taskset并发运行,一个taskset中的task串行运行,这样设计的好处是能够知足用户并发,串行,带context的串行多种需求,很是灵活。任务能够选择运行的节点,多个节点并发测试。

运行完测试的效果,目前尚未加入测试中的client,server资源监控,可是简单的测试统计已经有了。如图是测试的一个运行在1G虚拟机的nginx容器的测试结果。测试结果同时有各个节点的运行结果和汇总结果,同时绘制latency的百分位图。


完整项目地址在 github.com/arlert/ymir 欢迎拍砖。

相关文章
相关标签/搜索