需求:

  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. A1055. The World's Richest

    Forbes magazine publishes every year its list of billionaires based on the annual ranking of the wor ...

  2. A1082. Read Number in Chinese

    Given an integer with no more than 9 digits, you are supposed to read it in the traditional Chinese ...

  3. js中this的总结

    http://www.blogjava.net/baoyaer/articles/105864.html 在面向对象编程语言中,对于this关键字我们是非常熟悉的.比如C++.C#和Java等都提供了 ...

  4. location的三种连接方式和区别

    location.href是一个属性,要这样使用:location.href='http://www.example.com'而location.assign('http://www.example. ...

  5. openvpn路由配置

    openvpn路由配置 通常openvpn部署好以后,客户端连接VPN后会被配置一些路由,其客户端的路由会被修改为所有的流量都通过VPN来传输.但有时候,我们需要客户端的某些IP走VPN或者本地网关. ...

  6. 06-开闭原则(OCP)

    1. 背景 在软件的生命周期内,因为变化.升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试. 2. 定义   ...

  7. Java Web之路(一)Servlet

    前言 执行过程 Servlet 生命周期.工作原理:http://www.cnblogs.com/xuekyo/archive/2013/02/24/2924072.html Servlet的生命周期 ...

  8. digest 词根 gest

    digest  /ˈdaɪdʒest/: to change food that you have just eaten into substances that your body can use; ...

  9. 第16月第15天 glut

    1. https://tokoik.github.io/opengl/libglut.html https://github.com/wistaria/wxtest/tree/master/C htt ...

  10. Sql server 查询某个时间段,分布有几周,几月和几日

    1. 查询:以“周”为单位 --查询以下时间段内分别有几周 --时间段:“2017-09-01”到“2017-10-1” select number as wknum from master..spt ...