20分钟理解并写出简易的网速监控API

这是一篇学习笔记。用于快速理解,并在Raspbian(或者其他Linux系统)上,利用Python的Flask框架快速写出一份简易的网速监控API。

0 序言

我目前成为了树莓派的重度使用者。平时Python的练习可以在上面做,同时它也是我的NAS(简易版)、离线下载机、无线路由器……

对于我来说,对某台机器网速的监控有很大的需求,因此之前尝试利用基于PHP的探针提供的API来监控实时网速

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并编辑

#!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,开启调试,然后执行。

此时访问 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时间戳之差之商即为网速。

4 持久运行与使用

可以使用一切使进程持续运行的方法。

可以使用tmux

开启新对话时使用 tmux

在新的会话中使用

sudo python app.py

终端即可直接关闭

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/

原文来自 https://steinslab.xyz/archives/1275

 

发表评论

您的电子邮箱地址不会被公开。

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据