继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

go web压测工具实现

青春有我
关注TA
已关注
手记 1206
粉丝 205
获赞 1008

1)要知道的几个概念
并发连接数:
理解:并发连接数不等于并发数,真实的并发数只能在服务器中计算出来,这边的并发数等于处在从请求发出去,到收到服务器信息的这状态的个数总和。
总请求次数
响应时间
平均响应时间
成功次数
失败次数

2)代码实现

package main

import (    "fmt"
    "log"
    "net/http"
    "os"
    "strconv"
    "sync"
    "time")

var (
    SBCNum     int           // 并发连接数
    QPSNum     int           // 总请求次数
    RTNum      time.Duration // 响应时间
    RTTNum     time.Duration // 平均响应时间
    SuccessNum int           // 成功次数
    FailNum    int           // 失败次数

    BeginTime time.Time // 开始时间
    SecNum    int       // 秒数

    RQNum int    // 最大并发数,由命令行传入
    Url   string // url,由命令行传入

    controlNum chan int // 控制并发数量)

var mu sync.Mutex // 必须加锁func init() {    if len(os.Args) != 3 {
        log.Fatal("请求次数 url")
    }
    RQNum, _ = strconv.Atoi(os.Args[1])
    controlNum = make(chan int, RQNum)
    Url = os.Args[2]
}

func main() {
    go func() {        for range time.Tick(1 * time.Second) {
            SecNum++
            fmt.Printf("并发数:%d,请求次数:%d,平均响应时间:%s,成功次数:%d,失败次数:%d\n",
                len(controlNum), SuccessNum+FailNum, RTNum/(time.Duration(SecNum)*time.Second), SuccessNum, FailNum)
        }
    }()
    requite()
}

func requite() {    for {
        controlNum <- 1
        go func(c chan int) {
            var tb time.Time
            var el time.Duration            for {
                tb = time.Now()
                _, err := http.Get(Url)                if err == nil {
                    el = time.Since(tb)
                    mu.Lock() // 上锁
                    SuccessNum++
                    RTNum += el
                    mu.Unlock() // 解锁
                } else {
                    mu.Lock() // 上锁
                    FailNum++
                    mu.Unlock() // 解锁
                }
                time.Sleep(1 * time.Second)
            }
            <- c
        }(controlNum)
        time.Sleep(45 * time.Millisecond)
    }
}

四、单机多用户压测Go实现

package mainimport (    "fmt"
    "log"
    "net/http"
    "os"
    "strconv"
    "sync"
    "time")var (
    BeginTime time.Time // 开始时间
    SecNum    int       // 秒数

    RQNum int    // 最大并发数,由命令行传入
    Url   string // url,由命令行传入

    userNum    int      // 用户数
)var users []User

type User struct {
    UserId      int          // 用户id
    SBCNum     int           // 并发连接数
    QPSNum     int           // 总请求次数
    RTNum      time.Duration // 响应时间
    RTTNum     time.Duration // 平均响应时间
    SuccessNum int           // 成功次数
    FailNum    int           // 失败次数
    mu         sync.Mutex
}

func (u *User) request(url string) {
    var tb time.Time
    var el time.Duration    for i := 0;i < u.QPSNum;i++ {
        u.SBCNum++        go func(u *User) {            for {
                tb = time.Now()
                _, err := http.Get(Url)                if err == nil {
                    el = time.Since(tb)
                    u.mu.Lock() // 上锁
                    u.SuccessNum++
                    u.RTNum += el
                    u.mu.Unlock() // 解锁
                } else {
                    u.mu.Lock() // 上锁
                    u.FailNum++
                    u.mu.Unlock() // 解锁
                }
                time.Sleep(1 * time.Second)
            }
        }(u)
    }
}

func (u *User) show() {
    fmt.Printf("用户id:%d,并发数:%d,请求次数:%d,平均响应时间:%s,成功次数:%d,失败次数:%d\n",
        u.UserId,
        u.SBCNum,
        u.SuccessNum + u.FailNum,
        u.RTNum/(time.Duration(SecNum)*time.Second),
        u.SuccessNum,
        u.FailNum)
}func showAll(us []User) {
    uLen := len(us)

    var SBCNum     int           // 并发连接数
    var RTNum      time.Duration // 响应时间
    var SuccessNum int           // 成功次数
    var FailNum    int           // 失败次数

    for i := 0;i < uLen;i++ {
        SBCNum += us[i].SBCNum
        SuccessNum += us[i].SuccessNum
        FailNum += us[i].FailNum
        RTNum += us[i].RTNum
        us[i].show()
    }
    fmt.Printf("并发数:%d,请求次数:%d,平均响应时间:%s,成功次数:%d,失败次数:%d\n",
        SBCNum,
        SuccessNum+FailNum,
        RTNum/(time.Duration(SecNum)*time.Second),
        SuccessNum,
        FailNum)
    fmt.Println()
}func init() {    if len(os.Args) != 4 {
        log.Fatal("用户数 请求次数 url")
    }
    userNum, _ = strconv.Atoi(os.Args[1])
    RQNum, _ = strconv.Atoi(os.Args[2])
    Url = os.Args[3]
    users = make([]User, userNum)
}func main() {    go func() {        for range time.Tick(2 * time.Second) {
            SecNum += 2
            showAll(users)
        }
    }()    for range time.Tick(1 * time.Second) {
        requite()
    }
}func requite() {
    c := make(chan int)
    temp := 0
    for i := 0;i < userNum;i++ {        if RQNum % userNum != 0 && i < RQNum % userNum {
            temp = 1
        } else {
            temp = 0
        }
        users[i].UserId = i
        users[i].QPSNum = RQNum / userNum + temp
        go users[i].request(Url)
        time.Sleep(45 * time.Millisecond)
    }
    <- c    // 阻塞}

五、分布式压测Go
分主节点和从节点,现在分别实现以下功能
1)主节点功能
收集从节点的压测信息
显示压测信息
2)从节点功能
将压测信息发送给主节点
3)整个工作原理
一个主节点启动,设置监听端口,使用TCP方式,启动若干个从节点,每个从节点通过IP+端口连接到这个主节点,之后主节点记录连接上来的从节点信息。从节点将相关信息发往主节点,主节点在设定的时间里显示信息。
代码实现:
主节点代码实现

package mainimport (    "log"
    "net"
    "time"
    "os"
    "encoding/json"
    "fmt")var ip string
var port string
var slaves []*slave

type slave struct {
    UserId string
    SBCNum     int           // 并发连接数
    QPSNum     int           // 总请求次数
    RTNum      time.Duration // 响应时间
    RTTNum     time.Duration // 响应时间
    SecNum     int           // 时间
    SuccessNum int           // 成功次数
    FailNum    int           // 失败次数
    Url string
    conn net.Conn
}

func (s *slave) Run() {
    var v interface{}
    buf := make([]byte, 1024)    for {
        n, err := s.conn.Read(buf)        if err != nil {
            log.Println(err)            break
        }
        err = json.Unmarshal(buf[:n], &v)        if err != nil {
            log.Println(err)            continue
        }
        s.SBCNum = int(v.(map[string]interface{})["SBCNum"].(float64))  // 并发连接数
        s.RTNum = time.Duration(v.(map[string]interface{})["RTNum"].(float64))  // 响应时间
        s.SuccessNum = int(v.(map[string]interface{})["SuccessNum"].(float64))  //SuccessNum int           // 成功次数
        s.FailNum = int(v.(map[string]interface{})["FailNum"].(float64))            //FailNum    int           // 失败次数
        s.SecNum = int(v.(map[string]interface{})["SecNum"].(float64))
    }
}func init() {    if len(os.Args) != 3 {
        log.Fatal(os.Args[0] + " ip port")
    }
    ip = os.Args[1]
    port = os.Args[2]
}func main() {
    s, err := net.Listen("tcp", ip + ":" + port)    if err != nil {
        log.Fatal(err)
    }
    defer s.Close()
    buf := make([]byte, 128)
    fmt.Println("Run...")    go func() {        for range time.Tick(2 * time.Second) {
            show(slaves)
        }
    }()    for {
        conn, err := s.Accept()        if err != nil {
            log.Println(err)            continue
        }
        n, err := conn.Read(buf)
        tempC := slave{conn:conn,UserId:conn.RemoteAddr().String(), Url:string(buf[:n])}
        go tempC.Run()
        slaves = append(slaves, &tempC)
    }
}func show(clients []*slave) {    if len(clients) == 0 {        return
    }
    temp := slave{}
    num := 0
    for _, client := range clients {        if client.SecNum == 0 {            continue
        }
        num++
        fmt.Printf("用户id:%s,url: %s,并发数:%d,请求次数:%d,平均响应时间:%s,成功次数:%d,失败次数:%d\n",
            client.UserId,
            client.Url,
            client.SBCNum,
            client.SuccessNum + client.FailNum,
            client.RTNum / (time.Duration(client.SecNum) * time.Second),
            client.SuccessNum,
            client.FailNum)
        temp.SBCNum += client.SBCNum
        temp.RTNum += client.RTNum / (time.Duration(client.SecNum) * time.Second)
        temp.SecNum += client.SecNum
        temp.SuccessNum += client.SuccessNum
        temp.FailNum += client.FailNum
    }    if num == 0 {        return
    }
    fmt.Printf("并发数:%d,请求次数:%d,平均响应时间:%s,成功次数:%d,失败次数:%d\n",
        temp.SBCNum,
        temp.SuccessNum + temp.FailNum,
        temp.RTNum / time.Duration(num),
        temp.SuccessNum,
        temp.FailNum)
    fmt.Println()
}func heartbeat(clients []slave) []slave {   // 标记耦合
    tempC := []slave{}    for _, client := range clients {
        _, err := client.conn.Write([]byte(""))        if err == nil { // 删除
            tempC = append(tempC, client)
        }
    }    return tempC
}

从节点

package mainimport (    "net"
    "github.com/lunny/log"
    "encoding/json"
    "time"
    "os"
    "net/http"
    "sync"
    "strconv"
    "fmt")type master struct {
    ip string
    port string
    conn net.Conn
}

var (
    SBCNum     int           // 并发连接数
    QPSNum     int           // 总请求次数
    RTNum      time.Duration // 响应时间
    RTTNum     time.Duration // 平均响应时间
    SuccessNum int           // 成功次数
    FailNum    int           // 失败次数
    SecNum     int

    mt master
    err error
    mu sync.Mutex // 必须加锁
    RQNum int    // 最大并发数,由命令行传入
    Url   string // url,由命令行传入)func init() {    if len(os.Args) != 5 {
        log.Fatalf("%s 并发数 url ip port", os.Args[0])
    }
    RQNum, err = strconv.Atoi(os.Args[1])    if err != nil {
        log.Println(err)
    }
    Url = os.Args[2]
    mt.ip = os.Args[3]
    mt.port = os.Args[4]
}func main() {
    mt.conn, err = net.Dial("tcp", mt.ip + ":" + mt.port)    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("连接服务器成功。。。")
    _, err = mt.conn.Write([]byte(Url))    if err != nil {
        log.Println(err)
    }    go func() {        for range time.Tick(1 * time.Second) {
            sendToMaster(mt, map[string]interface{}{                "SBCNum": SBCNum,           // 并发连接数
                "RTNum": RTNum,             // 响应时间
                "SecNum": SecNum,           // 时间
                "SuccessNum": SuccessNum,   // 成功次数
                "FailNum": FailNum,         // 失败次数
            })
        }
    }()    go func() {        for range time.Tick(1 * time.Second) {
            SecNum++
        }
    }()
    requite(RQNum, Url)
}func requite(RQNum int, url string) {
    c := make(chan int)    for i := 0;i < RQNum;i++ {
        SBCNum = i + 1
        go func(url string) {
            var tb time.Time
            var el time.Duration            for {
                tb = time.Now()
                _, err := http.Get(url)                if err == nil {
                    el = time.Since(tb)
                    mu.Lock() // 上锁
                    SuccessNum++
                    RTNum += el
                    mu.Unlock() // 解锁
                } else {
                    mu.Lock() // 上锁
                    FailNum++
                    mu.Unlock() // 解锁
                }
                time.Sleep(1 * time.Second)
            }
        }(url)
        time.Sleep(45 * time.Millisecond)
    }
    <- c    // 阻塞}func sendToMaster(mt master, data map[string]interface{}) {
    r, err := json.Marshal(data)    if err != nil {
        log.Println(err)
    }
    _, err = mt.conn.Write(r)    if err != nil {
        log.Println(err)
        os.Exit(1)
    }
}



作者:laijh
链接:https://www.jianshu.com/p/7f28aad6ecc5


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP