• 自动化监控系统(四) 客户端设计


    一个程序的目录结构:

    bin:可执行文件

    conf:配置文件

    core:逻辑关系

    plugins:各种插件

    var:日志

    客户端:

    1、设置一个程序入口,运行该文件A就能启动客户端。

    2、给A传位置参数(start 或 stop),通过获取位置参数名称,使用反射来调用相应方法,做到启动或者停止client。

    3、启动客户端后,通过发起HTTP请求,获取service下发的任务(监控哪些服务:CPU、memory、network。。。。。。)

    发送请求中,应该有client相关配置信息:主机Id、主机IP地址、主机端口、url配置、请求超时时间和更新监控配置时间。

    暂时这些,后面再添加

     client端通过特定url,发送http请求,然后server端返回相应的信息,告诉client需要监控什么服务

    在服务端也需要添加相应的url和view,返回客户端的请求信息。

    view

    from django.shortcuts import render
    
    # Create your views here.
    
    from django.views.generic import View
    from django.http import HttpResponse
    import json
    
    from .serializer import ClientHandler
    
    
    class client_config_view(View):
    
        #返回客户端需要监控信息
        def get(self,request,client_id):
            #获取client信息
            # client_configs = ClientHandler(client_id).fetch_configs()
            client_obj = ClientHandler(client_id)
            client_config = client_obj.fetch_configs()
            print("客户端ID",client_id)
            if client_config:
                return HttpResponse(json.dumps(client_config),content_type="application/json")
    View Code

    serializer

    #!/usr/bin/env python
    # _*_ coding:utf-8 _*_
    __author__ = "BIGNI"
    __date__ = "2017/4/29 16:34"
    import traceback
    
    from django.views.generic import View
    
    from .models import Host
    
    
    class ClientHandler(View):
        #初始化
        def __init__(self,client_id):
            self.client_id = client_id
            #client配置
            self.client_configs = {
                "services":{}
            }
    
    
        def fetch_configs(self):
    
            try:
    
                host_obj_id = Host.objects.get(id=self.client_id)
                print(">>>>>>>>>",host_obj_id)
                #获取模板list
                template_list = list(host_obj_id.templates.select_related())
                print(">>>>",template_list)
                #获取主机组obj
    
                host_group_obj = host_obj_id.host_groups.select_related()
                #把主机组下的目标添加进来
                template_list.extend([template for template in host_group_obj])
                print("--->",template_list)
                #获取服务列表
                for template in template_list:
                    for service in template.services.select_related():
                        print(service)
                        #获取插件名和间隔时间
                        self.client_configs['services'][service.name] = [service.plugin_name,service.interval]
    
            except:
                traceback.print_exc()
            return self.client_configs
    
    # test = ClientHandler(1)
    # print(test,test.fetch_configs())
    View Code

     测试下:

    调用特定url,server端可以返回需要监控的信息。

    {"services": {"LinuxCPU": ["get_linux_cpu", 15], "LinuxNetwork": ["GetNetworkStatus", 60], "LinuxMemory": ["get_memory_info", 60]}}
    返回这种格式{"services":{"监控的服务":["插件名",监控间隔]}}
    client拿到这个信息后会根据插件名调用相关的脚本,拿到数据返回给server,这一过程会在线程里完成。

     接下来继续client端的开发:

    1、通过运行monitor_client启动客户端,执行main_command方法。

    2、main_command方法自带start和stop两个方法

    #!/usr/bin/env python
    # _*_ coding:utf-8 _*_
    __author__ = "BIGNI"
    __date__ = "2017/4/15 23:48"
    
    from .client import ClientHandlers
    
    class main_command(object):
        #初始化
        def __init__(self,sys_argv):
            self.sys_argv = sys_argv
            if len(sys_argv) < 2 :
                exit("请输入start或stop")
            else:
                self.entry_command()
    
        #根据命令调用方法
        def entry_command(self):
            print("##############################################")
            if hasattr(self.sys_argv[1]):
                func = getattr(self,self.sys_argv[1])
                return func()
            else:
                print("请输入正确的命令")
    
    
        def start(self):
            client = ClientHandlers()
            client.forever_run()
    
        def stop(self):
            pass
    main_command

    3、start方法会创建ClientHandlers对象,接着调用里面的forever_run方法,获取server端返回的信息,并根据插件名给各个服务进行监控

    #!/usr/bin/env python3
    # _*_ coding:utf-8 _*_
    __author__ = "BIGNI"
    __date__ = "2017/4/29 11:42"
    import time,threading,json
    
    import requests
    
    from conf import settings
    from plugins import plugin_api
    
    class ClientHandlers(object):
    
        def __init__(self):
            #初始化监控服务
            self.monitor_services = {}
    
        def load_lastest_config(self):
            """
            加载最新的配置信息
            :return:
            """
            #request请求方式
            request_type = settings.configs["urls"]["get_configs"][1]
            #拼接url
            request_url = "%s/%s" % (settings.configs['urls']['get_configs'][0], settings.configs['HostID'])
            lastest_config = self.url_request(request_type,request_url)
            #把最小配置更新到监控服务字典中
            self.monitor_services.update(lastest_config)
    
    
        def forever_run(self):
            #启动客户端
            exit_flag = False
            #第一次启动时初始化最新配置时间
            config_lastest_update_time = 0
            while not exit_flag:
    
                if time.time() - config_lastest_update_time > settings.configs['ConfigUpdateInterval']:
                    self.load_lastest_config()
                    print("Lastest_config:",self.monitor_services)
                    config_lastest_update_time = time.time()
    
                """
                Lastest_config: {'services': {'LinuxCPU': ['get_linux_cpu', 15], 'LinuxMemory': ['get_memory_info', 60],
                'LinuxNetwork': ['GetNetworkStatus', 60]}}
    
                """
                for service_name,val in self.monitor_services['services'].items():
                    if len(val) ==2:
                        # 第一次启动插件时,初始化当前时间,给每个服务维护一个计时器
                        self.monitor_services['services'][service_name].append(0)
                    #获取监控间隔和最新插件时间
                    monitor_interval = val[1]
                    last_invoke_time = val[2]
                    if time.time() - last_invoke_time > monitor_interval:
                        print("--->",last_invoke_time,"--->",time.time())
                        #重置计时器时间
                        self.monitor_services['services'][service_name][2] = time.time()
                        t = threading.Thread(target=self.invoke_plugin,args=(service_name,val))
                        t.start()
                        print("启动监控的服务: [{ServiceName}]".format(ServiceName=service_name))
    
                    else:
                        #需要等待监控间隔时间
                        print("监控的服务: {ServiceName} 距离下次启动时间:{interval} secs".format(ServiceName=service_name,
                                                                      interval=monitor_interval - (time.time() - last_invoke_time)))
                        time.sleep(5)
    
            time.sleep(1)
    
    
        #运行插件
        def invoke_plugin(self,service_name,val):
            #{"services": {"LinuxNetwork": ["n/a", 60,0], "LinuxMemory": ["n/a", 60,0], "LinuxCPU": ["n/a", 60,0]}}
            #获取插件名
            plugin_name = val[0]
            if hasattr(plugin_api,plugin_name):
                func = getattr(plugin_api,plugin_name)
                plugin_callback = func()
                print("####################################################")
                print(plugin_callback)
                print("####################################################")
    
                report_data = {
                    'client_id':settings.configs['HostID'],
                    'service_name':service_name,
                    'data':plugin_callback
                }
                #请求方式get or post
                request_action = settings.configs['urls']['service_report'][1]
                #请求路径
                request_url = settings.configs['urls']['service_report'][0]
    
                #report_data = json.dumps(report_data)
                # print('---report data(发送的数据):',report_data)
                #调用url_request方法,以post方式发送request
                self.url_request(request_action, request_url, params=report_data)
            else:
                print("33[31;1mCannot find service [%s]'s plugin name [%s] in plugin_api33[0m"% (service_name,plugin_name ))
            print('--plugin:',val)
    
    
        def url_request(self,action,request_url,**extra_data):
    
            abs_url = "http://{ip_addr}:{port}/{url}".format(ip_addr=settings.configs["Server"],
                                                             port=settings.configs["ServerPort"],
                                                             url=request_url)
            print("33[31;1m{abs_url}33[0m".format(abs_url=abs_url),type(extra_data),extra_data)
            if action in ('get',"GET"):
                print(abs_url,extra_data)
                try:
                    r = requests.get(abs_url,timeout=settings.configs['RequestTimeout'])
                    r_data = r.json()
                    return r_data
    
                except requests.RequestException as E :
                    exit("33[31;1m%s33[0m" % E)
    
            elif action in ('post','POST'):
                try:
                    #把数据转换成json再通过post发送
                    # data = json.dumps(extra_data['params'])
                    req = requests.post(url=abs_url,data=extra_data["params"])
    
                    res_data = req.json()
                    # res_data = req.text
                    print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
                    print("33[31;1m[%s]:[%s]33[0m response:
    %s,%s" % (action, abs_url, res_data,data))
                    print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
                    return res_data
    
                except Exception as e:
                    print('------exec', e)
                    exit("33[31;1m%s33[0m" % e)
    client

    PS:客户端在接收到服务端返回的配置信息,并不能识别具体要监控的服务,比如server端告诉client端需要监控CPU各项参数等,所以两端要能约定好,比如server端返回CPU、Memory和Network这三个字符串,就表示要监控这三种服务,三个字符串在这里就是插件名。插件统一放在plugins目录下。

     不同服务监控的时间可以通过server端返回的监控间隔来判断

     

     每次监控服务就生成一个新的线程,然后就让线程返回数据给服务端,不需要再做处理。

    client通过post发送的数据格式:

    report_data = {
         #客户端ID
          client_id':settings.configs['HostID'],
          #服务名
          'service_name':service_name,
          #数据
          'data':plugin_callback
    }

    发送数据这里我踩了个坑,其实也是不熟悉requests模块造成的,requests的post发送有三种方式,其中有form和json两种,

    区别是form发送可以直接把report_data直接发送到客户端,通过request.post.get方法可以获取到值。但我的report_data是嵌套字典,这种发送方式会把数据内容进行转换(细节我没搞懂,反正我传的数据变了。),所以嵌套字典的先转换成json格式,然后再用requests发送,服务端通过request.body可以拿到数据。

     对于server端和client端的数据交互,推荐使用requests模块,可以自动处理数据的编码、json、发送超时等问题。

     问题1:在models里的manytomany字段不会存储在数据表中,多对多的在xadmin里要按ctrl选中才行,不然通过select_relate获取到的是空的queryset,

    问题2:我调用model的serializer没放在app的默认views文件里,进行debug调试时,serializer也需要打上断点。

    问题3:我在client通过post方式发送数据给server端,debug调试时发现没取到值,查了资料,原来requests库有三种post发送数据方式,我使用的是json方式(发送前先用json.dumps处理下),改成用form方式(实际就是不需要json,requests会自动转换)

     
  • 相关阅读:
    职场中开口说重点思路分享
    c#值类型引用类型第一章
    .NET Core3.1 Dotnetty实战第三章
    WPF实现飞控姿态仪表盘控件Attitude dashboard
    .NET Core3.1 Dotnetty实战第二章
    Python函数
    python常用英语单词(初学,英语不好的适用)
    Python学到什么程度可以面试工作(解答一)
    来测试下 2019 你一共写了多少行代码?
    python配置虚拟开发环境
  • 原文地址:https://www.cnblogs.com/laonicc/p/6701779.html
Copyright © 2020-2023  润新知