最佳實踐【二】從 0 開始,用 flask+mongodb 打造分佈式服務器監控平臺

今天咱們將編寫功能模塊,並在代碼開始以前對功能模塊進行分析,並經過流程圖和 UML 類圖的繪製將模塊功能細化,接着從搭建骨架開始,逐步完成一個模塊的編寫。python

通過以前的學習 《 Python 系統資源信息獲取工具,你用過沒?》、 《【一】從0開始,用flask+mongo打造分佈式服務器監控平臺》召喚師峽谷萌新 已經能夠啓動一個 Web 頁面了,而且已經經過 MongoEngine 定義了一個 ORM。接下來咱們應該對每一個模塊功能進行編寫,而且爲每一個編寫好的模塊編寫視圖。

模塊編寫順序

思考:在多個模塊當中,萌新 應該先完成哪個模塊呢?數據庫

咱們給需求進行排序,看看他們各自的權重和依賴關係,回顧一下咱們須要編寫的模塊:flask

  • 數據處理與可視化
  • 信息監控
  • 警報模塊
  • 數據存儲
  • API 視圖

奎因 畫了一張圖,從這張圖上咱們就能夠看出每一個模塊的依賴和做用:windows

首先應該先讀取每臺服務器的資源,在讀取資源的時候檢查是否超過閾值,而後主機一次性獲取全部服務器資源信息並存入數據庫,接着出庫計算並將數據處理成可視化圖表。因此能夠將上面的模塊編寫順序作一下調整:api

  • 信息監控
  • 數據存儲
  • 數據處理與可視化

至於剩下的警報模塊和 API 視圖,在前三個模塊開發過程當中用於調試,能夠只保留邏輯佔位(也就是 python 中經常使用的 Pass)待前三個模塊開發完成後再回頭補充警報模塊和 API 視圖的邏輯便可。瀏覽器

信息監控模塊的實現

《Python 系統資源信息獲取工具,你用過沒?》 中,咱們已經學會了(非 windows)服務器資源信息的獲取,如今咱們須要將以前所學的知識轉化成爲功能模塊,也就是在代碼中經過類來實現資源信息獲取,這樣咱們就能夠經過類的實例化和方法調用的方式獲取的所需的數據。bash

思考:在弄清楚需求、做用以後,咱們就能夠開始編寫代碼了嗎?服務器

若是你是一名經驗豐富的開發者,想必你心中已經有了代碼的一個大體結構。可是做爲萌新,咱們還沒可以那麼快在腦海中生成結構,因此咱們仍是須要畫圖。在代碼開始前進行構思、畫圖可讓萌新們減小錯誤、同時也減小代碼的改動次數,有必定的概率提高,既然如今是編寫類,那麼咱們就來繪製一個 UML 類圖吧!微信

首先,咱們應該給類想一個名字,就叫 PresentMomentSystemResource 吧,因此一個空的 UML 圖就能夠畫出來了:restful

至於裏面的方法,咱們想一下:

  • 應該有一個 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 類圖對本身所編寫的模塊進行細化,最終實現了了一個獨立的模塊。做爲 召喚師峽谷的萌新 ,你體會到 奎因 今天的用意了嗎?

相關文章
相關標籤/搜索