目录

  • 需求背景
    • 关于OSS静态网站托管
    • OSS静态网站托管之CDN缓存刷新问题
  • 开始着手解决
    • CDN-刷新预热
      • 预热刷新API
      • SDK技术选型
    • 基础流程
    • FC函数计算
      • 创建函数
      • Python HTTP函数
      • 主代码
        • 环境变量
        • GitHub
      • 请求样例
      • Python运行环境
      • 部署
  • 总结

需求背景

我负责的项目是一个前后端分离项目。最近将前端静态页面通过阿里云OSS的静态网站托管功能进行部署,并通过云效的流水线功能对NodeJS项目进行打包发布。
同时,将页面接入阿里云的CDN,从而最大限度地加速C端用户的访问速度。

关于OSS静态网站托管

通过OSS的静态网页托管功能,我们可以很方便地发布我们自己的静态页面,而无需关注运维层面的诸多问题。
同时,在使用静态网站托管时,有个默认首页的配置。简单来说,假设我网站文件为index.html。在未配置静态首页的情况下,访问http://www.aaa.com时,会返回404,我们需要手动访问http://www.aaa.com/index.html才能展示首页;而如果配置了静态首页为index.html时,访问http://www.aaa.com则会直接展示首页。

但是,默认首页结合了CDN之后,却出现了一些问题

OSS静态网站托管之CDN缓存刷新问题

原本阿里云很贴心的为OSS静态网站托管+CDN组合提供了一个CDN 缓存自动刷新功能,即在特定的文件操作后,OSS会自动通知阿里云CDN做缓存刷新。(详情参看帮助文档)

但是在进行相应配置后,开发同事向我反馈,上传新版本后,浏览器端未更新。

经过排查发现,当我们访问http://www.aaa.com/index.html时,文件的确已经更新了。但是我们的入口是http://www.aaa.com/,此时从header中可知访问的文件更新时间还是上传之前版本文件的时间。

查阅文档后发现,文档中有如下描述:

由生命周期触发的对象过期(Expire)、类型转换(TransitionStorageClass)操作不再支持CDN缓存刷新。使用CDN缓存自动刷新时有如下注意事项:

  • CDN缓存自动刷新功能提交的刷新URL为CNAME/ObjectName,不会刷新带请求参数的URL(图片处理、视频截帧等)。例如Bucket绑定的加速域名为example.com,当您更新Bucket根目录的a.jpg文件,则访问example/a.jpg可以获取最新文件;访问example.com/a.jpg?x-oss-process=image/w_100可能获取的还是旧文件。
  • CDN缓存自动刷新功能不保证一定能成功提交刷新任务,也不保证刷新任务提交的及时性。
  • CDN缓存自动刷新功能仅支持少量文件的更新提交刷新任务。如果有大量文件的更新操作,可能会触发流控丢弃部分刷新任务。

简而言之:OSS中的CDN缓存自动刷新功能刷新的是File,而不是Directory(二者的区别可以参看阿里云CDN文档刷新和预热资源,后面也会着重用到)

所以,要解决我们的问题,有2种思路:

  • 将入口改为http://www.aaa.com/index.html
  • 手动调用CDN的Directory刷新

本文中,讨论第二种思路

开始着手解决

CDN-刷新预热

通过刷新功能,您可以删除CDN节点上已经缓存的资源,并强制CDN节点回源站获取最新资源,适用于源站资源更新和发布、违规资源清理、域名配置变更等;通过预热功能,您可以在业务高峰前预先将热门资源缓存到CDN节点,降低源站压力提升用户体验。

CDN预热刷新官方文档

简单地说,CDN给我们提供了一个入口,可以在版本更新后,通知CDN及时刷新缓存。而接下来,我们要做到自动调用,就需要找到它对应的API/SDK

预热刷新API

经过查询,我们所需要的API接口为RefreshObjectCaches(刷新节点上的文件内容)
用我一位同事的话来说:阿里云对开发者太友好了,阿里云要统治世界了
的确是这样,接口文档页面直接给出了多语言的Demo

SDK技术选型

这种脚本类型的功能,自然使用Python最为合适啦!
下面是Demo中给出的完整代码

# -*- coding: utf-8 -*-
# This file is auto-generated, don't edit it. Thanks.
import sysfrom typing import Listfrom alibabacloud_cdn20180510.client import Client as Cdn20180510Client
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_cdn20180510 import models as cdn_20180510_modelsclass Sample:def __init__(self):pass@staticmethoddef create_client(access_key_id: str,access_key_secret: str,) -> Cdn20180510Client:"""使用AK&SK初始化账号Client@param access_key_id:@param access_key_secret:@return: Client@throws Exception"""config = open_api_models.Config(# 您的AccessKey ID,access_key_id=access_key_id,# 您的AccessKey Secret,access_key_secret=access_key_secret)# 访问的域名config.endpoint = 'cdn.aliyuncs.com'return Cdn20180510Client(config)@staticmethoddef main(args: List[str],) -> None:client = Sample.create_client('accessKeyId', 'accessKeySecret')refresh_object_caches_request = cdn_20180510_models.RefreshObjectCachesRequest(object_path='http://www.aaa.com/',object_type='Directory')# 复制代码运行请自行打印 API 的返回值client.refresh_object_caches(refresh_object_caches_request)@staticmethodasync def main_async(args: List[str],) -> None:client = Sample.create_client('accessKeyId', 'accessKeySecret')refresh_object_caches_request = cdn_20180510_models.RefreshObjectCachesRequest(object_path='http://www.aaa.com/',object_type='Directory')# 复制代码运行请自行打印 API 的返回值await client.refresh_object_caches_async(refresh_object_caches_request)if __name__ == '__main__':Sample.main(sys.argv[1:])

其实在用到FC函数计算功能之前,我还考虑使用云效流水线的自定义步骤的功能,但是不知道为什么本地调试成功,远端无法运行。这部分如果成功了我将单独写一篇文章说明

基础流程

由于我们使用云效的发布功能,在云效发布完成后,有一个Webhook通知功能,如下图

所以,我们从站点打包发布到自动化更新CDN缓存的基础流程如下

#mermaid-svg-a563MdLeeuKpMK9E .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-a563MdLeeuKpMK9E .label text{fill:#333}#mermaid-svg-a563MdLeeuKpMK9E .node rect,#mermaid-svg-a563MdLeeuKpMK9E .node circle,#mermaid-svg-a563MdLeeuKpMK9E .node ellipse,#mermaid-svg-a563MdLeeuKpMK9E .node polygon,#mermaid-svg-a563MdLeeuKpMK9E .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-a563MdLeeuKpMK9E .node .label{text-align:center;fill:#333}#mermaid-svg-a563MdLeeuKpMK9E .node.clickable{cursor:pointer}#mermaid-svg-a563MdLeeuKpMK9E .arrowheadPath{fill:#333}#mermaid-svg-a563MdLeeuKpMK9E .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-a563MdLeeuKpMK9E .flowchart-link{stroke:#333;fill:none}#mermaid-svg-a563MdLeeuKpMK9E .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-a563MdLeeuKpMK9E .edgeLabel rect{opacity:0.9}#mermaid-svg-a563MdLeeuKpMK9E .edgeLabel span{color:#333}#mermaid-svg-a563MdLeeuKpMK9E .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-a563MdLeeuKpMK9E .cluster text{fill:#333}#mermaid-svg-a563MdLeeuKpMK9E div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-a563MdLeeuKpMK9E .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-a563MdLeeuKpMK9E text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-a563MdLeeuKpMK9E .actor-line{stroke:grey}#mermaid-svg-a563MdLeeuKpMK9E .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-a563MdLeeuKpMK9E .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-a563MdLeeuKpMK9E #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-a563MdLeeuKpMK9E .sequenceNumber{fill:#fff}#mermaid-svg-a563MdLeeuKpMK9E #sequencenumber{fill:#333}#mermaid-svg-a563MdLeeuKpMK9E #crosshead path{fill:#333;stroke:#333}#mermaid-svg-a563MdLeeuKpMK9E .messageText{fill:#333;stroke:#333}#mermaid-svg-a563MdLeeuKpMK9E .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-a563MdLeeuKpMK9E .labelText,#mermaid-svg-a563MdLeeuKpMK9E .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-a563MdLeeuKpMK9E .loopText,#mermaid-svg-a563MdLeeuKpMK9E .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-a563MdLeeuKpMK9E .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-a563MdLeeuKpMK9E .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-a563MdLeeuKpMK9E .noteText,#mermaid-svg-a563MdLeeuKpMK9E .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-a563MdLeeuKpMK9E .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-a563MdLeeuKpMK9E .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-a563MdLeeuKpMK9E .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-a563MdLeeuKpMK9E .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-a563MdLeeuKpMK9E .section{stroke:none;opacity:0.2}#mermaid-svg-a563MdLeeuKpMK9E .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-a563MdLeeuKpMK9E .section2{fill:#fff400}#mermaid-svg-a563MdLeeuKpMK9E .section1,#mermaid-svg-a563MdLeeuKpMK9E .section3{fill:#fff;opacity:0.2}#mermaid-svg-a563MdLeeuKpMK9E .sectionTitle0{fill:#333}#mermaid-svg-a563MdLeeuKpMK9E .sectionTitle1{fill:#333}#mermaid-svg-a563MdLeeuKpMK9E .sectionTitle2{fill:#333}#mermaid-svg-a563MdLeeuKpMK9E .sectionTitle3{fill:#333}#mermaid-svg-a563MdLeeuKpMK9E .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-a563MdLeeuKpMK9E .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-a563MdLeeuKpMK9E .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-a563MdLeeuKpMK9E .grid path{stroke-width:0}#mermaid-svg-a563MdLeeuKpMK9E .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-a563MdLeeuKpMK9E .task{stroke-width:2}#mermaid-svg-a563MdLeeuKpMK9E .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-a563MdLeeuKpMK9E .taskText:not([font-size]){font-size:11px}#mermaid-svg-a563MdLeeuKpMK9E .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-a563MdLeeuKpMK9E .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-a563MdLeeuKpMK9E .task.clickable{cursor:pointer}#mermaid-svg-a563MdLeeuKpMK9E .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-a563MdLeeuKpMK9E .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-a563MdLeeuKpMK9E .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-a563MdLeeuKpMK9E .taskText0,#mermaid-svg-a563MdLeeuKpMK9E .taskText1,#mermaid-svg-a563MdLeeuKpMK9E .taskText2,#mermaid-svg-a563MdLeeuKpMK9E .taskText3{fill:#fff}#mermaid-svg-a563MdLeeuKpMK9E .task0,#mermaid-svg-a563MdLeeuKpMK9E .task1,#mermaid-svg-a563MdLeeuKpMK9E .task2,#mermaid-svg-a563MdLeeuKpMK9E .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-a563MdLeeuKpMK9E .taskTextOutside0,#mermaid-svg-a563MdLeeuKpMK9E .taskTextOutside2{fill:#000}#mermaid-svg-a563MdLeeuKpMK9E .taskTextOutside1,#mermaid-svg-a563MdLeeuKpMK9E .taskTextOutside3{fill:#000}#mermaid-svg-a563MdLeeuKpMK9E .active0,#mermaid-svg-a563MdLeeuKpMK9E .active1,#mermaid-svg-a563MdLeeuKpMK9E .active2,#mermaid-svg-a563MdLeeuKpMK9E .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-a563MdLeeuKpMK9E .activeText0,#mermaid-svg-a563MdLeeuKpMK9E .activeText1,#mermaid-svg-a563MdLeeuKpMK9E .activeText2,#mermaid-svg-a563MdLeeuKpMK9E .activeText3{fill:#000 !important}#mermaid-svg-a563MdLeeuKpMK9E .done0,#mermaid-svg-a563MdLeeuKpMK9E .done1,#mermaid-svg-a563MdLeeuKpMK9E .done2,#mermaid-svg-a563MdLeeuKpMK9E .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-a563MdLeeuKpMK9E .doneText0,#mermaid-svg-a563MdLeeuKpMK9E .doneText1,#mermaid-svg-a563MdLeeuKpMK9E .doneText2,#mermaid-svg-a563MdLeeuKpMK9E .doneText3{fill:#000 !important}#mermaid-svg-a563MdLeeuKpMK9E .crit0,#mermaid-svg-a563MdLeeuKpMK9E .crit1,#mermaid-svg-a563MdLeeuKpMK9E .crit2,#mermaid-svg-a563MdLeeuKpMK9E .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-a563MdLeeuKpMK9E .activeCrit0,#mermaid-svg-a563MdLeeuKpMK9E .activeCrit1,#mermaid-svg-a563MdLeeuKpMK9E .activeCrit2,#mermaid-svg-a563MdLeeuKpMK9E .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-a563MdLeeuKpMK9E .doneCrit0,#mermaid-svg-a563MdLeeuKpMK9E .doneCrit1,#mermaid-svg-a563MdLeeuKpMK9E .doneCrit2,#mermaid-svg-a563MdLeeuKpMK9E .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-a563MdLeeuKpMK9E .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-a563MdLeeuKpMK9E .milestoneText{font-style:italic}#mermaid-svg-a563MdLeeuKpMK9E .doneCritText0,#mermaid-svg-a563MdLeeuKpMK9E .doneCritText1,#mermaid-svg-a563MdLeeuKpMK9E .doneCritText2,#mermaid-svg-a563MdLeeuKpMK9E .doneCritText3{fill:#000 !important}#mermaid-svg-a563MdLeeuKpMK9E .activeCritText0,#mermaid-svg-a563MdLeeuKpMK9E .activeCritText1,#mermaid-svg-a563MdLeeuKpMK9E .activeCritText2,#mermaid-svg-a563MdLeeuKpMK9E .activeCritText3{fill:#000 !important}#mermaid-svg-a563MdLeeuKpMK9E .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-a563MdLeeuKpMK9E g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-a563MdLeeuKpMK9E g.classGroup text .title{font-weight:bolder}#mermaid-svg-a563MdLeeuKpMK9E g.clickable{cursor:pointer}#mermaid-svg-a563MdLeeuKpMK9E g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-a563MdLeeuKpMK9E g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-a563MdLeeuKpMK9E .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-a563MdLeeuKpMK9E .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-a563MdLeeuKpMK9E .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-a563MdLeeuKpMK9E .dashed-line{stroke-dasharray:3}#mermaid-svg-a563MdLeeuKpMK9E #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-a563MdLeeuKpMK9E #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-a563MdLeeuKpMK9E #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-a563MdLeeuKpMK9E #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-a563MdLeeuKpMK9E #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-a563MdLeeuKpMK9E #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-a563MdLeeuKpMK9E #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-a563MdLeeuKpMK9E #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-a563MdLeeuKpMK9E .commit-id,#mermaid-svg-a563MdLeeuKpMK9E .commit-msg,#mermaid-svg-a563MdLeeuKpMK9E .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-a563MdLeeuKpMK9E .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-a563MdLeeuKpMK9E .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-a563MdLeeuKpMK9E g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-a563MdLeeuKpMK9E g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-a563MdLeeuKpMK9E g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-a563MdLeeuKpMK9E g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-a563MdLeeuKpMK9E g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-a563MdLeeuKpMK9E g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-a563MdLeeuKpMK9E .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-a563MdLeeuKpMK9E .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-a563MdLeeuKpMK9E .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-a563MdLeeuKpMK9E .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-a563MdLeeuKpMK9E .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-a563MdLeeuKpMK9E .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-a563MdLeeuKpMK9E .edgeLabel text{fill:#333}#mermaid-svg-a563MdLeeuKpMK9E .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-a563MdLeeuKpMK9E .node circle.state-start{fill:black;stroke:black}#mermaid-svg-a563MdLeeuKpMK9E .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-a563MdLeeuKpMK9E #statediagram-barbEnd{fill:#9370db}#mermaid-svg-a563MdLeeuKpMK9E .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-a563MdLeeuKpMK9E .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-a563MdLeeuKpMK9E .statediagram-state .divider{stroke:#9370db}#mermaid-svg-a563MdLeeuKpMK9E .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-a563MdLeeuKpMK9E .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-a563MdLeeuKpMK9E .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-a563MdLeeuKpMK9E .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-a563MdLeeuKpMK9E .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-a563MdLeeuKpMK9E .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-a563MdLeeuKpMK9E .note-edge{stroke-dasharray:5}#mermaid-svg-a563MdLeeuKpMK9E .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-a563MdLeeuKpMK9E .error-icon{fill:#522}#mermaid-svg-a563MdLeeuKpMK9E .error-text{fill:#522;stroke:#522}#mermaid-svg-a563MdLeeuKpMK9E .edge-thickness-normal{stroke-width:2px}#mermaid-svg-a563MdLeeuKpMK9E .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-a563MdLeeuKpMK9E .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-a563MdLeeuKpMK9E .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-a563MdLeeuKpMK9E .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-a563MdLeeuKpMK9E .marker{fill:#333}#mermaid-svg-a563MdLeeuKpMK9E .marker.cross{stroke:#333}:root { --mermaid-font-family: "trebuchet ms", verdana, arial;} #mermaid-svg-a563MdLeeuKpMK9E {color: rgba(0, 0, 0, 0.75);font: ;}

NodeJS打包
发布到OSS
WebHook通知FC函数计算接口
FC函数计算代码调用SDK通知刷新CDN

FC函数计算

函数计算文档中,对该产品的描述如下:

函数计算是事件驱动的全托管计算服务。使用函数计算,您无需采购与管理服务器等基础设施,只需编写并上传代码。函数计算为您准备好计算资源,弹性地、可靠地运行任务,并提供日志查询、性能监控和报警等功能。
借助函数计算,您可以快速构建任何类型的应用和服务,并且只需为任务实际消耗的资源付费。

创建函数

创建函数的基础功能,请参照官方文档操作

Python HTTP函数

函数计算的函数类型分为2种

  • 事件函数
  • HTTP函数

而根据我们基础流程的设计,要求我们需要使用HTTP函数
具体介绍如下:

函数计算系统会将用户的请求,包括Method、Path、Body、Query及Headers和函数计算系统生成的Common Headers转发给HTTP Server。您可以平滑迁移已有的HTTP Web应用。更详细的介绍,请参见简介。

具体到Python,请参看Python HTTP函数

Python Runtime中函数的签名遵循WSGI(Python Web Server Gateway Interface)规范。您可以使用WSGI规范对请求进行处理。

主代码

根据上面SDK提供的DEMO,结合HTTP函数文档,可用的Python代码如下

# 用于CDN刷新节点上的文件内容,主程序入口参数依次为:AK,SK,PATH,TYPE
# 详见接口文档:https://help.aliyun.com/document_detail/91164.html
import sys
import os
import logging
import urllib.parse
from Tea.core import TeaCorefrom alibabacloud_cdn20180510.client import Client as Cdn20180510Client
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_cdn20180510 import models as cdn_20180510_models
from alibabacloud_tea_console.client import Client as ConsoleClient
from alibabacloud_tea_util.client import Client as UtilClientlogging.basicConfig(level=logging.DEBUG,format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s'
)  # logging.basicConfig函数对日志的输出格式及方式做相关配置class DirFlush:def __init__(self):pass@staticmethoddef create_client(access_key_id: str,access_key_secret: str,) -> Cdn20180510Client:"""使用AK&SK初始化账号Client@param access_key_id:@param access_key_secret:@return: Client@throws Exception"""config = open_api_models.Config(# 您的AccessKey ID,access_key_id=access_key_id,# 您的AccessKey Secret,access_key_secret=access_key_secret)# 访问的域名config.endpoint = 'cdn.aliyuncs.com'return Cdn20180510Client(config)@staticmethoddef main(access_key_id: str,access_key_secret: str,object_path: str,object_type: str = 'File',):"""刷新/预热CDN方法:param access_key_id: ak 必填:param access_key_secret: sk 必填:param object_path: 目录,多个目录之间使用换行符分割 必填:param object_type: 刷新类型。取值:File:文件。 Directory:目录。:return:"""client = DirFlush.create_client(access_key_id, access_key_secret)refresh_object_caches_request = cdn_20180510_models.RefreshObjectCachesRequest(object_path=object_path,object_type=object_type)resp = client.refresh_object_caches(refresh_object_caches_request)resp_str = UtilClient.to_jsonstring(TeaCore.to_map(resp))ConsoleClient.log(resp_str)return resp_str@staticmethodasync def main_async(access_key_id: str,access_key_secret: str,object_path: str,object_type: str = 'File',) -> None:"""刷新/预热CDN方法(异步):param access_key_id: ak 必填:param access_key_secret: sk 必填:param object_path: 目录,多个目录之间使用换行符分割 必填:param object_type: 刷新类型。取值:File:文件。 Directory:目录。:return:"""client = DirFlush.create_client(access_key_id, access_key_secret)refresh_object_caches_request = cdn_20180510_models.RefreshObjectCachesRequest(object_path=object_path,object_type=object_type)resp = await client.refresh_object_caches_async(refresh_object_caches_request)ConsoleClient.log(UtilClient.to_jsonstring(TeaCore.to_map(resp)))# To enable the initializer feature (https://help.aliyun.com/document_detail/158208.html)
# please implement the initializer function as below:
# def initializer(context):
#    logger = logging.getLogger()
#    logger.info('initializing')def handler(environ, start_response):"""Web调用入口遵循WSGI规范用于阿里云FC函数调用功能调用通过Get请求的requestParam分别写入如下参数path(刷新目录,如:http://www.baidu.com/)type(刷新类型,枚举:File:文件  Directory:目录。):param environ: 通过requestParam分别写入path(刷新目录):param start_response::return: 返回执行结果"""request_param = urllib.parse.parse_qs(environ['QUERY_STRING']) # 获取请求参数中的Paramlogging.info("requestParam:[%s]", request_param)ak = os.getenv("CDN_AK")sk = os.getenv("CDN_SK")path = request_param["path"][0]object_type = "File" if request_param.get("type") is None else request_param["type"][0]object_type = "File" if object_type != "Directory" and object_type != "File" \else object_type  # 用来为object_type赋予一个默认值logging.info("ak:[%s],path:[%s],type:[%s]", ak, path, object_type)try:result = DirFlush.main(ak, sk, path, object_type)except IOError as err:logging.error(err)raise errstatus = '200 OK'response_headers = [('Content-type', 'application/json')]start_response(status, response_headers)return [result.encode()]if __name__ == '__main__':"""主程序入口"""if len(sys.argv[1:]) < 4:DirFlush.main(sys.argv[1], sys.argv[2], sys.argv[3])else:DirFlush.main(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])

环境变量

由于access_key_id和SK为敏感数据,为了尽量保证其安全,我们通过环境变量来获取。环境变量可以在函数设置中配置。环境变量如下:

  • CDN_AK:对应access_key_id
  • CDN_SK:access_key_secret

GitHub

在GitHub中有个独立项目,根目录下的server.py是一个http规范的调试脚本,本地调试可参照改脚本进行操作
Github项目aliyunApiTools

请求样例

我们通过下面2个Key放在Get请求的Request Param中,完成我们的请求

  • path:代表需要刷新的目录或文件
  • type: 代表刷新类型,如果不传则默认为File
curl --location --request GET 'http://127.0.0.1:8000?path=http://www.aaa.com/&type=Directory'

这里要注意的是,当需要刷新目录时,path应当以斜杠(/)结尾,否则会报错:

Tea.exceptions.TeaException: Error: InvalidObjectPath.Malformed code: 400, The specified ObjectPath is invalid. request id: 659EB0E6-CB0D-5141-9696-E92BE5DE2570 Response: {‘RequestId’: ‘659EB0E6-CB0D-5141-9696-E92BE5DE2570’, ‘HostId’: ‘cdn.aliyuncs.com’, ‘Code’: ‘InvalidObjectPath.Malformed’, ‘Message’: ‘The specified ObjectPath is invalid.’, ‘Recommend’: ‘https://error-center.aliyun.com/status/search?Keyword=InvalidObjectPath.Malformed&source=PopGw’}

Python运行环境

根据Python运行环境一文所示,函数计算环境所需的依赖包部分需要自己安装。而安装依赖的方式也有多种(参看Python运行环境中的“自定义模块”一节)。而我在这里使用pip包管理器进行依赖管理的方式。
关于当前类,我总结了下面几个环境是需要安装的,pip代码如下

pip3 install -t . alibabacloud_cdn20180510 --no-user
pip3 install -t . urllib --no-user
pip3 install -t . idna_ssl --no-user
pip3 install -t . alibabacloud_tea_console --no-user

部署

安装完环境后,打包整个目录并上传,即可在编辑页面进行部署了

注意,将函数设置中的入口函数更改一下

建议使用自定义域名功能进行管理

部署完成后,即可调用了

总结

  • 这次尝试帮我们打开一个新的思路:对于这种脚本化运行的自动化代码,可以尝试用函数计算功能实现,降低成本的同时也获得了较高的可用性
  • 当然,我们还有很多的方式,比如流水线里有个自定义步骤的功能
  • Github上我会维护更多的功能,有需要的伙伴可以关注一下

【阿里云原生应用】使用阿里云FC函数计算完成阿里云CDN目录刷新相关推荐

  1. 重磅发布 | 30+ 阿里巴巴云原生「顶流」,给你一堂《云原生技术实践公开课》

    以"云"为核心的软件研发思想,正逐步成为所有开发者的默认选项.像 Kubernetes 等云原生技术正在成为技术人员的必修课,大量的工作岗位正在涌现出来.2020 年,云原生技术大 ...

  2. “云原生”技术公开课第1章:第一堂“云原生”课

    摘要:欢迎大家来到阿里云与 CNCF 共同推出的"云原生"技术公开课.本文整理自"云原生"技术公开课的开篇:第一堂"云原生"课.在本文中,阿 ...

  3. 云原生与微服务架构基础:01 | 为什么说云原生重构了互联网产品开发模式

    为什么说云原生重构了互联网产品开发模式? 云原生的概念 云计算的前世今生 阶段1:虚拟化技术 阶段2:虚拟机的市场化应用 阶段3:容器化和容器编排的兴起 云原生到底是什么? 云原生出现的背景 云原生解 ...

  4. 强化云原生基础服务,焱融科技 YRCloudFile 与秒云完成产品兼容性互认证

    随着中国云计算市场整体规模快速增长,云原生正在成为企业数字化转型的重要引擎.为了助力企业用户全面上云,加快云原生应用的落地进程.近日,北京焱融科技有限公司(简称"焱融科技")与成都 ...

  5. 未来云原生世界的“领头羊”:容器批量计算项目Volcano 1.0版本发布

    在刚刚结束的CLOUD NATIVE+ OPEN SOURCE Virtual Summit China 2020上,由华为云云原生团队主导的容器批量计算项目Volcano正式发布1.0版本,标志着V ...

  6. 高校如何基于云原生构建面向未来的智慧校园?全栈云原生VS传统技术架构

    2022年全国教育工作会议以及<教育部2022年工作要点>明确提出,实施教育数字化战略行动."数字化转型"作为我国教育领域的一项重要改革举措,将为教育高质量发展注入新动 ...

  7. 云原生虚拟化:基于 Kubevirt 构建边缘计算实例

    导读 随着 Kubernetes 的普及,越来越多的业务开始运行在容器上,但是仍有部分业务形态更适合运行在虚拟机,如何同时管控虚拟机和容器逐渐成为了云原生时代的主流需求, Kubevirt 给出了完美 ...

  8. 函数计算如何粘合云服务,提供端到端解决方案

    作者 | 西流  阿里云技术专家 导读:阿里云 Serverless 产品函数计算可以作为粘合剂,串联其他云服务提供端到端解决方案,从而简化编程模型,快速实现最上层的业务目标. 传统单体应用的拆解 首 ...

  9. 【云原生 | 31】Docker运行实时流计算框架Apache Storm

    作者简介:

最新文章

  1. 如何使用htmlq提取html文件内容
  2. Linux下服务器端开发流程及相关工具介绍(C++)
  3. pywebio 之词云图
  4. 【深度学习】2个经典的练手CNN源码与MNIST数据集测试结果
  5. 京东 你访问的页面需要验证证书_SSL证书安全认证有什么原理?
  6. 制作支付页面弹框html,JS实现仿微信支付弹窗功能_蜡烛_前端开发者
  7. Exchange 服务器可支持性矩阵
  8. asterisk 常用命令
  9. 苹果折叠iPhone终于有动作了!已送样至富士康,售价将超万元
  10. 基于JAVA+SpringMVC+Mybatis+MYSQL的图书馆预约占座管理系统
  11. 了解 sourceMap 配置
  12. 不懂性能测试,被面试官挂了...
  13. C#的TextBox控件输入测试-只允许输入数字的测试:
  14. PHP 错误与异常 笔记与总结(1)错误(Deprecated,Notice,Warning)
  15. tensorflow学习笔记(4)softmax分类和简单神经网络比较
  16. CS229 笔记-1
  17. UNIX再学习 -- RS485 串口编程
  18. 自学计算机基础知识需要什么书,学习计算机基础知识,我强烈推荐这三本书!...
  19. 2022081班李亚楠20220914
  20. Android TabLayout设置选中状态标题字体大小,粗细

热门文章

  1. 台式计算机可以看视频吗,台式电脑看视频突然关机怎么解决
  2. MB1C MIGO入库时【不可能为条目A999 GBB CN01 BSA 7920确立帐户】解决方案
  3. Mogilefs学习
  4. 计算机专业开日语选修课,大学最“值钱”的四门选修课,选上了真幸运,上课别“摸鱼”...
  5. C语言字符串复制函数strcpy()的编写与详解
  6. VC/MFC DDX和DDV机制介绍
  7. linux红帽安装qq,Linux如何安装QQ软件_Centos_redhat_ubuntu
  8. 推荐系统 vs 搜索引擎
  9. qt repaint 用法_qt的repaint的问题
  10. ould not find method toSetting(View) in a parent or ancestor Context for android:onClick attribute d