本文由百度智能云-视频云-内容分发加速技术架构师——高岩 在百度开发者沙龙线上分享的演讲内容整理而成。内容从CDN应用Serverless的意义出发,详细介绍EdgeJS Serverless服务。
文/ 高岩
整理/ 百度开发者中心
视频回放:https://developer.baidu.com/live.html?id=11
本次分享的主题是:CDN边缘JavaScript敏捷交付实践,内容主要分为以下三个方面:
-
CDN应用Serverless的意义
-
EdgeJS Severless服务
-
沉浸式CDN编程体验
01
CDN应用Serverless的意义
CDN基本介绍
CDN含义:
是指一组分布在不同地理位置的服务器,协同工作以提供互联网内容的快速交付。
CDN服务已得到不断普及。如今,大多数web流量都通过CDN提供服务,几乎所有的门户网站、常用的视频 APP(例如,爱奇艺、抖音)都会用 CDN 架构实现更加快速的内容分发,让用户更快地看到视频内容,带来更好的用户体验。
这是因为 CDN 允许快速传输、加载互联网内容所需要的资源。以门户网站为例,我们需要加载 HTML 页面、JavaScript、文件 css 等资源;而视频网站则需要加载缩略图、视频文件。
CDN 还可帮助保护网站免受某些常见的恶意攻击,例如分布式拒绝服务(DDOS)攻击。
使用CDN优势:
1. 缩短网站加载时间
通过将内容分发到访问者附近的CDN服务器(以及其他优化措施),访问者体验到更快的页面加载时间。由于访问者更倾向于离开加载缓慢的网站,CDN 可以降低跳出率并增加人们在该网站上停留的时间。换句话说,网站速度越快,用户停留的时间越长。
2. 减少带宽成本
网站托管的带宽消耗成本是网站的主要费用。通过缓存和其他优化,CDN 能够减少源服务器必须提供的数据量,从而降低网站所有者的托管成本。
3. 增加内容可用性和冗余
大流量或硬件故障可能会扰乱正常的网站功能。由于CDN具有分布式特性,因此与许多源服务器相比,CDN 可以处理更多流量并更好地承受硬件故障。
4. 改善网站安全性
CDN 可以通过提供鉴权、安全证书的改进以及其他优化措施来提高安全性。
vCDN的发展
早在2009年,伯克利曾针对当时兴起的云计算做过评论,并提出了以下6个潜在的优点:
-
(理论上)无限可用的计算资源,可在资源池中实现任意的伸缩。
-
用户再也不需要承担服务器运维的工作和责任。
-
服务的按需付费成为可能。
-
超大型数据中心的使用成本显著降低。
-
通过可视化资源管理,运维操作的难度大大降低。
-
分时复用,物理硬件的利用率大大提高。
基于云计算的理念,可以实现一个虚拟化CDN(vCDN), 即可在专有、裸金属、虚拟化或基于容器的基础设施上运行CDN。vCDN作为云上的一个应用,是CDN和云紧密结合的产品。其主要功能特性包括:
硬件虚拟化:虚拟化基础架构使软件和硬件功能得以分解,服务器运维大大简化。
低延迟:在共享基础设施上运行CDN功能,可以更快的调起其他应用,比如可以实时进行图片处理。
弹性伸缩:可以按需使用CDN,在流量高峰和低峰,进行自动的弹性扩容和缩容。
但是云计算技术发展到今天,虚拟化并不能解决所有的问题,
在对性能有特别高要求的场景下,面临的主要难点如下:
-
虚拟机/容器,构建业务应用运维成本较高。
-
不能做到按需付费,仍然需要独占虚拟机。
-
开发复杂,需要很多其它的依赖,在开发业务的过程中,需要数据库、对象存储等框架。需要自己掌控运维和使用情况,开发难度较高。
Serverless介绍与特点
为了解决上述问题,亚马逊的 AWS 在 2015 年推出了 lambda 服务,提出了Cloud Function的概念,引起了业界对于 Serverless 的关注广泛。
Serverless 主要包含 Faas、Baas 两种形态。其中,Faas 将 Function 作为服务,Baas 将后端服务作为服务。
在应用了 vCDN 后,也可以用 Baas 的方式运行服务。Serverless 旨在让开发人员不需要再关注服务器,云会帮自动实现服务器的运维和伸缩。
具体而言,Serverless 具有以下特点:
1. 计算的无状态化,服务的储存和计算完全分开部署的,开发者只需要关心计算的实现,可以通过其它云上的独立服务进行储存,容易进行迁移和扩缩容。
2. 资源透明化的,无需要再关心服务器、虚拟机、容器需要多少资源、带宽、磁盘空间,可以通过调用函数在平台内实现资源的自动管理。
3. 按需计费,根据调用时长、调用次数进行计费。目前,vCDN 可以为客户提供 Baas 服务。如果用户需要更通用的函数计算产品,推荐使用百度云的 CFC,可以配置 CDN 的触发器。
CDN应用Serverless的意义
早在 2018年,云管理公司 RightScale 开展的一项调查显示,Serverless 是增长最快的公共云服务。
据统计,AWS 上超过一半的用户已经在使用 Serverless 服务。Serverless 一直在高速发展,呈现出越来越大的影响力。
Serverless 将无处不在,CDN也必须拥抱Serverless理念,提供边缘可编程能力,使用户可以在控制台上通过 API 设置代码,形成编程能力,更有效地控制 CDN。
在 CDN 业务中实践 Serverless 理念可以提供敏捷交付的能力,具有以下优势:
-
编程能力 对于刚接触CDN的客户来说,可编程能力,即便不理解CDN的具体运作流程,也能快速编写出可用的代码。
-
敏捷交付 对于CDN研发人员,serverless节省了他们部署和运维的时间,让他们能够更加专注于解决和优化应用本身的问题。
-
场景下沉结合编程能力,可以和其他场景更方便的结合在一起。
-
边缘计算在CDN的边缘,可以进行更加自由的计算,节省端上处理的时间和延迟。
02
EdgeJS Serverless服务
EdgeJS Serverless服务目标
Serverless 的目标是使用户可以更容易地编写和部署代码,而无需关注底层结构。尽管目前的 CDN 业务可以实现按需付费,但是仍然不够灵活。
为此,我们推出了 EdgeJS Serverless 服务。该服务在百度智能云 CDN 上使用JavaScript 语言去提供的一种可编程的配置能力,实现高并发、低成本的敏捷交付,使 CDN 能够体现出 serverless 的思想,进行靠拢或者是计划。
该服务具有以下特点:
-
嵌入式 JavaScript runtime,而非独立的 runtime。出于对性能的考虑,在支持 JS 标准库的同事,避免了独立 runtime 带来的冷启动时间。
-
提供请求对象 request,可以在代码中进行随意更改。
-
提供对外访问能力。
作为一种 Serverless 服务,EdgeJS 需要实现用户隔离、具备较高的性能,能够动态编码。具体而言,EdgeJS 具有以下特性:
-
动态编码。
-
即时编译。即用户代码在边缘区编译之后,可以被缓存,无需进行重复编译,理论上大大提升了执行的效率。
-
风险隔离。即严格隔离不同用户的代码,为用户可以使用的资源设置最高上限。
EdgeJS Serverless服务的特性
EdgeJS致力于让CDN更易用,向serverless服务能力迈进。所以EdgeJS的定位一开始就是完全免费,在CDN按需使用的带宽费用之外,不会产生额外的费用。
在降低CDN使用门槛的初衷下,必须还得保持CDN的高可用、低延迟、就近服务的能力。
EdgeJS的设计,是完全嵌入到CDN接入层的JavaScriptruntime,无需冷启动时间,没有性能损失,并支持标准ES6语法的JavaScriptAPI,各种特性陆续补齐中。
而且相比传统交付,开发上线至少周级别的交付周期,EdgeJS真正能做到秒级交付,用户在控制台配置上代码,就可以秒级生效。当然建议在正式发布前,先使用预发布功能来灰度验证。EdgeJS,可以根据请求进行各种特征处理,这极大丰富了CDN接入的场景。
为了更好地配合 CDN 业务,EdgeJS 具有以下特性:
-
使用 EdgeJS 在边缘进行计算(如一些特殊的鉴权等不能缓存的动态需求),将预计算任务部署在边缘设备上,大大减轻源站的压力。
-
做到秒级交付,使用户在控制台上配置代码,秒级生效。
-
根据请求进行各种特征处理,极大丰富 CDN 接入场景(包括不限于跨域访问、重定向、访问控制、单请求限速、自定义鉴权、m3u8改写、请求改写、A/BTest自定义错误页面等。
除此之外,EdgeJS还可以利用fetch等能力,和远端进行协同,包括不限于远程鉴权、云服务协同和请求画像打点上传。这些能力已经远超CDN的传统场景,向serverless服务能力靠拢。
在CDN庞大的算力加持下,可以减轻源站的性能压力和支出。而且,当增加了新的特征或者增加了新的计算方法,可以随时修改JavaScript代码,进行实时控制。
03
沉浸式CDN编程
url改写与复杂文件名改写
CDN 控制台本身支持一些 URL 改写的基本功能,但这些预定义的功能灵活性较低。我们可以通过 EdgeJS 根据用户的要求确定配置。EdgeJS 使用标准的 JS 语法,需要用户建立一个请求对象 request。
如上图所示,我们首先将 URL 中的大写字母转成小写,接着我们将请求的 variables 参数改成小写。variable 映射的是 UNIX 的变量,而这的规则完全一致的。
EdgeJS 支持复杂的文件名改写,这里涉及到三种情况:
(1)参数 attname 存在且不为空字符串
(2)参数 attname 存在且为空字符串
(3)参数 attname 不存在。
回源鉴权头
在一些对象存储场景下,我们可以通过 EdgeJs 构建回源鉴权头 authorization。首先生成一个随机数,获取当前时间,并生成请求 URL。接着,我们利用以上三者根据 crypto 算法生成鉴权头。
我们可以通过请求的 headersIn 特性获取请求头。有些特殊的请求头只能存在一份,如果重复则会被忽略(例如,host、connection,详见官网)。此外,重复的 Cookie 的请求头会返回所有的重复部分,并以分号分隔开来。如果我们想要获取所有的请求头,我们需要使用 rawHeadersIn 特性,如果请求头有多个,则会输出数组。
r.headersIn{}
请求头对象,可写Foo请求头可以使用r.headersIn.foo或者r.headersIn[‘Foo’]来访问
“Host”, “Connection”, “If-Modified-Since”, “If-Unmodified-Since”, “If-Match”, “If-None-Match”, “User-Agent”, “Referer”,
“Content-Length”, “Content-Range”, “Content-Type”, “Range”, “If-Range”, “Transfer-Encoding”, “TE”, “Expect”,
“Upgrade”, “Accept-Encoding”, “Via”, “Authorization”, “Keep-Alive”, “X-Real-IP”, “Accept”, “Accept-Language”, “Depth”,
“Destination”, “Overwrite”, “Date”这些请求头只能有一个,重复的会被忽略
重复的“Cookie”请求头,会返回所有的重复部分,并以分号(;)分隔,
重复的其他请求头,会返回所有的重复部分,并以逗号(,)分隔,
r.headersIn.foo =’foo’,赋值会覆盖所有的重复部分。
r.headersIn [‘Foo’]= [‘a’, ‘b’],赋值数组,会产生两个重复的请求头:
Foo:a和Foo:b
r.rawHeadersIn{}
请求头KV Array,只读。
比如请求头Host:localhost;Foo: bar ;foo: bar2
r.rawHeadersIn输出类似于[‘Host’, ‘localhost’], [‘Foo’, ‘bar’], [‘foo’, ‘bar2’]
获取所有的请求头foor.rawHeadersIn.filter(v=>v[0].toLowerCase()
== ‘foo’).map(v=>v[1])
输出[‘bar’, ‘bar2’]
文件名改写
EdgeJS Serverless 服务通过请求的 headersOut 特性可以实现文件名改写、跨域访问、设置相同的响应头等功能。
如上图所示,我们可以使用请求参数 filename 命名下载文件,使用请求头 Origin 赋值给跨域响应头 Access-Control-A。在设置相同的响应头时,我们可以通过赋值数组,产生重复的响应头。
r.headersOut{}响应头对象,可写Foo响应头可以使用r.headersOut.foo或者r.headersOut[‘Foo’]来访问。
“Server”, “Date”, “Content-Length”, “Content-Encoding”,
“Location”, “Refresh”, “Last-Modified”,
“Content-Range”, “Accept-Ranges”,
“WWW-Authenticate”, “Expires”, “E-Tag”, “ETag”, “Content-Type”, “X-Override-Charset”,
“Cache-Control”, “Link”, “Age”,
“Retry-After”,这些响应头只能有一个,重复的会被忽略
重复的”Set-Cookie”响应头,会返回一个数组,例如,r.headersOut[‘Set-Cookie’].forEach
(element=> console.log(element));
重复的其他响应头,会返回所有的重复部分,并以逗号(,)分隔
r.headersOut.foo =’foo’,赋值会覆盖所有的重复部分。
r.headersOut [‘Foo’]= [‘a’, ‘b’],赋值数组,会产生两个重复的响应头:
Foo:a和Foo:b
r.rawHeadersOut{}
响应头KV Array,只读
用法类似于r.rawHeadersIn{}
自定义错误页面
我们可以使用 EdgeJS 实现自定义的错误页面,当源站返回 404 时,可以重定向到一个对用户友好的页面。
如上图所示,我们通过 respHeader 回调实现上述功能。类似地,我们可以通过 respHeader 实现 A/B 测试,
如果源站返回了特殊头 a,可以命中一个升级的逻辑,重定向到一个应用升级的页面。
IP黑名单
EdgeJS 支持通过百度自有的库提供一些常用的访问控制功能:
(1)IP 黑名单,如果客户端地址在 192.168.1.1/32 或 192.168.2.1/24 等ip段内,则返回 403。
(2)Referer 白名单,如果 referer 不匹配某些通配符的形式,则返回 403。
(3)UA 黑名单,如果 UA 包含 curl 或 AppleWebKit,则返回 403。
代码示例:
r.remoteAddress
客户端地址,只读
baidu_utils库
function ipInCidr(ipv4,cidrs)
参数:
ipv4为点分十进制的ipv4地址,比如’192.168.2.100′
cidrs为CIDR地址列表,比如[‘192.168.1.1/32′,’192.168.2.1/24’]
使用示例:
if (baidu_utils.ipInCidr (‘192.168.2.100’,[‘192.168.1.1/32′,’192.168.2.1/24’])) {
r.return(403);
}
function matchWildcard(str,rule)
参数:
str为待匹配的字符串,比如’http://www.baidu.com/’
rule为有通配符的字符串,比如’http://*.baidu.com/*’
使用示例:
if (baidu_utils.matchWildcard(‘http://www.baidu.com/’, ‘http://*.baidu.com/*’)
{
r.return(403);
}
鉴权
百度云基于 EdgeJS 提供了 B 类防盗链等鉴权功能。原始的 URL 包含协议头 HTTP、域名、文件名。在加密之后,URL 变成了协议头、域名、时间戳、MD5 编码,文件名。
在上图的第一段代码中,CDN 服务器收到请求之后,首先拆分 URL,得到时间戳、MD5 、文件名。
在第二段代码中,时间戳的格式并非 Unicode,而是可读的格式,我们需要进行时间戳格式的转换,将秘钥与时间戳、文件名拼接,并进行 MD5 加密编码的比对。
子请求
CDN本身是一个缓存体系,对于hls或者dash来说,其索引文件中包含的防盗链信息,在用户请求的时候很可能过期了,所以在用户请求的m3u8或者mpd的防盗链验证通过之后,需要将这个请求中的防盗链信息改写到索引文件内容中。
Fetch
Fetch
ngx.fetch(url, [options])
类似于JavaScript原生的Fetch,请求URL,并返回解析Response对象的Promise。参考Using_Fetch
目前仅支持http协议,重定向需要调用者处理
类似于js fetch,options支持body/ headers / method。
ssl选项如下:
ssl_name 指定sni,默认为url中的域名
ssl_verify是否开启证书校验,默认开启
Response
标准的JavaScript内置对象
我们可以通过 EdgeJS Serverless 服务实现 Fetch 功能,请求远程鉴权服务器,根据远程服务器的响应进行处理,如果得到非 2xx 状态码,则返回 403 禁止访问。Fetch 返回的是一个 Promise 对象,包含类方法,可以将异步的操作变为序列化的操作,只要需要关心业务逻辑。与 Fetch 一同上线的还有 Await、SubtleCrypto 等功能。
以上是老师的全部分享内容,有任何问题可以在讨论区提出。
扫描二维码,备注:音视频开发,立即加入音视频开发技术交流群。
{{m.name}}
原创文章,作者:3628473679,如若转载,请注明出处:https://blog.ytso.com/201262.html