10min手寫(一):伺服器內存監控系統

本文作者shengxinjing (woniuppp) · GitHub ,授權發布

本文需要有一定的Python和前端基礎,如果沒基礎的,請關注我後續的基礎教程系列博客

項目地址,可以看到具體的代碼,喜歡請加個星星

視頻鏈接

錄製中間網出問題了,重啟了一下,所以有兩部分

  • 視頻1
  • 視頻2

本文的目的在於,儘可能用簡單的代碼,讓大家了解內存監控的原理

主題思路

  • 獲取內存信息
  • 存儲信息
  • 展現
  • 擴展
    • 加主機名,moitor部署在多台機器,不直接插資料庫
    • 通過http請求的方式,一台機器起flask專門存數據monitor

思路圖

第一步,我們需要獲取內存信息

其實所有的監控項,包括內存數據,都是從文件中讀取的,大家執行以下 cat /proc/meminfo就可以看到關於內存的信息,我們關注的是前四行,總內存,空閑內存,緩衝和緩存大小

計算內存佔用量公式:

(總內存-空閑內存-緩衝-緩存)/1024Mb

代碼呼之欲出 monitor.py

用with打開文件,可以自動關閉,比直接open優雅那麼一丟丟

def getMem(): with open(/proc/meminfo) as f: total = int(f.readline().split()[1]) free = int(f.readline().split()[1]) buffers = int(f.readline().split()[1]) cache = int(f.readline().split()[1]) mem_use = total-free-buffers-cache print mem_use/1024while True: time.sleep(1) getMem()

執行文件 python monitor.py,每一秒列印一條內存信息

[woniu@teach memory]$ python mointor.py 29202919291929192919

我們可以寫個很搓的測試代碼,佔用一點內存,看看數據會不會變

執行下面代碼,能看到內存使用量明顯多了幾M

# test.pys = akdsakjhdjkashdjkhasjkdhasjkdhkjashdaskjhfoopnnm,ioqouiew*100000for i in s: for j in s: s.count(j)~

獲取內存數據done!

第二部存儲資料庫

###我們選用mysql

新建表格,我們需要兩個欄位,內存和時間 sql呼之欲出,簡單粗暴

create memory(memory int,time int)

我們的 monitor.py就不能只列印內存信息了,要存儲資料庫啦,引入mysql模塊,代碼如下

import timeimport MySQLdb as mysqldb = mysql.connect(user="reboot",passwd="reboot123",db="memory",host="localhost")db.autocommit(True)cur = db.cursor()def getMem(): with open(/proc/meminfo) as f: total = int(f.readline().split()[1]) free = int(f.readline().split()[1]) buffers = int(f.readline().split()[1]) cache = int(f.readline().split()[1]) mem_use = total-free-buffers-cache t = int(time.time()) sql = insert into memory (memory,time) value (%s,%s)%(mem_use/1024,t) cur.execute(sql) print mem_use/1024 #print okwhile True: time.sleep(1) getMem()

比之前的多了拼接sql和執行的步驟,具體過程見視頻,大家到資料庫里執行一下下面的sql,就能看到我們辛辛苦苦獲取的內存數據啦

select * from memory

我們的資料庫里數據越來越多,怎麼展示呢

我們需要flask

我們看下文件結構

.├── flask_web.py web後端代碼├── mointor.py 監控數據獲取├── static 靜態文件,第三方圖表庫│ ├── exporting.js│ ├── highstock.js│ └── jquery.js├── templates│ └── index.html 展示前端頁面└── test.py 佔用內存的測試代碼

flask_web就是我們的web服務代碼,template下面的html,就是前端展示的文件,static下面是第三方庫

flask_web的代碼如下

  • 提供兩個路由
    • 根目錄渲染文件index.html
    • /data路由去資料庫插數據,返回json,供畫圖使用

from flask import Flask,render_template,requestimport MySQLdb as mysqlcon = mysql.connect(user=reboot,passwd=reboot123,host=localhost,db=memory)con.autocommit(True)cur = con.cursor()app = Flask(__name__)import json@app.route(/)def index(): return render_template(index.html)@app.route(/data)def data(): sql = select * from memory cur.execute(sql) arr = [] for i in cur.fetchall(): arr.append([i[1]*1000,i[0]]) return json.dumps(arr)if __name__==__main__: app.run(host=0.0.0.0,port=9092,debug=True)

前端index.html

highstock的demo頁面,copy過來,具體過程見視頻

<html><head><title>51reboot</title></head><body>hello world<div id="container" style="height: 400px; min-width: 310px"></div><script src=/static/jquery.js></script><script src=/static/highstock.js></script><script src=/static/exporting.js></script><script>$(function () { // 使用當前時區,否則東八區會差八個小時 Highcharts.setOptions({ global: { useUTC: false } }); $.getJSON(/data, function (data) { // Create the chart $(#container).highcharts(StockChart, { rangeSelector : { selected : 1 }, title : { text : 內存數據 }, series : [{ name : 本機內存, data : data, tooltip: { valueDecimals: 2 } }] }); });});</script></body></html>

具體觀察數據結構的過程,見視頻和demo鏈接,我們做的 就是把資料庫里的數據,拼接成前端畫圖需要的數據,展現出來

這時候前端就能看到圖表啦

我們並不僅限於此,如果想實時的看到內存,應該怎麼搞呢

  • 查詢數據時候增加一個時間戳當限制條件,再次查詢時,只返回兩次查詢之間的增量數據
  • 前端動態添加增量結點數據到圖表中
  • 代碼呼之欲出

python

tmp_time = 0@app.route(/data)def data(): global tmp_time if tmp_time>0: sql = select * from memory where time>%s % (tmp_time/1000) else: sql = select * from memory cur.execute(sql) arr = [] for i in cur.fetchall(): arr.append([i[1]*1000,i[0]]) if len(arr)>0: tmp_time = arr[-1][0] return json.dumps(arr)

前端,3秒查一次增量數據

$.getJSON(/data, function (data) { // Create the chart $(#container).highcharts(StockChart, { chart:{ events:{ load:function(){ var series = this.series[0] setInterval(function(){ $.getJSON(/data,function(res){ $.each(res,function(i,v){ series.addPoint(v) }) }) },3000) } } }, rangeSelector : { selected : 1 }, title : { text : AAPL Stock Price }, series : [{ name : AAPL, data : data, tooltip: { valueDecimals: 2 } }] }); });

done!兩個文件都搞定,double kill!

效果

最終代碼直接下載那個木看也行

監控文件monitor.py

import timeimport MySQLdb as mysqldb = mysql.connect(user="reboot",passwd="reboot123",db="memory",host="localhost")db.autocommit(True)cur = db.cursor()def getMem(): f = open(/proc/meminfo) total = int(f.readline().split()[1]) free = int(f.readline().split()[1]) buffers = int(f.readline().split()[1]) cache = int(f.readline().split()[1]) mem_use = total-free-buffers-cache t = int(time.time()) sql = insert into memory (memory,time) value (%s,%s)%(mem_use/1024,t) cur.execute(sql) print mem_use/1024 #print okwhile True: time.sleep(1) getMem()

flask

from flask import Flask,render_template,requestimport MySQLdb as mysqlcon = mysql.connect(user=reboot,passwd=reboot123,host=localhost,db=memory)con.autocommit(True)cur = con.cursor()app = Flask(__name__)import json@app.route(/)def index(): return render_template(index.html)tmp_time = 0@app.route(/data)def data(): global tmp_time if tmp_time>0: sql = select * from memory where time>%s % (tmp_time/1000) else: sql = select * from memory cur.execute(sql) arr = [] for i in cur.fetchall(): arr.append([i[1]*1000,i[0]]) if len(arr)>0: tmp_time = arr[-1][0] return json.dumps(arr)if __name__==__main__: app.run(host=0.0.0.0,port=9092,debug=True)

前端

<html><head><title>51reboot</title><meta charset=utf-8></head><body>hello world<div id="container" style="height: 400px; min-width: 310px"></div><script src=/static/jquery.js></script><script src=/static/highstock.js></script><script src=/static/exporting.js></script><script>$(function () { // 使用當前時區,否則東八區會差八個小時 Highcharts.setOptions({ global: { useUTC: false } }); $.getJSON(/data, function (data) { // Create the chart $(#container).highcharts(StockChart, { chart:{ events:{ load:function(){ var series = this.series[0] setInterval(function(){ $.getJSON(/data,function(res){ $.each(res,function(i,v){ series.addPoint(v) }) }) },3000) } } }, rangeSelector : { selected : 1 }, title : { text : 內存數據 }, series : [{ name : 本機內存, data : data, tooltip: { valueDecimals: 2 } }] }); });});</script></body></html>

代碼沒有特別注意細節,希望大家喜歡

運維開發交流QQ群:238757010

歡迎大家關注公共號,高品質運維開發

本文僅授權51Reboot相關賬號發布。

推薦閱讀:

為什麼 x in range(1000000000000001) 的執行速度這麼快
如何看待將Python代碼轉換成Go代碼並進一步編譯的 Grumpy 項目?
基於ArcGIS的python編程:2.python基礎(一)
不再寫 for 循環

TAG:运维 | Python | 系统架构 |