Loading... ## CDN 预热? > CloudFront 网络拥有 200 个以上的存在点 (PoP),这些存在点通过 AWS 主干网相互连接,为最终用户提供超低延迟性能和高可用性。 > 通过预热功能,可以强制CDN节点回源并获取最新文件。通过预热功能您可以在业务高峰前预热热门资源,提高资源访问效率。 ## Python 脚本本地运行 下载 [POP节点列表](https://static.cloudping.cloud/cdn/cloudfront-edge-locations.json),命名为 cf_pops.json。 与下列 Python 脚本放在同一个文件夹下,修改 Python 脚本入口中的 cdn 和 files 变量,运行即可 ````python import argparse import datetime import json import os import sys from concurrent.futures import ThreadPoolExecutor, as_completed import urllib.request import urllib.error import socket class CloudFrontWarmer: def __init__(self, max_workers=32): self.max_workers = max_workers self.results = { "success": [], "failed": [] } def warm_single_pop(self, pop, cf_id, cf_url, file_name): """单个 POP 点预热处理""" file_url = f'http://{cf_id}.{pop["code"]}.cloudfront.net{file_name}' header = { 'Host': cf_url } try: req = urllib.request.Request(url=file_url, headers=header) response = urllib.request.urlopen(req, timeout=10) response.close() success_msg = f'SUCCESS -> COU:{pop["country"]} POP:{pop["code"]}' return True, success_msg except (urllib.error.HTTPError, urllib.error.URLError, socket.timeout) as e: error_msg = f'FAILED -> COU:{pop["country"]} POP:{pop["code"]} REASON:{str(e)}' return False, error_msg except Exception as e: error_msg = f'ERROR -> COU:{pop["country"]} POP:{pop["code"]} REASON:{str(e)}' return False, error_msg def warm_all_pops(self, cloudfront_pops, cf_id, cf_url, file_name): """ 并发处理所有 POP 点的预热 """ print('======================================================================================') print(f'预热任务开始 [{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}] -> {cf_url}{file_name}') with ThreadPoolExecutor(max_workers=self.max_workers) as executor: future_to_pop = { executor.submit( self.warm_single_pop, pop, cf_id, cf_url, file_name ): pop for pop in cloudfront_pops } for future in as_completed(future_to_pop): pop = future_to_pop[future] try: success, msg = future.result() print(msg) if success: self.results["success"].append(pop['code']) else: self.results["failed"].append(pop['code']) except Exception as e: error_msg = f'ERROR -> POP:{pop["code"]} REASON:{str(e)}' print(error_msg) self.results["failed"].append(pop['code']) # 打印统计结果 print(f'预热任务结束 [{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}] -> {cf_url}{file_name}') print(f'成功的POP点({len(self.results["success"])}): {", ".join(self.results["success"])}') print(f'失败的POP点({len(self.results["failed"])}): {", ".join(self.results["failed"])}') print('======================================================================================') return self.results def get_pops(): with open(f'{os.path.split(os.path.abspath(sys.argv[0]))[0]}/cf_pops.json') as f: data = json.loads(f.read()) pops = [] for el in data['edge_locations']: try: for an in el['active_nodes']: pops.append({ 'code': an['code'], 'country': el['country'] }) except: pass return pops def lambda_handler(event, context): # 解析输入参数 file = json.dumps(event) file_name = json.loads(file)['filename'] # 文件名 cloudfront_url = json.loads(file)['cloudfront_url'] # CF URL distributions_id = cloudfront_url.split('.')[0] # 配置预热参数 warmer = CloudFrontWarmer( max_workers=32, # 并发数 ) # 要预热的 POP 点列表 cloudfront_pops = get_pops() # 执行预热 results = warmer.warm_all_pops( cloudfront_pops, distributions_id, cloudfront_url, file_name ) return { "statusCode": 200, "body": json.dumps({ "success_count": len(results["success"]), "failed_count": len(results["failed"]), "success_pops": results["success"], "failed_pops": results["failed"] }) } if __name__ == '__main__': cdn = "xxxxxxxx.cloudfront.net" # CDN域名 files = ["/1.apk", "/2.zip"] # 文件列表 for file in files: p = { "filename": file, "cloudfront_url": cdn } lambda_handler(p, "") ```` ## Jenkins 流水线运行 上面的脚本,可配合 Jenkins 使用 jenkinsfile: ```perl pipeline { agent any options { buildDiscarder(logRotator(numToKeepStr: '20')) } parameters { text(name: 'FILES', defaultValue: '', description: '文件路径,一行一个,根路径前要加/') choice(name: 'CDN', choices: ['xxxxxxxxxx.cloudfront.net'], description: 'CDN 域名') } stages { stage('文件检查') { steps { script { def fileList = env.FILES.split('\n').collect { it.trim() }.findAll { it } fileList.each { file -> def statusCode = sh( script: """ curl -I -s \ -o /dev/null \ -w "%{http_code}" \ "https://${env.CDN}${file}" """, returnStdout: true ).trim() if (statusCode == '200' || statusCode == '206') { echo "检查通过 -> ${env.CDN}${file}" } else { error "状态码异常(${statusCode}) -> ${env.CDN}${file}" } } } } } stage('CDN预热') { steps { script { env.FILES.split('\n').each { file -> echo "开始预热 -> ${env.CDN}${file}" sh """ python3 /var/jenkins_home/scripts/cloudfront_prewarm/cloudfront_prewarm_local.py \ -f '${file}' \ -d '${env.CDN}' """ echo "预热完成 -> ${env.CDN}${file} " } } } } } } ``` 修改 Python 脚本入口: ```python if __name__ == '__main__': parser = argparse.ArgumentParser(description="Cloudfront Prewarm Tool") parser.add_argument("-f", required=True, help="Files") parser.add_argument("-d", required=True, help="Domain") args = parser.parse_args() if args.f in ['null', '', None] or args.d in ['null', '', None]: print('输入参数不能为空白') sys.exit(-1) files = args.f.split(',') for file in files: p = { "filename": file, "cloudfront_url": args.d } lambda_handler(p, "") ```   ## AWS Lambda 运行 可使用参考资料【1】中的脚本 ## 参考资料 https://github.com/xiangqua/cloudfront-lambda-prewarm https://www.feitsui.com/zh-hans/article/3 最后修改:2024 年 11 月 04 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏