首先我们先了解一下Linux系统获取网络流量的几种方法
1.基于sysfs虚拟文件系统,这是由内核用来将设备或驱动相关的信息输出到用户空间的一种机制。网络接口的相关分析数据会通过“/sys/class/net/<ethX>/statistics”输出。
加之Linux提供了LKM机制可以使我们在内核空间工作,在LKM机制中一个重要的组成部分就是proc伪文件系统,它为用户提供了动态操作Linux内核信息的接口,是除系统调用之外另一个重要的Linux内核空间与用户空间交换数据的途径。
如:
- /sys/class/net/eth0/statistics/rx_packets: 收到的数据包数据
- /sys/class/net/eth0/statistics/tx_packets: 传输的数据包数量
- /sys/class/net/eth0/statistics/rx_bytes: 接收的字节数
- /sys/class/net/eth0/statistics/tx_bytes: 传输的字节数
- /sys/class/net/eth0/statistics/rx_dropped: 当收到包数据包下降的数据量
- /sys/class/net/eth0/statistics/tx_dropped: 传输包数据包下降的数据量
2.sar
sar命令包含在sysstat工具包中,提供系统的众多统计数据。其在不同的系统上命令有些差异,某些系统提供的sar支持基于网络接口的数据统计,也可以查看设备上每秒收发包的个数和流量。
3./proc/net/dev
本文使用的是方法1,即为通过定时读取/sys/class/net/eth0/statistics/xxx文件的内容,来进行计算,获取最终的网络带宽
使用lua 的io库进行读文件操作
file_rx = io. open ( "/sys/class/net/eth0/statistics/rx_bytes" , "r" )
file_tx = io. open ( "/sys/class/net/eth0/statistics/tx_bytes" , "r" )
local tx1 = file_tx : read ( "*l" )
local rx1 = file_rx : read ( "*l" )
获得秒初的网络数据包数量
进而通过ngx.sleep进行非阻塞的等待一秒后
再次读取文件中的数据包数量
ngx.sleep (1)
file_rx :seek( "set" )
file_tx :seek( "set" )
local tx2 = file_tx : read ( "*l" )
local rx2 = file_rx : read ( "*l" )
这样,获得了秒末的数据包数量
将秒末的数据包数量减去秒初的数据包数量。即为秒内的网络流动数据包数量
因为每个数据大小为8个字节,因此,得出的结果*8就是每秒字节数,进而可以通过多次除去1024得道千字节数,兆字节数等
local tx = (tx2-tx1)*8
local rx = (rx2-rx1)*8
--and
--tx = tx/1024/1024 --mbps
--rx = rx/1024/1024 --mbps
--核心代码实现
function read_network(types)
file_rx = io. open ( "/sys/class/net/eth0/statistics/rx_bytes" , "r" )
file_tx = io. open ( "/sys/class/net/eth0/statistics/tx_bytes" , "r" )
local tx1 = file_tx : read ( "*l" )
local rx1 = file_rx : read ( "*l" )
ngx.sleep (1)
file_rx :seek( "set" )
file_tx :seek( "set" )
local tx2 = file_tx : read ( "*l" )
local rx2 = file_rx : read ( "*l" )
local tx = (tx2-tx1)*8
local rx = (rx2-rx1)*8
mb = {time=ngx.now()}
if (types == 1)
then
if (rx<10000)
then
mb["rx"]=rx
mb["rx_mode"]="bps"
elseif(rx<10000000)
then
rx = rx/1024
mb["rx"]=rx
mb["rx_mode"]="Kbps"
elseif(rx<10000000000)
then
rx = rx/1024/1024
mb["rx"]=rx
mb["rx_mode"]="Mbps"
elseif(rx<10000000000000)
then
rx = rx/1024/1024/1024
mb["rx"]=rx
mb["rx_mode"]="Gbps"
end
if (tx<10000)
then
mb["tx"]=tx
mb["tx_mode"]="bps"
elseif(tx<10000000)
then
tx = tx/1024
mb["tx"]=tx
mb["tx_mode"]="Kbps"
elseif(tx<10000000000)
then
tx = tx/1024/1024
mb["tx"]=tx
mb["tx_mode"]="Mbps"
elseif(tx<10000000000000)
then
tx = tx/1024/1024/1024
mb["tx"]=tx
mb["tx_mode"]="Gbps"
end
elseif(types == 2)
then
mb["tx"]=tx/1024/1024
mb["tx_mode"]="Mbps"
mb["rx"]=rx/1024/1024
mb["rx_mode"]="Mbps"
else
mb["tx"]=tx
mb["tx_mode"]="bps"
mb["rx"]=rx
mb["rx_mode"]="bps"
end
file_rx :close()
file_tx :close()
return mb
end
因为将要使用websocket进行实时的数据传输,我们需要安装支持resty.websocket的扩展库,openresty用户默认就有,非openresty用户可前往https://github.com/openresty/lua-resty-websocket安装库
安装过程可参考https://www.cnblogs.com/scotoma/p/3330190.html
websocket相关代码
local server = require "resty.websocket.server"
local wb, err = server:new{
timeout = 50000, -- in milliseconds
max_payload_len = 6553500,
}
if not wb then
ngx.log(ngx.ERR, "failed to new websocket: ", err)
return ngx.exit(444)
end
bytes, err = wb:send_text(json.encode({Msg="Connect OK,Service will traceback the traffic data soon",code = "OK"}))
if not bytes then
ngx.log(ngx.ERR, "failed to send a text frame: ", err)
end
while (1)
do
bytes, err = wb:send_text(json.encode(read_network(types,pod)))
if not bytes then
ngx.log(ngx.ERR, "failed to send a text frame: ", err)
end
end
全部完整代码
json = require "cjson"
ngx.update_time()
local server = require "resty.websocket.server"
local wb, err = server:new{
timeout = 50000, -- in milliseconds
max_payload_len = 6553500,
}
if not wb then
ngx.log(ngx.ERR, "failed to new websocket: ", err)
return ngx.exit(444)
end
function read_network(types)
file_rx = io. open ( "/sys/class/net/eth0/statistics/rx_bytes" , "r" )
file_tx = io. open ( "/sys/class/net/eth0/statistics/tx_bytes" , "r" )
local tx1 = file_tx : read ( "*l" )
local rx1 = file_rx : read ( "*l" )
ngx.sleep (1)
file_rx :seek( "set" )
file_tx :seek( "set" )
local tx2 = file_tx : read ( "*l" )
local rx2 = file_rx : read ( "*l" )
local tx = (tx2-tx1)*8
local rx = (rx2-rx1)*8
mb = {time=ngx.now()}
if (types == 1)
then
if (rx<10000)
then
mb["rx"]=rx
mb["rx_mode"]="bps"
elseif(rx<10000000)
then
rx = rx/1024
mb["rx"]=rx
mb["rx_mode"]="Kbps"
elseif(rx<10000000000)
then
rx = rx/1024/1024
mb["rx"]=rx
mb["rx_mode"]="Mbps"
elseif(rx<10000000000000)
then
rx = rx/1024/1024/1024
mb["rx"]=rx
mb["rx_mode"]="Gbps"
end
if (tx<10000)
then
mb["tx"]=tx
mb["tx_mode"]="bps"
elseif(tx<10000000)
then
tx = tx/1024
mb["tx"]=tx
mb["tx_mode"]="Kbps"
elseif(tx<10000000000)
then
tx = tx/1024/1024
mb["tx"]=tx
mb["tx_mode"]="Mbps"
elseif(tx<10000000000000)
then
tx = tx/1024/1024/1024
mb["tx"]=tx
mb["tx_mode"]="Gbps"
end
elseif(types == 2)
then
mb["tx"]=tx/1024/1024
mb["tx_mode"]="Mbps"
mb["rx"]=rx/1024/1024
mb["rx_mode"]="Mbps"
else
mb["tx"]=tx
mb["tx_mode"]="bps"
mb["rx"]=rx
mb["rx_mode"]="bps"
end
file_rx :close()
file_tx :close()
return mb
end
local types = tonumber(ngx.var.arg_types) or 0
local pod = ngx.var.arg_pod or "tx"
bytes, err = wb:send_text(json.encode({Msg="Connect OK,Service will traceback the traffic data soon",code = "OK"}))
if not bytes then
ngx.log(ngx.ERR, "failed to send a text frame: ", err)
end
while (1)
do
bytes, err = wb:send_text(json.encode(read_network(types,pod)))
if not bytes then
ngx.log(ngx.ERR, "failed to send a text frame: ", err)
end
end
详细项目地址https://gitee.com/daofengql/lua-websocket-real-time-network-traffic
到此,lua部分已经编写完毕,接下来就要在nginx中配置配置文件来接入路由
样例conf
location / { #路由名称具实际修改
#lua_code_cache off;
default_type text/html;
add_header 'Access-Control-Allow-Origin' *;
content_by_lua_file /data/traffic.lua;#文件存放地址更具实际修改
}
配置完成后,如果不出意外,重载NGINX之后,可以使用在线websocket测试,测试服务
推送出去,客户端收到的是json字符,可以再对其转化为字典对象等
可以进一步将项目仓库内的html目录下的页面进行适当的修改和部署
计数器核心功能代码
<script language="JavaScript">
$(function () {
var chart;
var previous = null;
var chart; // global
var pointx = null;
var ip = "180.188.16.147";
$(window).load(function () {
initiateChart("L4dstat");
//parseFile();
});
var server = 'wss://ws.jcdpn.cn/?types=2';
var ws = new WebSocket(server);
var temp_arr = [0.1];
var max_bps = 0.1;
ws.onclose = function () {
reconnect(service);
};
ws.onmessage = function (evt) {
data = JSON.parse(evt.data);
temp_arr.push(data.rx);
max_bps = Math.max.apply(null, temp_arr);
temp_arr = [0.1];
var series = chart.series[0],
shift = series.data.length > 20;
chart.series[0].addPoint([Math.floor($.now()), max_bps], true, shift);
max_bps = 0.1;
};
function initiateChart(divid) {
var options = {
plotOptions: {
series: {
events: {
legendItemClick: function (event) {
event.preventDefault();
}
}
}
},
chart: {
zoomType: '',
renderTo: divid,
style: {
fontFamily: "'Unica One', sans-serif"
},
//plotBorderColor: '#606063'
backgroundColor: '#e2e3e5',
},
//title:{
// text: null
// },
title: {
text: '» '+ip+'核心流量计数器 «',
},
xAxis: {
type: 'datetime',
dateTimeLabelFormats: {
day: '%a'
}
},
yAxis: {
minPadding: 0.2,
maxPadding: 0.2,
title: {
text: 'Mb/秒',
margin: 80
}
},
credits: {
enabled: false
},
series: [{
type: 'area',
//shadowSize: 0,
name: 'Mbps/秒',
color: '#164791',
data: []
}]
};
chart = new Highcharts.Chart(options);
}
});
</script>
例如将
默认的socket地址改成自己的
流量计数器默认使用的单位为mbps,其他单位还需要自己修改和换算
流量计数器使用highcharts.js来进行表格绘制
效果
评论 (0)