最佳实践【二】从 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 类图对本身所编写的模块进行细化,最终实现了了一个独立的模块。做为 召唤师峡谷的萌新 ,你体会到 奎因 今天的用意了吗?

相关文章
相关标签/搜索