离开游戏行业了,分享一个非常灵活好用游戏服务器压力测试工具,压测新手流程、活动、副本都用它,如果你有一定的编程基础,不妨试一试。

以下所有操作均在 windows 下执行

1.工具下载&运行

  1. 下载

    访问 https://gitee.com/lutianming/supersheeps_extend/releases

    下载最新版本的 zip 压缩包,解压并双击 exe 程序,默认启动单机模式

  2. 访问 web 后台

    第一种:在 supersheeps 控制台输入 0,键入回车

    第二种:浏览器访问 http://127.0.0.1:1080 或者 http://[::1]:1080

2.工具介绍

  1. 基于游戏客户端网络事件进行录制和回放,将回放过程抽象为一个状态可控、业务可编程的播放器。
  2. 核心功能由 C++ 编写,实现高性能异步网络 IO,充分利用 CPU 多核,单机并发性能强悍,使用静态 IP 突破单机端口最大 65535 限制,分布式设计更能无限扩展并发能力。
  3. 内部嵌套 lua 接收处理框架事件,C++ 向 lua 层提供播放状态、网络(支持 tcp、udp、ssl、kcp)、报表接口,用户仅需专注于协议解析和业务编码。

3.用例准备

  1. 工具通过代理的方式录制生成用例,内部实现了 http、https、socks5 代理服务(http/https/socks5 代理地址均为 127.0.0.1:1080)
  2. 进入 web 后台 “用例管理”,在 “录制” 项点击 “开启”,此时已经准备好进行用例录制。(开始录制前记得删除旧的数据)

  3. 再将要录制的游戏客户端配置好代理方式,启动客户端进行操作录制,这里我用一个 python 脚本模拟此过程,录制一条 http 请求,获取百度首页。

import requests

if __name__ == "__main__":
    proxyies = {'http':'http://127.0.0.1:1080', 'https':'http://127.0.0.1:1080'}
    res = requests.get(url='http://www.baidu.com', proxies=proxyies)
    print(res.status_code)
  1. 运行完上面的 python 脚本,就生成了一条默认用例,用例内容默认存储在 default.db 文件中

  2. 此时在 “录制” 项点击 “关闭”

4.创建任务并运行

  1. 在 web 后台 “任务管理”,我们如图配置任务信息,并点击 “添加”

  2. 在刚添加的任务点击 “开始” 执行任务,观察控制台日志输出,或者查看 log 目录下 task* 命名的日志文件。

  3. 不出意外任务会正确执行并自动结束

5.测试报告

  1. 可以从任务列表末尾产看报表

  2. 任务结束自动保存报表到 report 目录,通过 web 后台 “测试报告” 可以查看历史报表

6.代码编写及调试

  1. 因为内部已经实现了 http 插件,所以上面的任务可以正确运行,如果需要处理业务逻辑,修改请改请求参数,需要了解如何编写 lua 脚本
  2. 从控制台信息我们可以看到:
    当前项目编号是 0,
    所以脚本默认加载 project/lua/0/script/main.lua
    用文本编辑器打开文件
--http插件使用示例
local http_controller = require "http_plugin.controller"
local router = require "router"

function EventStart(task)
    Log(LOG_NORMAL,"EventStart", task)
    local paly_fast = false
    local run_count = 1
    local params = task.params
    if params.PlayFast then paly_fast = true end
    if params.RunCount then run_count = tonumber(params.RunCount) end
    http_controller.init(router, paly_fast, run_count)  --初始化,设置路由、播放模式、及执行次数
end

function EventConnectOpen(ip, port, protocol)
    Log(LOG_DEBUG,"EventConnectOpen")
    http_controller.connection_open(ip, port, protocol)
end

function EventConnectClose(ip, port, protocol)
    Log(LOG_DEBUG,"EventConnectClose")
    http_controller.connection_close(ip, port, protocol)
end

function EventConnectSend(ip, port, data, protocol)
    Log(LOG_DEBUG,"EventConnectSend")
    http_controller.connection_send(ip, port, data, protocol)
end

function EventConnectMade(hsock)
    Log(LOG_DEBUG,"EventConnectMade")
    http_controller.connection_made(hsock)
end

function EventConnectFailed(hsock, err)
    Log(LOG_DEBUG,"EventConnectFailed")
    http_controller.connection_failed(hsock)
end

function EventConnectClosed(hsock, err)
    Log(LOG_DEBUG,"EventConnectClosed")
    http_controller.connection_closed(hsock, err)
end

function EventConnectRecved(hsock, data)
    Log(LOG_DEBUG,"EventConnectRecved data, len", #data)
    --Log(3, "Tcp Socket Connect Recved:", #data)
    http_controller.connection_recved(hsock, data)
end

function EventTimeOut()
    --Log(LOG_DEBUG,"EventStart")
    http_controller.timeout()
end

function EventStop(msg)
    Log(LOG_FAULT,"EventStop:",msg)
end
  1. 可以看到 main.lua 中定义了 10 个 “Event” 为前缀的全局函数,这是框架默认必须存在的,用于接收相应事件的函数,其他代码实现用户可以任意修改。下面用伪代码展示这几个事件函数的调用过程
--伪代码
EventStart()  --开始
while(true){
    EventTimeout()
    if player_state != stop && event != NULL then
        case event:
            -- EventConnectOpen() ➡ pause
            -- EventConnectMade() ➡ play
            -- EventConnectFaild() ➡ stop
            -- EventConnectSend() ➡ pause
            -- EventConnectRecved() ➡ play
            -- EventConnectClose() ➡ stop
            -- EventConnectClosed() ➡ stop
    end
}
EventStop() --结束
  1. main.lua 中引用了由 lua 实现的 http 插件 (支持 websocket),插件部分实现了 http/websocket 协议解析和 cookie 处理,有必要用户可以自己修改
  2. http 插件要求用户传入接口路由,以处理接口请求前置逻辑及请求返回逻辑处理,其定义在 project/lua/0/script/router.lua
local router = {}

router["/"] = function(conn, req)  --接口路由,即使不写该请求也会被默认处理
    Log(LOG_NORMAL, "http请求:", req)
    --PlayPause()   --发送请求之后暂停执行,等待本次返回
    --conn.ssl = true  --启用ssl连接

    conn.on_response = function(conn, res)  --http接口返回回调
        Log(LOG_NORMAL, "http 返回:", res.status_code, res.status)
        --PlayNormal()  --接收返回后继续执行
    end

    conn.on_ws_request = function(conn, message)   --websocket发送回调
        Log(LOG_NORMAL, "websocket 发送:", message)
        return message
    end

    conn.on_ws_response = function(conn, message)   --websocket接收回调
        Log(LOG_NORMAL, "websocket 接收:", message)
    end

    conn.on_close = function(conn, err)  --连接关闭回调函数
        Log(LOG_NORMAL, "http关闭:", err)
    end

end

-- router["*"] = function(http, req)   --这个路由用于接收所有未知的接口,用户可以更加自由的组织自己的代码
--     print(req.uri)
-- end

return router
  1. 在这里 router.lua 才是用户需要关心的代码,尝试修改其中的代码,如添加打印信息,再次运行任务,以理解其运行机制。

7.压测实施及其他注意事项

  1. 因为此文章只是演示工具使用,上面任务中只配置了一个用户,用于功能调试,实际场景需要配置千上万用户。
  2. 上面演示使用 http 协议,如果游戏使用私有协议,需要自己实现协议解析和打包,工具内置了 sproto 和 protobuf 库。
  3. 每个用户拥有独立的 lua 环境,数据变量不会相互影响。
  4. 注意不要在事件中书写死循环和使用协程,避免阻塞线程。
  5. 实际压测时请关闭日志控制台打印和调整日志输出等级,打印较多时影响并发性能,请通过日志文件查看日志。
  6. 有任何疑问可以阅读随工具附带的用户手册,web 后台 “关于 - 查看用户手册”,或者联系作者。


↙↙↙阅读原文可查看相关链接,并与作者交流