最佳实践【二】从 0 开始,用 flask+mongodb 打造分布式服务器监控平台

举报
Tracy 发表于 2019/11/06 23:43:12 2019/11/06
【摘要】 今天我们将编写功能模块,并在代码开始之前对功能模块进行分析,并通过流程图和 UML 类图的绘制将模块功能细化,接着从搭建骨架开始,逐步完成一个模块的编写。经过之前的学习 《 Python 系统资源信息获取工具,你用过没?》、《【一】从0开始,用flask+mongo打造分布式服务器监控平台》, 召唤师峡谷萌新 已经可以启动一个 Web 页面了,并且已经通过 MongoEngine 定义了一...

今天我们将编写功能模块,并在代码开始之前对功能模块进行分析,并通过流程图和 UML 类图的绘制将模块功能细化,接着从搭建骨架开始,逐步完成一个模块的编写。


1经过之前的学习 《 Python 系统资源信息获取工具,你用过没?》、《【一】从0开始,用flask+mongo打造分布式服务器监控平台》,  召唤师峡谷萌新 已经可以启动一个 Web 页面了,并且已经通过 MongoEngine 定义了一个 ORM。接下来我们应该对每个模块功能进行编写,并且为每个编写好的模块编写视图。


模块编写顺序

思考:在多个模块当中,萌新 应该先完成哪一个模块呢?

我们给需求进行排序,看看他们各自的权重和依赖关系,回顾一下我们需要编写的模块:

  • 数据处理与可视化

  • 信息监控

  • 警报模块

  • 数据存储

  • API 视图

奎因 画了一张图,从这张图上我们就可以看出每个模块的依赖和作用:


1


首先应该先读取每台服务器的资源,在读取资源的时候检查是否超过阈值,然后主机一次性获取所有服务器资源信息并存入数据库,接着出库计算并将数据处理成可视化图表。所以可以将上面的模块编写顺序做一下调整:

  • 信息监控

  • 数据存储

  • 数据处理与可视化

至于剩下的警报模块和 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 文件后,我们刷新刚才浏览器的页面,得到一串数据(火狐浏览器自动格式化,其他浏览器的数据显示可能没有那么整齐):


1


这些数据就是我们在视图类中 return 的 cpu、内存、磁盘以及进程信息数据。

至此,我们 德玛西亚阵营 的服务器信息获取模块就编写完成,下一次我们将会编写数据存储以及其他的模块。


小结

(文中所用代码和流程图、UML 类图等,关注微信公众号【进击的Coder】后,在微信公众号发送 "最佳实践二" 即可获得下载地址)

今天你在 召唤师峡谷 学习到的并不止是模块的编写和 flask 的基本代码编写, 更重要的是学会了如何分析模块的构成、通过绘制流程图和 UML 类图对自己所编写的模块进行细化,最终实现了了一个独立的模块。作为 召唤师峡谷的萌新 ,你体会到 奎因 今天的用意了吗?


1
作者:夜幕韦世东
链接:https://juejin.im/post/5c04e1c56fb9a04a0378f823
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

转载声明: 

原文链接:

https://juejin.im/post/5c04e1c56fb9a04a0378f823

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。