这是一篇学习笔记。用于快速理解,并在Raspbian(或者其他Linux系统)上,利用Python的Flask框架快速写出一份简易的网速监控API。
Table of Contents
0 序言
我目前成为了树莓派的重度使用者。平时Python的练习可以在上面做,同时它也是我的NAS(简易版)、离线下载机、无线路由器……
对于我来说,对某台机器网速的监控有很大的需求,因此之前尝试利用基于PHP的探针提供的API来监控实时网速。
https://steinslab.io/archives/1164
PHP探针在建站环境上的主机会非常方便。但是对于没有PHP环境的主机,专门为了一个探针而配置PHP,则过于臃肿。
今天写了这篇文章,同时也作为我自己的笔记,用Python的Flask框架快速写出一份简易的网速监控API。
进行操作后,不只是网速监控API,可以根据自身需求推广到其他API的快速搭建。
我们最终的目标是,HTTP访问目标ip:端口,得到json形式的返回结果:
{ "interface": "eth0", "rx": "58829838538", "time": "1506500429.11", "tx": "59272128479" }
包含网口名,rx和tx字节数,此时的时间戳(api提供端无状态,只提供时间戳。网速信息在api获取端运算)。
注:JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式。它基于 ECMAScript (w3c制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
1 了解 /proc
Linux 内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构、改变内核设置的机制。proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。
pi@raspberrypi:/proc $ ls 1 1158 1323 1452 1673 2057 2474 45 576 66 77 8032 904 bus interrupts modules timer_list 10 1180 1336 146 1674 2058 25 46 579 67 7791 8035 912 cgroups iomem mounts timer_stats 1009 1184 1350 1460 1691 21 26 47 58 670 7797 8097 915 cmdline ioports net tty 104 1187 1356 148 1695 2122 27 48 580 68 78 8134 916 consoles irq pagetypeinfo uptime 1076 124 1357 1482 17 2126 273 49 582 69 7804 8136 917 cpu kallsyms partitions vc-cma 1077 1259 1358 15 1736 22 28 5 59 6948 7811 82 918 cpuinfo keys sched_debug version 1078 1260 1361 1536 1742 222 29 50 60 7 7813 83 919 crypto key-users self vmallocinfo 1079 1269 1363 1585 1777 223 3 51 61 70 79 85 922 devices kmsg slabinfo vmstat 108 1272 1364 1625 1780 224 31 52 619 71 7901 879 932 device-tree kpagecgroup softirqs zoneinfo 1081 1290 1365 1626 18 228 32 53 62 72 7936 88 933 diskstats kpagecount stat 1085 1291 1378 1646 1813 2288 322 54 63 73 7940 89 937 driver kpageflags swaps 1097 13 1379 1648 1871 23 33 55 64 738 8 892 946 execdomains loadavg sys 11 1309 14 1651 19 238 34 56 65 75 80 898 960 fb locks sysrq-trigger 1108 1312 1402 1652 2 239 35 57 658 7669 8025 9 asound filesystems meminfo sysvipc 1154 1321 1442 1668 2056 24 44 575 659 7691 8030 90 buddyinfo fs misc thread-self
详情可参考 Linux下/proc目录简介 [1]
对于状态监控,我们一般关心的信息如下:
/proc/cpuinfo cpu的信息
/proc/loadavg 根据过去一段时间内CPU和IO的状态得出的负载状态
/proc/meminfo RAM使用的相关信息
/proc/uptime 系统已经运行了多久
/proc/net 网卡设备信息
/proc/net/dev 显示网络适配器及统计信息
/proc/diskstats 取得磁盘信息
这样,我们就可以各取所需。在本例中,我使用了/proc/net/dev 显示网络适配器及统计信息。
Inter-| Receive | Transmit face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed tun0: 3852102709 17680346 0 0 0 0 0 0 1974345633 21150157 0 182375 0 0 0 0 wlan0: 7886340 51924 0 4 0 0 0 248 15654713 23263 0 0 0 0 0 0 lo: 1109576 6222 0 0 0 0 0 0 1109576 6222 0 0 0 0 0 0 eth0: 1357382867 17758879 0 32317 0 0 0 0 354351259 21157222 0 0 0 0 0 0 eth1: 145114110 1581538 0 0 0 0 0 0 2151465834 4647523 0 0 0 0 0 0
/proc/net/dev就是我们流量监控的数据来源。
我关心eth0的网卡流量。因此我只要抓出eth0(第六行)行的RX字节数和TX字节数即可,再在api中返回此时的时间戳。
对于 /proc/net/dev 文件中我关心的信息,我使用的部分Python代码如下。
f = open('/proc/net/dev','r') for x in range(5): f.readline() line = f.readline() strline = line.split(' ') strline2 = [] for a in strline: if a != ' ': if a !='': strline2.append(a)
至此,我们得到的strline2就是eth0行中包含的数据列表。根据/proc/net/dev 格式,rx数据量是strline2[1],tx数据量是strline2[9]。当然,你也可以用正则表达式来实现。
毕竟是自己使用的简易API,如果不是特别在意的话,实现方法不优雅也无妨。
至此我们就能把自己需要的信息提取出来了
2 熟悉 flask 框架
Flask是一个使用Python编写的轻量级Web应用框架。基于Werkzeug WSGI工具箱和Jinja2 模板引擎。 Flask使用BSD授权。 Flask也被称为“microframework”,因为它使用简单的核心,用extension增加其他功能。Flask没有默认使用的数据库、窗体验证工具。然而,Flask保留了扩增的弹性,可以用Flask-extension加入这些功能:ORM、窗体验证工具、文件上传、各种开放式身份验证技术。
Flask有提供文档 http://docs.jinkan.org/docs/flask/
用简单的话说,Flask可以让你快速搭建起来一个web服务端,而省去一些技术细节。
不要误解,Flask的能力足以支撑大型的商业项目。但由于其轻量易上手,使得我们也可以在自己的项目中轻松使用。我们来看一下其helloworld代码。
from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello World!"
如你所见,短短几行代码实现了基本的web静态服务。
我们将上面获取网速的代码加入,就可以实现我们的API功能。
具体操作如下:
安装flask
sudo pip3 install flask
在我们的目录下新建一个app.py并编辑
touch app.py nano app.py
写入以下代码
#!flask/bin/python from flask import Flask app = Flask(__name__) @app.route('/') def index(): return "Hello, World!" if __name__ == '__main__': app.run(host='0.0.0.0',port='5050',debug=True)
注意,相比helloworld,最后一行代表的含义是:在0.0.0.0(本地/不检查ip)上开放,端口5050,开启调试,然后执行
pi@raspberrypi:~/dev/monitor $ python3 app.py * Running on http://0.0.0.0:5050/ * Restarting with reloader
此时访问 http://localhost:5050 或者 http://目标机器ip:5050 即可返回HelloWorld。(访问失败,注意防火墙设置)
具体特性如调试、路由等等使用方法,本文不多提及,请参照官方文档。
3 写出服务端
我们最终的目标是,HTTP访问目标ip:端口,得到json形式的返回结果:
{ "interface": "eth0", "rx": "58829838538", "time": "1506500429.11", "tx": "59272128479" }
接下来将上面两部分结合,写出服务端即可。我的代码如下:
#!flask/bin/python from flask import Flask, jsonify import time app = Flask(__name__) net = { 'interface':'eth0', 'rx':'0', 'tx':'0', 'time': '0' } @app.route('/') def index(): try: f = open('/proc/net/dev','r') f.readline() f.readline() f.readline() f.readline() line = f.readline() strline = line.split(' ') strline2 = [] for a in strline: if a != ' ': if a !='': strline2.append(a) net['rx']=strline2[1] net['tx']=strline2[9] net['time']=str(time.time()) finally: if f: f.close() strline=[] strline2=[] return jsonify(net) if __name__ == '__main__': app.run(host='0.0.0.0',port=5050,debug=True)
说明:
1 jsonify是flask提供的快速打包工具。使用jsonify(net),即可将net打包为json,然后直接返回即可。
2 成功实现后,请取消debug。
我提供一个示例:
http://sfo01.misaka.cc:5050/
访问得到
{ "interface": "eth0", "rx": "58829838538", "time": "1506500429.11", "tx": "59272128479" }
关于此api的利用,可以参阅我之前的文章。定期刷新获取json,解析后,两次请求数据量差与time时间戳之差之商即为网速。
https://steinslab.io/archives/1164
4 持久运行与使用
可以使用一切使进程持续运行的方法。
可以使用tmux
sudo apt-get install tmux
开启新对话时使用
tmux
即可
在新的会话中使用
python app.py
后,终端即可直接关闭。返回会话可使用
tmux attach
直接返回
5 小结
至此。我们使用flask完成了一个最简单的API的搭建。下一步可以考虑更广的用途。
最后,笔者为生活在一个不需要重复造轮子的时代而感到幸运。
6 相关阅读
[1] Linux下/proc目录简介 http://blog.csdn.net/zdwzzu2006/article/details/7747977
[2] 百度百科-JSON https://baike.baidu.com/item/JSON
[2] Flask框架中文文档 http://docs.jinkan.org/docs/flask/
请问 为什么我打不开proc/net/dev目录呀 说是权限不够
@ifuuuuuun 看到你的邮件了。使用sudo可以提权
@SPtuan 请问你这个里面的eth1是哪里的啊
@SPtuan 请问如果树莓派与路由器相连,能通过你这个方法提取路由器的流量数据吗
@ifuuuuuun 实际上提取的就是你指定网卡ethX的流量数据
站主该更新了
@催更的fandy 有啥好的题材建议么
@SPtuan 明天万圣节,来个游戏roll楼呗
@fandy 谁催更谁roll,你可以在自己博客roll4份橘子汁,18块钱
@SPtuan 我猜送不出去2333我好友里没这么多有steam账号的,还是交给你吧
@fandy 放心吧roll游戏还能roll不出去,快发
@SPtuan 圣诞节再说吧,现在没这个心情
@fandy 成交
博主这是要深入学习python了么
@hen Python能做的事太多了,想随便深入一项都太难了