写点什么

架构师训练营第 1 期 -Week7 - 课后练习

用户头像
鲁小鲁
关注
发布于: 2020 年 11 月 08 日



作业



  1. 性能压测的时候,随着并发压力的增加,系统响应时间和吞吐量如何变化,为什么?

性能压测可以三步:性能测试、负载测试、压力测试。



第一阶段:性能测试,随着并发压力的增加,系统响应时间变长,但是幅度不大,吞吐量持续线性上升,直到达到系统最佳运行点(b点)。此时系统某种资源呈现饱和状态,再增加并发,会造成阻塞。



第二阶段:负载测试,随着并发的增加,系统响应时间变长,吞吐量继续上升,但是上升幅度变慢,直到达到系统最大负载点(c点)。此时系统某种资源不足(如CPU核数、内存、磁盘IO、网络IO),过了系统最大负载点,系统响应时间线性增长,吞吐量开始下降。



第三阶段:压力测试,随着并发压力的增加,系统响应时间变长,吞吐量降低、开始出现响应异常。直到达到系统崩溃点(d点)。此时系统某种资源耗尽,请求全部失败。







2.用你熟悉的编程语言写一个 Web 性能压测工具,输入参数:URL,请求总次数,并发数。输出参数:平均响应时间,95% 响应时间。用这个测试工具以 10 并发、100 次请求压测 www.baidu.com。



采用golang实现。



stress.go

package stress

import (
"math"
"net/http"
"sort"
"sync"
"time"
)

type Result struct {
Avg int64
P50 int64
P95 int64
P100 int64
Qps int64
Response []int64
}

var usedChan chan int64

/**
输入参数:url,执行次数
输出参数:平均响应时间,95% 响应时间
qps: = (1000/响应时间)* 并发数 = (200/77)*10 = 2597
*/
func Execute(url string, concurrence int64, times int64) *Result {

if times < 1 {
return &Result{}
}

usedChan = make(chan int64, times)
request(url, concurrence, times)

total, response := PrecessResponse(times)
index95 := getIndex(times, 0.95)
index50 := getIndex(times, 0.5)

avg := math.Ceil(float64(total) / float64(times))
qps := (1000 / avg) * float64(concurrence)
result := &Result{
Avg: int64(avg),
P50: response[index50],
P95: response[index95],
P100: response[times-1],
Qps: int64(qps),
Response: response,
}

return result
}

// 发起请求
func request(url string, concurrence int64, times int64) {
var i int64 = 0
var wg sync.WaitGroup
wg.Add(int(times))
limit := make(chan bool, concurrence) // 控制并发数, 每时每刻都持续一定的并发

for ; i < times; i++ {
limit <- true // 写入数据,当buff满了时,阻塞在这里。控制request的协程数量

go func(i int64) {
requestGet(url)
wg.Done()
<-limit // 释放一次请求
}(i)
}

wg.Wait()
close(usedChan)
}

// 处理耗时
func PrecessResponse(times int64) (int64, []int64) {
var total int64
response := make([]int64, 0, times)

for {
used, ok := <-usedChan
if !ok { // 判断channel是否被关闭且没有数据
break
}

total = total + used
response = append(response, used)
}

// 升序
sort.Slice(response, func(i, j int) bool {
return response[i] < response[j]
})

return total, response
}

// 获取response的index
func getIndex(times int64, scale float64) int {
result := float64(times) * scale

index := math.Floor(result)

if times < int64(index) {
return int(times)
}

return int(index)
}

// 执行请求
func requestGet(url string) {
//fmt.Println("request one time")
start := time.Now()
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Get(url)

if err != nil {
panic(err)
}

defer resp.Body.Close()

usedChan <- time.Now().Sub(start).Milliseconds()
}


测试用例 stress_test.go

package stress

import (
"fmt"
"testing"
"time"
)

func TestExecute(t *testing.T) {
url := "https://www.baidu.com"

var requestTotal int64 = 1000
var number int64 = 10

result := Execute(url, number, requestTotal)
fmt.Printf("并发数:%v\n", number)
fmt.Printf("请求数:%v\n", requestTotal)
fmt.Printf("平均时间:%v\n", result.Avg)
fmt.Printf("50:%v\n", result.P50)
fmt.Printf("95:%v\n", result.P95)
fmt.Printf("100:%v\n", result.P100)
fmt.Printf("qps:%v\n", result.Qps)
fmt.Println("**************************************************")
time.Sleep(5 * time.Second)

}



输出

并发数:10

请求数:300

平均时间:57

50:49

95:81

100:1058

qps:5263

**************************************************





用户头像

鲁小鲁

关注

博学而笃志,切问而近思。 2018.09.29 加入

Go开发

评论

发布
暂无评论
架构师训练营第 1 期 -Week7 - 课后练习