Python 使用APScheduler实现并发定时Dig域名解析任务

Python 使用APScheduler实现并发定时Dig域名解析任务

一、概念澄清
  • APScheduler

    APScheduler是Python的一个定时任务框架,用于执行周期或者定时任务,可以基于日期、时间间隔,及类似于Linux系统上的定时任务crontab类型的定时任务,因此可以将其作为Linux系统crontab或windows计划任务程序的替换,值得注意的是,APScheduler不是守护进程,不带有任何命令行工具,仅仅是作为提供调度器或者调度服务的基础模块,使用者需要根据具体需求引入使用。

    • 触发器: 触发器包含调度逻辑, 描述一个任务何时被触发, 有按日期、按时间间隔、按cronpb 描述式三种触发方式。每作业都有它自己的触发器, 除了初始配置之外, 触发器是完全无状态的。

    • 作业存储器:作业存储器指定了作业被存放的位置, 默认的作业存储器是内存, 也可以将作业保存在各种数据库中。

    • 执行器:执行器是将指定的作业( 调用函数) 提交到线程池或进程池中运行, 当任务完成时, 执行器通知调度器触发相应的事件。

    • 调度器:任务调度器, 控制器角色, 通过它配置作业存储器、执行器和触发器, 添加、修改和删除任务。

      • 常用调度器

        BlockingScheduler:适用于调度程序是进程中唯一运行的进程,调用start函数会阻塞当前线程,不能立即返回。
        BackgroundScheduler:适用于调度程序在应用程序的后台运行,调用start后主线程不会阻塞。
        AsyncIOScheduler:适用于使用了asyncio模块的应用程序。
        GeventScheduler:适用于使用gevent模块的应用程序。
        TwistedScheduler:适用于构建Twisted的应用程序。
        QtScheduler:适用于构建Qt的应用程序。
        
  • threating

    python提供Thread类实现线程功能,其基本结构和参数和进程一致,如下:

    • 结构

      class Thread(group=None, target=None, name=None, args=(), kwargs={})
      
    • 参数

      • target: 调用对象,一般为函数、类,是进程的执行实体
      • name: 进程的别名
      • args:调用对象的位置参数元组
      • kwargs:调用对象的字典
      • group:基本不使用,忽略
    • Process类提供的方法

      • is_alive(): 返回进程是否激活
      • join(]): 阻塞进程,直到进程执行完成或者终止
      • run(): 进程运行的函数,可以被重写
      • start(): 激活进程,进程开始运行
      • terminate(): 进程终止
二、基本示例
  • APScheduler

    • 基本示例

      from datetime import datetime
      import os
      from apscheduler.schedulers.blocking import BlockingScheduler
      
      def tick():
          print('Tick! The time is: %s' % datetime.now())
      
      if __name__ == '__main__':
          scheduler = BlockingScheduler()
          scheduler.add_job(tick, 'interval', seconds=3)
          print('Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C    '))
          try:
              scheduler.start()
          except (KeyboardInterrupt, SystemExit):
              pass
      
    • 间隔任务功能示例

      from datetime import datetime
      import os
      from apscheduler.schedulers.blocking import BlockingScheduler
      
      def tick():
          print('Tick! The time is: %s' % datetime.now())
      
      if __name__ == '__main__':
          scheduler = BlockingScheduler()
          scheduler.add_job(tick, 'cron', hour=19,minute=23)
          print('Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C    '))
      
          try:
              scheduler.start()
          except (KeyboardInterrupt, SystemExit):
              pass
      
      
      hour =19 , minute =23
      hour ='19', minute ='23'
      minute = '*/3' 表示每 5 分钟执行一次
      hour ='19-21', minute= '23' 表示 19:23、 20:23、 21:23 各执行一次任务
      
  • threating

三、插件实现
  • 功能:实现指定dns域名的dig解析探测: dig domain @nam_server A / AAAA

  • 使用包:

    "dnspython"
    "numpy"
    "datetime"
    "IPy"
    "apscheduler"
    "requests"
    
  • 域名解析模块【支持v4和v6】

    # ------------------------------------- dig模块 -------------------------------------
    # 域名解析,根据域名和nameserver进行解析,返回解析结果集数组
    def dns_resolve(domains, name_server, detect_type):
        for domain in domains:
            num = 0
            ips = []
            ttl_value = 0
            if name_server == "":
                continue
            try:
                ans = resolver.resolve(domain, detect_type)  # 指定查询类型为A记录
                ans.nameserver = [].append(name_server)
                for i in ans.response.answer:  # 通过response.answer方法获取查询信息
                    for ip in i.items:  # 遍历结果集信息
                        ips.append(ip)
                        num = num + 1
                ttl_value = ans.rrset.ttl
            except:
                ips = ["unknown"]
                num = 0
                ttl_value = 0
            finally:
                print("采集结果: ", domain + '@' + name_server + '@' + str(num) + '@' + str(
                    ttl_value) + '@' + ';'.join(
                    '%s' % a for a in ips) + '\n')
    
  • 多线程模块

    # ------------------------------------- 任务处理模块 -------------------------------------
    # 继承一个Thread类,在run方法中进行需要重复的单个函数操作
    class Detect(threading.Thread):
        def __init__(self, queue, lock, num):
            # 传递一个队列queue和线程锁,并行数
            threading.Thread.__init__(self)
            self.queue = queue
            self.lock = lock
            self.num = num
    
        def run(self):
            with self.num:  # 同时并行指定的线程数量,执行完毕一个则死掉一个线程
                # 以下为需要重复的单次函数操作
                target_info = self.queue.get()  # 等待队列进入
                for task_name, task_info in target_info.items():
                    urls = [task_info["domain"]]
                    # self.lock.acquire()  # 锁住线程,防止同时输出造成混乱
                    dns_resolve(urls, task_info["name_server_v4"], 'A', )
                    dns_resolve(urls, task_info["name_server_v6"], 'AAAA')
                    # self.lock.release()
                # print(datetime.datetime.now().strftime('%Y/%m/%d/%H/%M/%S'), '任务线程:', self.name, '任务详情:', target_info)
                self.queue.task_done()  # 发出此队列完成信号
                        
    
  • 任务队列示例

    def task_queue(que):
        target_info = {}
        domain_info = {"domain": "www.baidu.com", "name_server_v4": "8.8.8.8", "name_server_v6": ""}
        target_info['your_task_name'] = domain_info
        que.put(target_info)  # 模拟执行函数的逐个不同输入    
        
    def detect_core():
        threads = []
        que = queue.Queue()  # 初始化任务队列
        lock = threading.Lock()
        num = threading.Semaphore(600)  # 设置同时执行的线程数
        # 启动所有线程,隔3秒执行一次
        task_queue(que)
        for i in range(que.qsize()):  # 总共需要执行的次数
            t = Detect(que, lock, num)
            t.start()
            threads.append(t)
        # 等待线程执行完毕
        for t in threads:
            t.join()
        que.join()  # 等待队列执行完毕才继续执行,否则下面语句会在线程未接受就开始执行
    
  • 调度器

    # ------------------------------------- 主函数入口 -------------------------------------
    if __name__ == '__main__':
        print("任务执行")
        # 初始化调度器模块
        scheduler = BlockingScheduler()
        # 添加任务
        scheduler.add_job(detect_core, 'interval', seconds=3, id="1", coalesce=True, max_instances=2)
        # 启动任务
        scheduler.start()
    
  • 运行结果

    任务执行
    采集结果:  www.baidu.com@8.8.8.8@3@260@www.a.shifen.com.;163.177.151.109;163.177.151.110
    采集结果:  www.baidu.com@8.8.8.8@0@0@unknown
    采集结果:  www.baidu.com@8.8.8.8@3@258@www.a.shifen.com.;163.177.151.109;163.177.151.110
    采集结果:  www.baidu.com@8.8.8.8@3@255@www.a.shifen.com.;163.177.151.109;163.177.151.110
    ...
    

备注:【本文代码基本部分来源于书籍《Python自动化运维快速入门》,有兴趣的可以自行购买查阅】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玉言心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值