购买两台按量付费的云主机,第一台运行被压测的服务,另一台做压测机,配置如下:

  • 实例:通用型 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/

压测结果

gbench

分析

被压测的机器是 4 核心 cpu,也就是说系统负载到 4 的时候,我们 4 个核心都跑到了 100%,其他任务就要等待排队了。我们预设超过 5 倍 cpu 核心数(20)的负载状态是危险状态。负载到>=4 的时候,这台机器每秒处理请求数基本已经确定了,因为 cpu 已经跑满了。从系统负载来看,php7 无框架的情况,支持高并发还是可以的。laravel 是很糟糕的,在这台机器上并发 100 就会持续高负载。以此类推 thinkphp 在这台机器上大概是 400 并发,就会持续高负载。golang 在 1500 并发的时候负载才到 4,说明 golang 在这台机器才刚到每秒处理请求数的极限,只有加核心才能每秒处理更多请求了。