S## 需求分析
前几天接到一个需求,有一个游戏的服务器业务逻辑是使用Lua编写的,运行环境为 Ubuntu14.04,需要做一个统计分析模块,间隔一定时间,记录一次系统的CPU、内存、TCP连接数,在线玩家数,并写入数据库中。
Lua本身是应该是没有权限去获取系统信息的(没有查证),初步设想有两种可行方案:
最终决定使用方案二。
在 shell 中直接输入 top
可以进入视图
这个视图下,数据会间隔一定时间自动刷新,但是我们只需要获取一次数据。
输入 top -n1
即可指定返回一组数据。
我们需要以批处理方式执行命令,以便于让 lua 拿到管道的输出结果。
所以命令改为 top -bn1
。
经过测试,会发现,一段时间内,每次执行top -bn1
命令,得到的结果数据,是一样的,但是通过其他方式查看资源使用率,确实是一直在变化的。这其实是Linux系统的一个bug
,在 Mac OS 下使用 top 命令就没有这个问题。因此,我们无法使用top命令第一次的执行结果来提取数据,必须要执行多次。命令再一次修改为top -bn2
,这样shell就会返回两次结果。但是还有一个问题,这两次结果返回,间隔时间比较长,我们还需要再加上一个参数,将命令改为top -bn2 -d 0.1
,将结果刷新间隔设为0.1秒,这样shell就会在第0秒返回第一次结果,0.1秒返回第二次结果。这个时间可以按需要设置,程序需要配合该命令做出异步处理。
本文以分析需求、整理思路和实现方法为主,关于 top 命令的详解,请参看本文最后的【附】或者互联网上其他资料。
直接看代码
local function excute_cmd(cmd)
local t = io.popen(cmd)
local ret = t:read("*all")
return ret
end
io.popen(cmd)
用于执行命令,t:read("*all*")
用于获取shell的完整输出结果
local function get_system_info()
if system_info == nil or system_info == "" then
local cmd = "top -bn 2 -i -c -d 0.1"
local output = excute_cmd(cmd)
-- top result fisrt line is not correct on linux, use second line
local i, j = string.find( output, "%s\ntop.*" )
local ret = string.sub(output, i, j)
system_info = ret
end
end
匹配指定模式的字符串
local function string_match(str, patten)
local i, j = string.find(str, patten)
local ret = string.sub(str, i, j)
return ret
end
匹配指定模式字符串中的数字
local function string_match_num(str, patten)
local ret = string_match(str,patten)
local i, j = string.find(ret, "[0-9]+%.*[0-9]*")
local num = string.sub(ret, i, j)
return num
end
如此,通过调用 string_match_num(system_info, "[0-9]+%.?[0-9]*%sus,")
,就可以提取出 Cpu(s) 0.3 us ....
中的 0.3
。
Linux下CPU的使用率,由几个部分组成:用户空间时间 us
、用户进程时间 ni
、内核时间 sy
、空闲时间 id
、软件中断时间 si
、硬件中断时间 hi
、虚拟机消耗时间 st
、等待输入时间 wa
。
根据以上参数,给出一个计算CPU使用率的公式:
cpu_usage = (us + ni + sy + si + hi + st + wa) / (us + ni + sy + si + hi + st + wa + id)
因此,可以写出如下代码:
local function get_cpu_usage()
local cpu_user = string_match_num(system_info, "[0-9]+%.?[0-9]*%sus,")
local cpu_system = string_match_num(system_info, "[0-9]+%.?[0-9]*%ssy,")
local cpu_nice = string_match_num(system_info, "[0-9]+%.?[0-9]*%sni,")
local cpu_idle = string_match_num(system_info, "[0-9]+%.?[0-9]*%sid,")
local cpu_wait = string_match_num(system_info, "[0-9]+%.?[0-9]*%swa,")
local cpu_hardware_interrupt = string_match_num(system_info, "[0-9]+%.?[0-9]*%shi,")
local cpu_software_interrupt = string_match_num(system_info, "[0-9]+%.?[0-9]*%ssi,")
local cpu_steal_time = string_match_num(system_info, "[0-9]+%.?[0-9]*%sst")
local cpu_total = cpu_user + cpu_nice + cpu_system + cpu_wait + cpu_hardware_interrupt + cpu_software_interrupt + cpu_steal_time + cpu_idle
local cpu_time = cpu_user + cpu_nice + cpu_system + cpu_wait + cpu_hardware_interrupt + cpu_software_interrupt + cpu_steal_time
local cpu_usage = time / total
return cpu_usage
end
system_info
是上文中通过 get_system_info()
得到的结果。
原理同上,mem_usage = mem_used / mem_total
直接给出代码:
local function get_mem_usage()
local mem_total = string_match_num(system_info, "Mem[%d%p%s]*[0-9]+%stotal")
local mem_used = string_match_num(system_info, "free[%d%p%s]*[0-9]+%sused")
local mem_usage = mem_used / mem_total
return mem_usage
end
[附]
Top 命令详解在top命令中按f按可以查看显示的列信息,按对应字母来开启/关闭列,大写字母表示开启,小写字母表示关闭。带*号的是默认列。