购买两台按量付费的云主机,第一台运行被压测的服务,另一台做压测机,配置如下:
- 实例:通用型 g5 / ecs.g5.xlarge(4 核 CPU 16GiB)
- 系统盘 : SSD 云盘 20GiB
- 镜像 : Ubuntu 16.04 64 位
开通了 3 个小时,成本 10 块钱左右。
内核参数调优#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| #该参数设置系统的 TIME_WAIT 的数量,如果超过默认值则会被立即清除
net.ipv4.tcp_max_tw_buckets = 20000
#定义了系统中每一个端口最大的监听队列的长度,这是个全局的参数
net.core.somaxconn = 65535
#对于还未获得对方确认的连接请求,可保存在队列中的最大数目
net.ipv4.tcp_max_syn_backlog = 262144
#在每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目
net.core.netdev_max_backlog = 30000
#能够更快地回收 TIME-WAIT 套接字。此选项会导致处于 NAT 网络的客户端超时,建议为 0
net.ipv4.tcp_tw_recycle = 0
#系统所有进程一共可以打开的文件数量
fs.file-max = 6815744
#设置单进程允许打开文件数
ulimit -HSn 102400
|
程序调优#
- nginx 设置为固定 4 个进程(和 cpu 核心数相同),使用 epoll,单进程 10240 个连接
- php-fpm 设置为固定 512 个进程,开启 opcache,关闭 opcache 的自动刷新,和 nginx 通讯使用 socket 文件
- laravel5.6 框架 关闭 session 等中间件,关闭 debug 模式,运行 artisan 命令缓存调优
- thinkphp5.1 框架 关闭 session 关闭 debug
压测对象#
php 无框架
1
2
3
| <?php
usleep(50000); // 模拟业务逻辑操作数据耗时等 50ms
echo str_repeat('s', 4000); // 模拟接口返回 4kb 数据
|
php laravel 框架
1
2
3
4
5
| <?php
Route::get('/', function () {
usleep(50000);
return str_repeat('s', 4000);
});
|
php thinkphp 框架
1
2
3
4
5
6
7
8
9
10
| <?php
namespace app\index\controller;
class Index
{
public function index()
{
usleep(50000);
return str_repeat('s', 4000);
}
}
|
golang gin 框架
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| package main
import (
"github.com/gin-gonic/gin"
"time"
"strings"
)
func main() {
gin.SetMode(gin.ReleaseMode)
router := gin.New()
router.GET("/", func(c *gin.Context) {
time.Sleep(time.Millisecond * 50)
c.String(200, strings.Repeat("s", 4000))
})
router.Run(":8888")
}
|
压测方法#
两台机器在同一内网内,直接执行 ab -c100 -n100000 http://hostname/
压测结果#
被压测的机器是 4 核心 cpu,也就是说系统负载到 4 的时候,我们 4 个核心都跑到了 100%,其他任务就要等待排队了。我们预设超过 5 倍 cpu 核心数(20)的负载状态是危险状态。负载到>=4 的时候,这台机器每秒处理请求数基本已经确定了,因为 cpu 已经跑满了。从系统负载来看,php7 无框架的情况,支持高并发还是可以的。laravel 是很糟糕的,在这台机器上并发 100 就会持续高负载。以此类推 thinkphp 在这台机器上大概是 400 并发,就会持续高负载。golang 在 1500 并发的时候负载才到 4,说明 golang 在这台机器才刚到每秒处理请求数的极限,只有加核心才能每秒处理更多请求了。