需求:

  1、所有要检测的资源url放到一个单独文件中

  2、检测cdn节点资源大小与源站文件大小是否一致

  3、随机抽查几个资源,检查md5sum是否一致

  4、使用多线程,可配置线程数

代码目录:

hexm:Hexm hexm$ tree ./checkcdn
./checkcdn
├── README.TXT
├── check.py # 主程序
├── conf
│ └── url.txt # 配置文件
├── lib
│ ├── __init__.py
│ ├── common.py
│ └── threadpool.py # 线程池
└── tmp
├── cdn # 存放从CDN节点系在的资源
└── origin # 存放从源站下载的资源

README.TXT

依赖:
requests
兼容性:
兼容Python3以及Python2.7 使用方法:
usage: check.py [-h] [-t THREADS] [-c COUNTS] optional arguments:
-h, --help show this help message and exit
-t THREADS, --threads THREADS
开启多少线程,默认5个
-c COUNTS, --counts COUNTS
检测多少个包的md5值,默认3个

conf/url.txt

http://xxx_1020101.apk
http://xxx_1020102.apk
http://xxx_1020103.apk
http://xxx_1020104.apk

check.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# File Name : check.py
# Author : hexm
# Mail : xiaoming.unix@gmail.com
# Created Time : 2017-03-24 10:03 import os
import sys
import random
import argparse
import requests BASE_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(BASE_DIR) # 代理IP
PROXIES = {
"http": "http://183.136.135.191:80",
}
# 配置文件
CONFIG = BASE_DIR + '/conf/url.txt'
# 保存CDN节点文件临时目录
CDNTEMPDIR = BASE_DIR + '/tmp/cdn/'
# 保存源站文件临时目录
ORIGINTEMPDIR = BASE_DIR + '/tmp/origin/' from lib.threadpool import ThreadPool
from lib.common import isdir, download, getfilemd5 def callback(status, result):
"""
回调函数,如果函数有返回值得话用得到
:param status: 状态 True or None
:param result: 函数返回值
"""
pass def checkstatus(url):
"""
通过head方法查看源站与当前CDN节点资源大小是否一致
:param url: url
:return: None
""" r1 = requests.head(url, proxies=PROXIES)
r2 = requests.head(url) if r1.status_code == 200 and r2.status_code == 200:
if r1.headers['Content-Length'] == r2.headers['Content-Length']:
print("%s 源站和CDN节点资源\033[0;32m一致\033[0m, 源站文件大小为%s,CDN节点文件大小为%s"
% (url,r1.headers['Content-Length'],r2.headers['Content-Length']))
else:
print("%s 源站和CDN节点资源\033[0;31m不一致\033[0m, 源站文件大小为%s,CDN节点文件大小为%s"
% (url,r1.headers['Content-Length'],r2.headers['Content-Length']))
else:
print("%s 源站和CDN节点状态码\033[0;31m异常\033[0m,源站状态码为%s,CDN节点状态码为%s"
% (url,r1.status_code,r2.status_code)) def checkmd5(url, cdnTempDir, originTempDir):
"""
检查源站与当前cdn节点资源是否一致,下载超时300s
:param url: url
:param cdnTempDir: 保存从cdn节点下载的临时文件目录
:param originTempDir: 保存从源站下载的临时文件目录
:return: None
""" filename = url.split('/')[-1]
tempCdnFile = cdnTempDir + filename
tempOriginFile = originTempDir + filename status1 = download(url, tempOriginFile, proxies=PROXIES) if status1 is not None:
if status1 == 200:
status2 = download(url, tempCdnFile)
else:
print("%s \033[0;31m状态码异常\033[0m校验失败" % url) if status1 == 200 and status2 == 200:
if getfilemd5(tempCdnFile) == getfilemd5(tempOriginFile):
print("%s 源站和cdn节点资源md5值\033[0;32m一致\033[0m," % url)
else:
print("%s 源站和cdn节点资源md5值\033[0;31m不一致\033[0m" % url)
elif status1 is None or status2 is None:
print("%s \033[0;31m下载失败\033[0m" % url) # 检查后删除下载的文件
try:
os.remove(tempOriginFile)
os.remove(tempCdnFile)
except Exception as e:
pass def parse_args():
"""
解析命令行参数
:return: args
""" parser = argparse.ArgumentParser()
help = '开启多少线程,默认5个'
parser.add_argument('-t', '--threads', type=int, help=help, default='') help = '检测多少个包的md5值,默认3个'
parser.add_argument('-c', '--counts', type=int, help=help, default=3) args = parser.parse_args()
return args if __name__ == "__main__": if not isdir(CDNTEMPDIR): os.makedirs(CDNTEMPDIR)
if not isdir(ORIGINTEMPDIR): os.makedirs(ORIGINTEMPDIR) # 从文件中获取所有url
urls = [line.strip() for line in open(CONFIG, mode='r').readlines()]
args = parse_args() # 检查包大小
pool = ThreadPool(args.threads) # 最多创建5个线程
for url in urls:
pool.run(checkstatus, (url,), callback=None) # 随机抽查3个,检查md5
for randurl in random.sample(urls, args.counts):
pool.run(checkmd5, (randurl, CDNTEMPDIR, ORIGINTEMPDIR,), callback=None)
pool.close()

check.py

lib/common.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# File Name : common.py
# Author : hexm
# Mail : xiaoming.unix@gmail.com
# Created Time : 2017-03-24 10:03 import os
import hashlib
import requests def getfilesize(path):
"""
获取文件大小
:param path: 文件路径
:return: 返回文件大小
"""
return os.path.getsize(path) def isfile(path):
"""
判断是否是文件
:param path: 文件路径
:return: 如果是返回True,否则返回None
"""
if os.path.isfile(path): return True def isdir(path):
"""
判断是否是目录
:param path: 路径
:return: True or None
"""
if os.path.isdir(path): return True def getstatus(url, proxies=None):
"""
返回状态码
:param url: url
:return: 状态码
"""
return requests.head(url, proxies).status_code def download(url, path, proxies=None):
"""
下载文件,并返回状态码
:param url: 下载的url
:param path: 保存文件的路径
:param proxies: 使用代理的地址
:return: 返回状态码
"""
try:
response = requests.get(url, proxies=proxies, stream=True, timeout=60) status = response.status_code
total_size = int(response.headers['Content-Length'])
# print(response.headers)
if status == 200:
with open(path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk: f.write(chunk)
if total_size == getfilesize(path): # 下载文件大小与头部Content-Length大小一致,则下载成功
return status
# 状态码非200,返回状态码
else: return status
except Exception as e:
return None def getfilemd5(path):
"""
返回文件的md5sum
:param path: 文件路径
:return: 返回校验和,否则返回None
"""
if isfile(path):
md5obj = hashlib.md5()
maxbuf = 8192
f = open(path, 'rb')
while True:
buf = f.read(maxbuf)
if not buf:
break
md5obj.update(buf)
f.close()
hash = md5obj.hexdigest()
return hash
return None if __name__ == "__main__":
pass

lib/threadpool.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# File Name : threadpool.py
# Author : hexm
# Mail : xiaoming.unix@gmail.com
# Created Time : 2017-03-23 20:03 import sys
if sys.version > '':
import queue
else:
import Queue as queue
import threading
import contextlib
import time StopEvent = object() # 终止线程信号 class ThreadPool(object):
"""
1、解决线程重用问题,当前线程执行完任务后,不杀掉,放到空闲线程列表,继续执行下个任务
2、根据任务量开启线程,如果设置10个线程,只有2个任务,最多只会开启两个线程
3、如果有500个任务,任务执行非常快,2个线程就能完成,如果设置开启10个线程,
只会开启两个线程
""" def __init__(self, max_num, max_task_num = None):
if max_task_num:
self.q = queue.Queue(max_task_num) # 指定任务最大数,默认为None,不限定
else:
self.q = queue.Queue()
self.max_num = max_num # 最多多少线程
self.cancel = False # 执行完所有任务,终止线程信号
self.terminal = False # 无论执行完毕与否,都终止所有线程
self.generate_list = [] # 已创建多少线程
self.free_list = [] # 空闲多少线程 def run(self, func, args, callback=None):
"""
线程池执行一个任务
:param func: 任务函数
:param args: 任务函数所需参数
:param callback: 任务执行失败或成功后执行的回调函数,回调函数有两个参数1、任务函数执行状态;2、任务函数返回值
:return: 如果线程池已经终止,则返回True否则None
"""
if self.cancel:
return
# 没有空闲线程 并且已创建线程小于最大线程数才创建线程,
if len(self.free_list) == 0 and len(self.generate_list) < self.max_num:
self.generate_thread() # 满足则创建线程,并将任务放进队列
w = (func, args, callback,)
# 函数,元组,函数 ,将这三个参数放在元组里面,当成一个整体放到队列里面
self.q.put(w) # 满足条件则创建线程,并把任务放队列里面 def generate_thread(self):
"""
创建一个线程
"""
t = threading.Thread(target=self.call) # 每一个线程被创建,执行call方法
t.start() def call(self):
"""
循环去获取任务函数并执行任务函数
"""
current_thread = threading.currentThread()
self.generate_list.append(current_thread) # 每创建一个线程,将当前线程名加进已创建的线程列表 event = self.q.get() # 在队列中取任务, 没任务线程就阻塞,等待取到任务,线程继续向下执行
while event != StopEvent: # 是否满足终止线程 func, arguments, callback = event # 取出队列中一个任务
try:
result = func(*arguments) # 执行函数,并将参数传进去
success = True
except Exception as e:
success = False
result = None if callback is not None:
try:
callback(success, result)
except Exception as e:
pass with self.worker_state(self.free_list, current_thread): # 当前线程执行完任务,将当前线程置于空闲状态,
#这个线程等待队列中下一个任务到来,如果没来,一直处于空闲, 如果到来,去任务
if self.terminal:
event = StopEvent
else:
event = self.q.get() # 将当前任务加入到空闲列表后,如果有任务,取到,没有阻塞 取到后,移除当前线程
else: # 满足终止线程,在创建的线程列表中移除当前线程
self.generate_list.remove(current_thread) def close(self):
"""
执行完所有的任务后,杀掉所有线程
"""
self.cancel = True # 标志设置为True
full_size = len(self.generate_list) + 1 # 已生成线程个数, +1 针对python2.7
while full_size:
self.q.put(StopEvent) #
full_size -= 1 def terminate(self):
"""
无论是否还有任务,终止线程
"""
self.terminal = True while self.generate_list:
self.q.put(StopEvent) self.q.queue.clear() @contextlib.contextmanager
def worker_state(self, state_list, worker_thread):
"""
用于记录线程中正在等待的线程数
"""
state_list.append(worker_thread) # 将当前空闲线程加入空闲列表
try:
yield
finally:
state_list.remove(worker_thread) # 取到任务后,将当前空闲线程从空闲线程里移除, # 使用例子
if __name__ == "__main__": pool = ThreadPool(5) # 创建pool对象,最多创建5个线程 def callback(status, result):
pass def action(i):
time.sleep(1)
print(i) for i in range(30): # 共30个任务
ret = pool.run(action, (i,), callback=None) # 将action函数,及action的参数,callback函数传给run()方法
pool.close()

例子:


脚本检测CDN节点资源是否与源站资源一致的更多相关文章

  1. Error524 源站处理超时 Error 524: A timeout occurred

    https://su.baidu.com/helps/index.html#/4/5a61e4b5b34f697f13234a5b Error524 源站处理超时 更新时间:2018-01-19 20 ...

  2. httplib模块,测试cdn节点文件同步

    httplib模块是一个专门用于http的模块,urllib和urllib2也都是基于对它进行了更上层次的封装 我记得刚开始的时候,公司用的cdn有段时间抽风,全球40多个节点总是有那么几个节点不同步 ...

  3. 快速缓存刷新CDN节点的方法

    缓存刷新方式有 URL 刷新.目录刷新和 URL 预热.URL 刷新是以文件为单位进行缓存刷新.目录刷新是以目录为单位,将目录下的所有文件进行缓存刷新.URL 预热是以文件为单位进行资源预热. 刷新后 ...

  4. 服务降级 托底预案 Nginx中使用Lua脚本检测CPU使用率,当达到阀值时开启限流,让用户排队

    https://mp.weixin.qq.com/s/FZAcQQAKomGEe95kln1HCQ 在京东我们是如何做服务降级的 https://mp.weixin.qq.com/s/FZAcQQAK ...

  5. CDN 的缓存与回源机制解析

    CDN的缓存与回源机制解析 CDN (Content Delivery Network,即内容分发网络)指的是一组分布在各个地区的服务器.这些服务器存储着数据的副本,因此服务器可以根据哪些服务器与用户 ...

  6. Nginx实现简易泛域名CDN节点

    如何使用Nginx泛域名解析+反向代理+静态资源缓存呢? 安装nginx,安装过程不再赘述,记得带上pcre.gzip.sub.status这几个模块,另外如果想开通在线清理缓存功能,需要安装ngx_ ...

  7. 如何绕过CDN找源站ip?

    这是一个总结帖,查了一下关于这个问题的国内外大大小小的网站,对其中说的一些方法总结归纳形成,里面具体发现ip的方法不是原创,所有参考的原贴都也贴在了后面,大家可以自行看看原贴. 首先,先要明确一个概念 ...

  8. Incapsula免费日本CDN加速和CDNZZ香港CDN节点加速

    Incapsula免费日本CDN加速和CDNZZ香港CDN节点加速 免费的CDN对于那些将空间放在美国的博客网站加速效果是最好的,CDN可以解决国内连接美国的网络线路经常抽风和访问速度时好时坏的问题, ...

  9. windows下脚本检测tomcat是否启动,没有启动则启动

    最近有个服务需要部署到windows server2003上面,机房没有windows ser的机器,没办法搞了个阿里云服务,购买的配置比较低, 不知道什么原因,tomcat启动后总是容易自动退出,搞 ...

随机推荐

  1. CF 991

    843名... 正规比赛肯定要掉分了...... 就算C没WA也是765名...为什么会这么菜呢? A,水.我加了两个特判. B,水.以 n * 4.5 为目标即可. C,裸二分,可耻的WA了一次是为 ...

  2. Shell中while循环的done 后接一个重定向<

    读文件的方法: 第一步: 将文件的内容通过管道(|)或重定向(<)的方式传给while 第二步: while中调用read将文件内容一行一行的读出来,并付值给read后跟随的变量.变量中就保存了 ...

  3. Makefile ------ .PHONY的作用

    看下面的例子 Makefile文件 .PHONY: cleanclean: rm *.o 当Makefile文件所在目录有文件名为clean的文件,命令行“.PHONY: clean”又没添加的话,执 ...

  4. pymysql 解决 sql 注入问题

    1. SQL 注入 SQL 注入是非常常见的一种网络攻击方式,主要是通过参数来让 mysql 执行 sql 语句时进行预期之外的操作. 即:因为传入的参数改变SQL的语义,变成了其他命令,从而操作了数 ...

  5. 即将上线的YARN服务器面临的一系列填坑笔记

    即将上线的YARN服务器面临的一系列填坑笔记 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 折腾了一个星期,终于让开发将数据跑起来了,可通过yarn的webUI界面,发现这里的核心 ...

  6. 即将上线的Kafka服务器面临的一系列填坑笔记

      即将上线的Kafka服务器面临的一系列填坑笔记 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Kafka日志报错:[error] k.m.j.KafkaJMX$ - Fai ...

  7. C和C++的区别和联系

    关于C和C++的区别是面试中经常会被问到的问题,本着即将面试的心态,进行知识整理,并对小知识点进行扩展: C/C++的联系: C++是C的超集,兼容大部分C的语法的结构: 联系嘛我只能想到这个,毕竟c ...

  8. 用java发邮件之一 (直接源于真实项目) 【原】

    真实项目应用的java发送邮件,应该还待进一步完善. 依赖 mail-1.4.jar jar包下载地址: http://mvnrepository.com/artifact/javax.mail/ma ...

  9. C++中返回值

    函数的返回值用于初始化在调用函数是创建的临时对象. 1.返回值为非引用类型: 会将函数的返回值复制给临时对象.跟实参初始化形参的方式一样. 2.返回值为引用类型: 没有复制返回值,返回的是对象本身.返 ...

  10. CSS进阶之模拟Bootstrap网格布局

    目前暂时实现效果,容后面整理心得,先贴上源代码. 源码 <!DOCTYPE html> <html> <head> <title>demo bootst ...