├── README.md └── lualib └── profile_lib.lua /README.md: -------------------------------------------------------------------------------- 1 | 这是一个 Skynet profile 的便捷用例,查看服务中处理消息的函数实际 cpu 消耗情况,[Skynet profile wiki](https://github.com/cloudwu/skynet/wiki/Profile) 2 | 3 | Skynet 默认开启 profile ,但只会统计单个服务所有消息回调的 cpu 总耗时,函数的真实耗时需要手动开启(不包括挂起等待的时间) 4 | 5 | **API** 6 | 这个 API 会返回一个 function 供 `skynet.dispatch` 或 `skynet.register_protocol` ,通常我们只关心 `"lua"` type 7 | `profileLib.init(CMD, name, swith, realFun, extFun)` `CMD` 服务消息处理函数 table, `name` 服务名,`swith` 该服务的 profile 开关, nil 时默认为 skynet config 中的 profile 配置,`realFun` 自定义整个函数,`extFun` 自定义追加函数 8 | **注意** 9 | 返回的默认注册函数中调用方式为 `CMD[cmd](CMD, ...)` ,所以 `CMD` 函数样例为 `CMD:xxxFun(...)` 或 `CMD.xxxFun(self, ...)` 10 | 11 | 示例: 12 | ```lua 13 | local profileLib = require "profile_lib" 14 | 15 | --同配置开关,使用默认注册函数 16 | skynet.dispatch("lua", profileLib.init(CMD, "player_mgr")) 17 | 18 | --不开启 profile,使用默认注册函数 19 | skynet.dispatch("lua", profileLib.init(CMD, "logger", false)) 20 | 21 | --同配置开关,自定义注册函数 22 | skynet.dispatch("lua", profileLib.init(CMD, "watchdog", nil, function(session, _, cmd, subcmd, ...) 23 | if cmd == "socket" then 24 | local f = CMD.SockMgr[subcmd] 25 | f(CMD.SockMgr, ...) 26 | else 27 | local f = CMD[cmd] 28 | if not f then 29 | log.info("watchdog can't dispatch cmd ".. (cmd or nil)) 30 | skynet.ret(skynet.pack({ok=false})) 31 | return 32 | end 33 | if session == 0 then 34 | f(CMD, subcmd, ...) 35 | else 36 | skynet.ret(skynet.pack(f(CMD, subcmd, ...))) 37 | end 38 | end 39 | end)) 40 | 41 | --同配置开关,使用默认注册函数,定义追加函数 42 | skynet.dispatch("lua", profileLib.init(CMD, "agent", nil, nil, function() 43 | collectgarbage("step") 44 | end)) 45 | ``` 46 | 47 | 然后就可以对愉快开启的服务 console_debug info 了,默认以总耗时排序,带参则以平均耗时排序 48 | `average` 平均耗时(秒) `callTimes` 调用次数 `costTime` 总耗时(秒) `fun` 函数名 49 | ``` 50 | info 0000001f 51 | 1 average:0.0000541680 callTimes:4 costTime:0.0002166720 fun:send 52 | 2 average:0.0000995960 callTimes:1 costTime:0.0000995960 fun:start 53 | 3 average:0.0000831420 callTimes:1 costTime:0.0000831420 fun:join 54 | 4 average:0.0000109660 callTimes:1 costTime:0.0000109660 fun:check 55 | 56 | info 0000001f true 57 | 1 average:0.0000995960 callTimes:1 costTime:0.0000995960 fun:start 58 | 2 average:0.0000831420 callTimes:1 costTime:0.0000831420 fun:join 59 | 3 average:0.0000559698 callTimes:6 costTime:0.0003358190 fun:send 60 | 4 average:0.0000109660 callTimes:1 costTime:0.0000109660 fun:check 61 | 62 | ``` 63 | -------------------------------------------------------------------------------- /lualib/profile_lib.lua: -------------------------------------------------------------------------------- 1 | local profile = require "skynet.profile" 2 | local skynet = require "skynet" 3 | local envSwith = skynet.getenv("profile") 4 | 5 | local table_insert = table.insert 6 | 7 | local profileLib = {} 8 | 9 | local profileTime = {} 10 | 11 | function profileLib.init(CMD, name, swith, realFun, extFun) 12 | if swith == nil then swith = envSwith end 13 | if extFun == nil then extFun = function() end end 14 | if realFun == nil then 15 | realFun = function(session, _, cmd, ...) 16 | local f = CMD[cmd] 17 | if not f then 18 | skynet.error(name .. " can't dispatch cmd ".. (cmd or nil)) 19 | skynet.ret(skynet.pack({ok=false})) 20 | return 21 | end 22 | 23 | if session > 0 then 24 | skynet.ret(skynet.pack(f(CMD, ...))) 25 | else 26 | f(CMD, ...) 27 | end 28 | 29 | extFun() 30 | end 31 | end 32 | 33 | if swith then 34 | skynet.info_func(function(sortByAverage) 35 | local ret = {} 36 | for cmd, info in pairs(profileTime) do 37 | local time = string.format("%.10f", info.ti) 38 | local average = string.format("%.10f", time/info.n) 39 | table_insert(ret, {average=average, costTime=time, callTimes=info.n, fun=cmd}) 40 | end 41 | if sortByAverage then 42 | table.sort(ret, function(a,b) 43 | return a.average > b.average 44 | end) 45 | else 46 | table.sort(ret, function(a,b) 47 | return a.costTime > b.costTime 48 | end) 49 | end 50 | return ret 51 | end) 52 | 53 | return function(session, _, cmd, ...) 54 | profile.start() 55 | 56 | realFun(session, _, cmd, ...) 57 | 58 | local time = profile.stop() 59 | local p = profileTime[cmd] 60 | if p == nil then 61 | p = { n = 0, ti = 0 } 62 | profileTime[cmd] = p 63 | end 64 | p.n = p.n + 1 65 | p.ti = p.ti + time 66 | 67 | end 68 | else 69 | return realFun 70 | end 71 | end 72 | 73 | return profileLib 74 | --------------------------------------------------------------------------------