作者:@万明珠
喜欢这篇文章的话,就点个关注吧,会持续分享高质量Python文章,以及其它相关内容。
当我们打开浏览器访问页面的时候,其实就是一个不断发送 HTTP 请求的过程,客户端发送 HTTP 请求,服务端返回响应。这里的客户端可以是浏览器,也可以是我们使用 Python 编写的代码,而服务端则是使用 Web 框架编写的应用程序。
说到这儿,补充一个知识点,很多搞 Python 的小伙伴可能分不清 WSGI, uwsgi, uWSGI, Nginx 之间的区别,我们来总结一下。
WSGI
WSGI 的全称是 Web Server Gateway Interface,即 Web 服务器网关接口,它不是服务器,也不是 Python 模块、框架、或者任何软件,它只是一种描述 Web 服务器和 Web 应用程序(使用 Web 框架编写的程序)进行通信的规范、协议。基于 Web 框架编写的服务要运行在 Web 服务器上,尽管这些框架本身自带了一个小型 Web 服务器,但只用于开发和测试。
uWSGI
uWSGI 是一个 Web 服务器,它实现了 WSGI, uwsgi, HTTP 等协议,所以我们把使用 Web 框架编写好的服务部署在 uWSGI 上面,是可以直接对外提供服务的。
Nginx
同样是一个 Web 服务器,但它相比 uWSGI 可以提供更多的功能,比如反向代理、负载均衡、缓存静态资源、对 HTTP 请求更加友好,这些都是 uWSGI 所不具备、或者不擅长的。所以将 Web 服务部署在 uWSGI 之后,还要在前面再搭一层 Nginx。此时 uWSGI 就不再暴露 HTTP 服务了,而是暴露 TCP 服务,因为它是和 Nginx 进行通信,使用 TCP 会更快一些,Nginx 来对外暴露 HTTP 服务。
uwsgi
uwsgi 是 Nginx 和 uWSGI 通信所采用的协议,我们说 uWSGI 是和 Nginx 对接。Nginx 在接收到用户请求后,如果请求的是图片等静态资源,那么可以直接返回。请求的是动态资源,那么会将请求转发给 uWSGI,然后再由 uWSGI 调用相应的 Web 服务进行处理,处理完毕之后将结果交给 Nginx,Nginx 再返回给客户端。
而 uWSGI 和 Nginx 之所以能交互,也正是因为它们都支持 uwsgi 协议,Nginx 内部有一个模块叫 HttpUwsgiModule,它的作用就是与 uWSGI 服务器进行交互。
回到正题,我们知道 HTTP 是请求-响应模型,当使用浏览器打开一个页面时,浏览器和目标服务器便是 HTTP 协议的两个端点。
那么问题来了,浏览器发出的请求,一定要直接到达指定的目标服务器吗?可不可以在其它地方先逗留一下呢?答案是可以的,这个地方就是代理。
在 HTTP 协议中,代理(Proxy)是请求方和应答方中间的一个环节。作为中转站,既可以转发客户端的请求,也可以转发服务器的应答。代理的种类很多,常见的有:
匿名代理:完全隐匿了被代理的机器,外界看到的只是代理服务器
透明代理:顾名思义,它在传输过程中是透明开放的,外界既知道代理,也知道客户端
正向代理:靠近客户端,代表客户端向服务器发送请求
反向代理:靠近服务器,代表服务器向客户端返回响应
关于正向代理和反向代理,我们再举两个例子解释一下。
正向代理
假设你想找 B 借一样东西,但是 B 不同意,于是你拜托 A 去从 B 那里借,然后再交给你。这里的 A 就扮演了代理的角色,也是正向代理,因为真正找 B 借东西的是 A。
如果 A 在找 B 借东西的时候没有说这是你想借的,那么 A 就是匿名代理,因为 B 不知道你的存在;如果 A 告诉了 B,其实是你拜托他来找 B 的,那么 A 就是透明代理,B 知道 A,同时也知道你。
像我们平常挂的 V批N
就是正向代理,当你访问谷歌却惨遭拒绝时,你可以让 V批N
去帮你访问。而对于谷歌而言,向它发请求的是 V批N
,不是你。
反向代理
反向代理也很简单,比如访问百度,其背后可能有千千万万台服务器在提供服务,但我们不会直接访问它们,而是访问反向代理服务器。www.baidu.com 所在的就是代理服务器,它会帮我们把请求转发到真实的服务器那里去。像 Nginx 就是一个非常好的反向代理服务器,可以对背后所有真实的服务器进行一个权衡,将请求转发到一个合适的服务器上,也就是所谓的负载均衡。
再比如小明同学联系老鸨,希望她能提供一个小姐姐上门帮忙补习外语,这个老鸨就是反向代理,她会将小明的请求转发到某一个小姐姐那里去。
所以正向代理和反向代理都属于代理,而核心区别就在于代理的对象不同:正向代理代理的是客户端,负责向服务端发送请求;反向代理代理的是服务端,负责向客户端返回响应。
由于代理在传输过程中插入了一个中间层,因此可以在这个环节做很多有意思的事情,比如:
负载均衡:把访问的请求均匀分散到多台机器,实现访问集群化
内容缓存:暂存上下行的数据,减轻后端的压力
安全防护:隐匿 IP,使用 WAF 等工具抵御网络攻击,保护被代理的机器
数据处理:提供压缩、加密等额外的功能
了解了以上内容,就能很清楚地知道什么是 CDN 了。
CDN 的全称为 Content Delivery Network,翻译过来就是内容分发网络,它应用了 HTTP 协议里的缓存和代理技术,代替源站服务器响应客户端的请求。所以 CDN 也属于代理,并且通常扮演透明代理和反向代理的角色。
说白了就是它可以缓存源站的数据,让浏览器的请求不用千里迢迢地到达源站服务器,直接在半路就可以获取响应。如果 CDN 的调度算法很优秀,那么就可以找到离用户最近的节点,大幅度缩短响应时间。因为在现如今这个信息量爆炸的时代,用户等待的耐心也越来越低,有一个说法:当用户打开一个页面,如果超过 4 秒没有响应,他就会关闭这个页面。所以任何一个服务提供商,都希望自己服务的响应速度足够快,这样才能留住用户。
而用户从发出请求到接收响应所消耗的时间不仅取决于网络带宽,还取决于传输距离。比如服务器在广东,但访问的用户在北京,地理位置的距离会导致延迟变得明显,而且传输距离过长也会导致数据包丢失的可能性变大,导致网络中断。所以 CDN 便诞生了,它是专门负责解决长距离导致网络访问速度慢的一种应用服务。其最初的核心理念就是将内容缓存在终端用户附近,源站不是离用户远吗,没关系,在靠近用户的地方建立一个缓存服务器,将源站的内容拷贝一份放在这里不就行了。
后续北京的用户访问北京的缓存服务器,上海用户访问上海的缓存服务器。没错,这便是 CDN 的核心思想,只是建立缓存服务器需要大量资金,很多公司一般不会自己这么干,而是购买现有的 CDN 服务。有很多 CDN 厂商投入了大笔资金,在全国、乃至全球的各个大枢纽城市都建立了机房,部署了大量拥有高存储高带宽的节点,构建了一个专用网络。这个网络是跨运营商、跨地域的,虽然内部也划分成多个小网络,但它们之间用高速专有线路连接,是真正的信息高速公路,基本上可以认为不存在网络拥堵。
有了高速的网络传输通道后,CDN 就要分发源站的内容了,采用缓存代理技术,使用推或拉的手段,将源站的内容逐级缓存到网络的每一个节点上。由于整个过程相当于通过网络对内容做了分发,因此叫 CDN,即内容分发网络。
具体来说,CDN 就是采用更多的缓存服务器(也叫 CDN 节点),当用户访问网站时,利用全局负载技术,将用户的请求转发到距离最近的 CDN 节点(术语叫边缘节点)上,由缓存服务器响应用户请求。这样一来就省去了长途跋涉的时间成本,实现了网络加速。
那么 CDN 都能加速什么样的内容呢?或者说 CDN 节点应该保存哪些内容呢?在 CDN 领域,内容其实就是 HTTP 协议里的资源,比如超文本、图片、视频等。资源按照是否可以缓存,分为静态资源和动态资源两类。
静态资源:数据内容静态不变,任何时候来访问都是一样的,比如图片、音频。
动态资源:数据内容动态变化,即内容由后台服务计算生成,每次访问都可能发生变化,比如商品的库存、微博的粉丝数等。
很显然,只有静态资源才能够被缓存加速、就近访问,而动态资源只能由源站实时生成,即使缓存了也没有意义。不过,如果动态资源在一段时间内不会发生变化,那么可以在响应头中指定 Cache-Control 字段,表示允许缓存一段短暂的时间,那么它在这段时间里也就变成了静态资源,可以被 CDN 缓存加速。
CDN:我们不生产内容,我们只做内容的搬运工。
到目前为止,我们已经知道 CDN 到底是啥了,然后再来看看它是怎么运行的。CDN 有两个关键组成部分:全局负载均衡和缓存系统。
全局负载均衡
全局负载均衡(Global Sever Load Balance)一般简称为 GSLB,它是 CDN 的大脑。主要的职责是当用户接入网络的时候,在 CDN 专网中挑选出一个最佳节点提供服务,解决的是如何找到最近的节点,即边缘节点,负责对整个 CDN 网络进行负载均衡。
GSLB 最常见的实现方式是 DNS 负载均衡,但是要略微复杂一些。
首先在没有 CDN 的时候,权威 DNS 返回的就是源站服务器的实际 IP 地址,浏览器收到 DNS 解析结果后直连即可。但加入 CDN 就不一样了,权威 DNS 返回的不再是 IP 地址,而是一个 CNAME(Canonical Name)别名记录,指向的就是 CDN 的 GSLB。意思就是我没法给你源站服务器的 IP,给你的是 GSLB,你需要再去 GSLB 查一下。因为没拿到 IP 地址,于是本地 DNS 就会向 GSLB 再发起请求,这样就进入了 CDN 的全局负载均衡系统,基于以下原则开始智能调度:
看用户的 IP 地址,查表得知地理位置,找相对最近的边缘节点。比如 IP 是北京,那么就找北京的边缘节点;
看用户所在的运营商网络,找相同网络的边缘节点,因为边缘节点不止一个,但选择网络相同的更有优势;
检查边缘节点的负载情况,找负载较轻的节点;
参考节点的健康状况、服务能力、带宽、响应时间等;
GSLB 把这些因素综合起来,用一个复杂的算法,最后找出一台最合适的边缘节点,把这个节点的 IP 地址返回给用户,用户就可以就近访问 CDN 的缓存代理了。
缓存系统
缓存系统是 CDN 的另一个关键组成部分,相当于 CDN 的心脏。如果缓存系统的服务能力不够,不能很好地满足用户的需求,那 GSLB 调度算法再优秀也没有用。但互联网上的资源是无穷无尽的,不管 CDN 厂商有多大的实力,都不可能把所有资源都缓存起来。所以,缓存系统只能有选择地缓存那些最常用的资源,于是便产生了 CDN 中的两个关键概念:命中和回源。
命中就是指用户访问的资源恰好在缓存系统里,可以直接返回给用户。回源则正相反,缓存里没有,代理必须先将数据从源站同步过来。
相应的,衡量 CND 服务质量的两个指标便是命中率和回源率,计算方式为命中次数、回源次数除以总访问次数。显然好的 CDN 应该是命中率越高越好,回源率越低越好。现在的商业 CDN 命中率都在 90% 以上,相当于把源站的服务能力放大了 10 倍以上。
那怎样才能尽可能地提高命中率、降低回源率呢?
首先肯定是在存储系统上下功夫,尽可能存储更多的内容。
其次,缓存也可以划分层级,分为一级缓存和二级缓存。一级缓存配置高一些,直连源站,二级缓存配置低一些,直连用户。回源的时候,二级缓存只找一级缓存,一级缓存没有再回源。这样扇入便缩小了,可以有效地减少回源。
CDN 虽然有很多的优点,但它不是万能的。如果是用户动态交互的实时数据,那么很难缓存在 CDN 中。另外很多公司为了保护自身的数据隐私,不允许第三方 CDN 厂商缓存数据,只允许自家 CDN 缓存,这个可能会造成一些影响。
然后就是最关键的,如果是自建 CDN,那么非常烧钱,因此大部分公司都不会自建,而是选择专门的 CDN 厂商。但即便是租用 CDN 服务,花的钱也不少,区域越多花的钱也越多。
CDN 和边缘计算
互联网公司采用 CDN 是用存储空间换网络低延迟,但很多通信公司也青睐 CDN,目的则是以存储空间换网络带宽。通过服务下沉,减轻上层骨干网络的流量压力,避免硬件扩容,降低网络建设成本。因为大量的业务流量数据在骨干网跑来跑去,骨干网肯定吃不消,要拼命扩容。但如果这些业务流量数据在底层就被解决了,那么骨干网的带宽压力自然就减轻了。
很多运营商已经将 CDN 下沉到地市级,以此减轻压力,同时可以提升用户体验。说到这,你应该想到了边缘计算,有很多人觉得 CDN 和边缘计算很相似,因为 CDN 算是边缘计算的雏形。
一直以来,随着网络能力的不断提升,内容资源和计算能力都在不断往上走,走到云计算中心。通过核心的云计算中心,对所有终端节点提供服务。但随着用户量的增加,用户所在区域和计算中心可能距离很远,那么不管把计算中心设置在什么地方,也不管它的能力有多强大,都无法克服物理距离上的障碍。
于是人们便想到,数据能不能不要上传到计算中心,而是转移到网络的边缘,即数据输入的地方(例如 IoT 设备),然后直接计算呢?于是便有了边缘计算,它不会将数据发送到计算中心进行处理,而是在数据源附近处理数据。
所以 CDN 和边缘计算还是有差异的:
CDN 负责优化数据交付;边缘计算负责优化数据处理
CDN 通常用于交互静态内容,例如网页、图片和视频;边缘计算用于需要快速数据处理的应用,例如实时数据分析等
CDN 会将内容放到距离用户近的地方;边缘计算会将计算和数据处理放到尽可能靠近数据产生的地方
但这两者是可以互补的,比如一个边缘计算设备可以使用 CDN 来高效地交互内容,同时在网络边缘处理数据。
另外 CDN 也可以使用边缘计算,通过把计算动态资源的代码和数据也放在 CDN 的节点上,这样就可以在 CDN 里获取动态资源不用回源了。