經過之前的學習 《 Python 系統資源資訊獲取工具,你用過沒?》、《【一】從0開始,用flask+mongo打造分散式伺服器監控平臺》, 召喚師峽谷萌新 已經可以啟動一個 Web 頁面了,並且已經通過 MongoEngine 定義了一個 ORM。接下來我們應該對每個模組功能進行編寫,並且為每個編寫好的模組編寫檢視。今天我們將編寫功能模組,並在程式碼開始之前對功能模組進行分析,並通過流程圖和 UML 類圖的繪製將模組功能細化,接著從搭建骨架開始,逐步完成一個模組的編寫。
模組編寫順序
思考:在多個模組當中,萌新 應該先完成哪一個模組呢?
我們給需求進行排序,看看他們各自的權重和依賴關係,回顧一下我們需要編寫的模組:
- 資料處理與視覺化
- 資訊監控
- 警報模組
- 資料儲存
- API 檢視
奎因 畫了一張圖,從這張圖上我們就可以看出每個模組的依賴和作用:
首先應該先讀取每臺伺服器的資源,在讀取資源的時候檢查是否超過閾值,然後主機一次性獲取所有伺服器資源資訊並存入資料庫,接著出庫計算並將資料處理成視覺化圖表。所以可以將上面的模組編寫順序做一下調整:
- 資訊監控
- 資料儲存
- 資料處理與視覺化
至於剩下的警報模組和 API 檢視,在前三個模組開發過程中用於除錯,可以只保留邏輯佔位(也就是 python 中常用的 Pass)待前三個模組開發完成後再回頭補充警報模組和 API 檢視的邏輯即可。
資訊監控模組的實現
在 《Python 系統資源資訊獲取工具,你用過沒?》 中,我們已經學會了(非 windows)伺服器資源資訊的獲取,現在我們需要將之前所學的知識轉化成為功能模組,也就是在程式碼中通過類來實現資源資訊獲取,這樣我們就可以通過類的例項化和方法呼叫的方式獲取的所需的資料。
思考:在弄清楚需求、作用之後,我們就可以開始編寫程式碼了嗎?
如果你是一名經驗豐富的開發者,想必你心中已經有了程式碼的一個大致結構。但是作為萌新,我們還沒能夠那麼快在腦海中生成結構,所以我們還是需要畫圖。在程式碼開始前進行構思、畫圖可以讓萌新們減少錯誤、同時也減少程式碼的改動次數,有一定的機率提升,既然現在是編寫類,那麼我們就來繪製一個 UML 類圖吧!
首先,我們應該給類想一個名字,就叫 PresentMomentSystemResource 吧,所以一個空的 UML 圖就可以畫出來了:
至於裡面的方法,我們想一下:
- 應該有一個 init 方法,這樣就可以在類例項化的時候指定一些類變數;
- 對於 cpu、記憶體硬碟以及程式的資料,應該有不同的方法進行獲取;
- 考慮到這個類可能會被多次例項化,在多個地方被呼叫,那麼就有可能需要使用單例模式;
所以 UML 圖改動一下:
奎因 給大家解釋一下這個 UML 類圖的含義:
- 魔術方法 new 來完成類單例模式
- 魔術方法 init 設定例項化時使用的一些類變數
- 接下來給每個硬體資訊資料定義一個方法,並且的到的結果都以 dict 的資料型別返回
然後我們就可以開始程式碼的編寫了。
對於這一次的專案,我們新建一個資料夾 Monitors,然後再在裡面新建一個 python package 名字叫 monitors,接著在專案內新建一個名為 core.py 的檔案,結構如下圖所示:
並且根據 UML 類圖編寫類的基礎結構:
# 崔慶才丨靜覓、韋世東丨奎因 邀請你關注微信公眾號【進擊的Coder】和大佬一起coding 共同進步
class PresentMomentSystemResource:
def __new__(cls, *args, **kwargs):
# singleton
pass
def __init__(self):
pass
def memory_usage(self):
"""當前時刻記憶體用量資訊
"""
pass
def cpu_usage(self):
""" 當前時刻cpu用量資訊 """
pass
def disks_usage(self):
""" 當前時刻根目錄磁碟用量資訊 """
pass
def processes_id(self):
""" 篩選當前時刻關鍵字相關的pid列表及數量 """
pass
複製程式碼
搭好類的骨架之後,我們就來為每個方法編寫實際的程式碼。
首先,我們用魔術方法 new 將類變成單例模式,所以 new 方法部分的程式碼改為:
def __new__(cls, *args, **kwargs):
# singleton
if not hasattr(cls, '_instance'):
cls._instance = super(PresentMomentSystemResource, cls).__new__(cls, *args, **kwargs)
return cls._instance
複製程式碼
作為老司機,應該是可以在編寫完程式碼開始下一個方法的編寫。但是現在我們是 召喚師峽谷的萌新 ,對不對?那 萌新 肯定是不知道自己寫的程式碼是否正確,所以我們需要編寫測試程式碼,只要確定類例項化後確實只有一個例項物件,那麼就可以進行下一步了。
所以我們還需要新建一個測試檔案 testing.py,並在裡面編寫:
from monitors.core import PresentMomentSystemResource
if __name__ == "__main__":
p1 = PresentMomentSystemResource()
p2 = PresentMomentSystemResource()
print(p1, p2)
複製程式碼
執行 testing.py 檔案,我們看一看控制檯的輸出內容:
<monitors.core.PresentMomentSystemResource object at 0x7fb0862a7128>
<monitors.core.PresentMomentSystemResource object at 0x7fb0862a7128>
Process finished with exit code 0
複製程式碼
由輸出結果得知,p1 和 p2 是同一個例項物件,說明 new 方法實現單例模式奏效了。
接著我們來編寫下一個方法。在之前的文章 《Python 系統資源資訊獲取工具,你用過沒?》 中提到過可以獲取系統資源資訊的 psutil ,並且知道獲取 cpu 資訊、記憶體資訊以及磁碟資訊所用的方法,所以我們可以在 init 方法中將這幾個方法初始化:
import psutil
def __init__(self):
self.memory = psutil.virtual_memory()
self.cpu = psutil.cpu_times()
self.disk = psutil.disk_usage("/")
複製程式碼
其中關於磁碟部分的資訊,我們制定獲取 "/" 碟符資訊即可,其他掛載磁碟資訊並沒有那麼重要。
獲取到的數值單位為k,但是通常情況下我們使用的單位是 M 或者 G ,這裡還需要設定兩個單位:
from math import pow
def __init__(self):
self.memory = psutil.virtual_memory()
self.cpu = psutil.cpu_times()
self.disk = psutil.disk_usage("/")
self.mb = pow(1024, 2)
self.gb = pow(1024, 3)
複製程式碼
pow 方法是 python 內建庫中用於計算冪的方法,可以計算某個數值 m 的 n 次冪,也就是可以將它理解為:
pow(m, n) = m 的n 次方。
到了真正編寫每個硬體資源資訊程式碼的時候了,我們首先來看看記憶體。記憶體需要的資訊為記憶體總量、已使用量、剩餘量及剩餘百分比。我們從之前的文章可以知道,通過上面定義的 self.memory 就可以直接取到部分記憶體的用量資訊:
def memory_usage(self):
"""當前時刻記憶體用量資訊
"""
total = self.memory.tota
used = self.memory.used
複製程式碼
psutil 並沒有給我們提供直接獲取餘量和餘量百分比,所以我們在將數值單位計算完畢後,可以用數學運算計算出餘量和餘量百分比,此處 memory_usage 程式碼改為:
def memory_usage(self):
"""當前時刻記憶體用量資訊
"""
total = self.memory.total/self.gb
used = self.memory.used/self.gb
free = total - used
percent = round(free/total, 2)
return total, used, free, percent
複製程式碼
然後到 testing.py 中測試一下:
from monitors.core import PresentMomentSystemResource
if __name__ == "__main__":
p1 = PresentMomentSystemResource()
res = p1.memory_usage()
print(res)
複製程式碼
執行後得到的輸出結果為:
(7.676643371582031, 1.7717132568359375, 5.904930114746094, 0.77)
複製程式碼
跟系統自帶的系統資源監控做個比對:
總體上是吻合的,說明這種方法取值和計算是沒有問題的。
然後按照之前的文章和這樣的方法,編寫其他幾個硬體的程式碼,最後整個 core.py 檔案的程式碼為:
import psutil
from math import pow
from functools import reduce
class PresentMomentSystemResource:
def __new__(cls, *args, **kwargs):
# singleton
if not hasattr(cls, '_instance'):
cls._instance = super(PresentMomentSystemResource, cls).__new__(cls, *args, **kwargs)
return cls._instance
def __init__(self):
self.memory = psutil.virtual_memory()
self.cpu = psutil.cpu_times()
self.disk = psutil.disk_usage("/")
self.mb = pow(1024, 2)
self.gb = pow(1024, 3)
@property
def memory_usage(self):
"""當前時刻記憶體用量資訊
"""
total = self.memory.total/self.gb
used = self.memory.used/self.gb
free = total - used
percent = round(free/total, 2)
buffers = self.memory.buffers/self.gb
cached = self.memory.cached/self.gb
total, used, free, buffers, cached = map(lambda x: round(x, 2), [total, used, free, buffers, cached])
return {"total": total, "used": used, "free": free, "free_percent": percent, "buffers": buffers, "cached": cached}
@property
def cpu_usage(self):
""" 當前時刻cpu用量資訊 """
count = psutil.cpu_count()
logical_count = psutil.cpu_count(logical=True)
percent = psutil.cpu_percent(interval=1)
return {"count": count, "logical_count": logical_count, "percent": percent}
@property
def disks_usage(self):
""" 當前時刻根目錄磁碟用量資訊 """
total, used, free = map(lambda x: round(x/self.gb), self.disk[:3])
percent = self.disk.percent
return {"total": total, "used": used, "free": free, "free_percent": percent}
def processes_id(self, keywords=['python']):
""" 篩選當前時刻關鍵字相關的pid列表及數量 """
attrs = psutil.process_iter(attrs=['pid', 'name'])
pid = [[p.info for p in attrs if keyword in p.info['name']] for keyword in keywords]
pid_number = reduce(lambda x, y: x+y, [len(p) for p in pid])
return {"pid": pid, "type_number": len(pid), "pid_number": pid_number}
複製程式碼
這裡著重說明一下:由於我們的監控是針對爬蟲與伺服器資源關係的監控,所以 processes_id 方法限定程式 id 的獲取僅獲取 Python 相關的程式。
flask 檢視編寫
有了資訊獲取,那麼我們來試試,如何在 flask 中使用這個類。
首先,我們在 monitors 的 init.py 檔案中設定好 flask
from flask import Flask
from flask.ext.restful import Resource, Api
app = Flask(__name__)
api = Api(app)
resource = Resource
複製程式碼
然後新建一個 start.py 檔案,並像之前的文章一樣將 flask 的骨架搭好
(在此之前請在電腦的 python 環境中安裝 flask、flask-restful):
# start.py
from monitors import app, api, resource
class PresentMomentSystemResourceView(resource):
""" 當前時刻系統資源佔用資訊檢視 """
def __init__(self):
pass
def get(self):
return {"status": "success", "message": "this is flask view"}
api.add_resource(PresentMomentSystemResourceView, '/')
if __name__ == '__main__':
app.run(debug=False)
複製程式碼
接著執行 start.py 檔案,得到輸出:
from flask.ext.restful import Resource, Api
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
複製程式碼
說明我們可以通過瀏覽器訪問本機的 5000 埠:
這裡返回的內容就是剛才編寫的試圖時 return 的內容,說明 flask 的檢視骨架搭好了。下一步則是將系統資源資訊獲取類與檢視類相關聯,將 start .py 的程式碼改為:
# start.py
from monitors import app, api, resource
from monitors.core import PresentMomentSystemResource
class PresentMomentSystemResourceView(resource):
""" 當前時刻系統資源佔用資訊檢視 """
def __init__(self):
self.sr = PresentMomentSystemResource()
def get(self):
memory = self.sr.memory_usage
disks = self.sr.disks_usage
cpu = self.sr.cpu_usage
pid = self.sr.processes_id()
return {"cpu": cpu, "memory": memory, "disk": disks, "pid": pid}
複製程式碼
在執行 start.py 檔案後,我們重新整理剛才瀏覽器的頁面,得到一串資料(火狐瀏覽器自動格式化,其他瀏覽器的資料顯示可能沒有那麼整齊):
這些資料就是我們在檢視類中 return 的 cpu、記憶體、磁碟以及程式資訊資料。
至此,我們 德瑪西亞陣營 的伺服器資訊獲取模組就編寫完成,下一次我們將會編寫資料儲存以及其他的模組。
小結
(文中所用程式碼和流程圖、UML 類圖等,關注微信公眾號【進擊的Coder】後,在微信公眾號傳送 "最佳實踐二" 即可獲得下載地址)
今天你在 召喚師峽谷 學習到的並不止是模組的編寫和 flask 的基本程式碼編寫, 更重要的是學會了如何分析模組的構成、通過繪製流程圖和 UML 類圖對自己所編寫的模組進行細化,最終實現了了一個獨立的模組。作為 召喚師峽谷的萌新 ,你體會到 奎因 今天的用意了嗎?