2019-03-12
架构,不能作圣,皆负此形也。 –鲁迅?
一个五个人用的软件,给他什么架构都是没有必要的,AWS 可,ThinkPad 可,树莓派亦可。既然谈论云端 Web 应用,我们的目标就是星辰大海,是负载能力,是可用性。
一台电脑开启的服务器,肯定是没有架构可言的。很多台电脑开启的服务器,服务器的位置就需要考虑了。服务之前,用户和服务之间的通信需要依靠负载平衡工具,负载平衡工具会将收到的请求分散给各个服务实例,返回正确的结果。
无论是任何云服务,都会涉及 可用区域
的概念。在简单情况的前提下,只要选择软件主要服务的区域就可以了。AWS 当前没有提供全局的负载均衡器,甚至 Route53
也是要选择可用区域的,这并不是说 AWS 就不能服务全球,ELB 在跨可用区域的 IP 来源是动态的,不可控的。GCP 提供的全球负载均衡器可以实现一个 IP 地址全球访问,服务全世界的软件为了简便考虑可以使用 GCP 的服务 Global forwarding rules route
,简单可控。
如果你没有打算现在选一个云服务器,这一节对你没有什么好看的 –!鲁迅
表格就算了,直接说结论就行了。GCP 在负载均衡这一块功能可以说非常全面了,目前其提供三种负载均衡器 HTTP/HTTPS
, PROXY
, 和 Network
本质上的区别是支持的协议不同,抛去最明显的不说, Proxy 支持 TCP
和 SSL
, Network 支持 TCP
和 UDP
。其中 Network
不支持全球可用区域。配合服务的名字一下子就能看懂了。面向用户的暴露接口应该用 HTTP
和 Proxy
内部服务见的通讯应该使用 Network
。除了全球可用区域之外 GCP 还有一些优点,HTTP/HTTPS
的负载均衡器也支持 WebSocket
协议。算法上,亚马逊基本就是平均分配,大概是因为那个 Auto Scaling Group
和 ELB
通信不够吊,而 GCP 可以通过 CPU 使用率之类的信息动态分配请求。而且!HTTP
的负载均衡器可以根据 URL 的不同分配实例(甚至不同可用区域)。太棒了!
Proxy
和 HTTP/HTTPS
都支持 TCP ?是的,如果你想的话这两种负载均衡服务都可以转发 HTTP 请求,作为用户的入口。如果你这么做,会有一些影响,比如说你会丢失用户的链接信息,这是因为 Proxy
分配来的请求是克隆的,而 HTTP/HTTPS
分配来的请求是转发的。
首先,所有的 ELB 都不支持跨可用区域,这是前提,如果你的软件是面向全球的,你不应该使用 ELB(这不是说你不能用,只是不应该)。这不是唯一的缺点,其他缺点还包括比如说分配算法,跨域配置等等。这并不是一个已经存在的项目很容易选择的内容,我并不是说 ELB 不好,毕竟谷歌的这些服务晚面世了很久,我只是指出确实存在的缺点。
我们已经提到了负载均衡的理念,对于无状态的服务器(也就是这篇文章讨论的对象)每次客户请求到达哪一个服务器是我们所不在乎的。虽然如此,ELB 对于 Keep-Alive
的 HTTP 请求还是会尽量重用同一个 TCP 链接的,在重用的情况下,每次链接只需要两次握手而不是三次。那么问题来了,这个负载均衡器应该怎么排布呢?:D.
有一个经典简单的 Web 应用:
React
和 Webpack
实现,通过打包好的 Bundle.js
, index.html
和 style.css
外加服务器渲染来分发,服务器渲染用 Express
实现,并不考虑同构问题。Spring Boot
框架开发的服务提供所有的 RESTful API 接口,所有这些 API 都是无状态的,它处理请求的数据来源来自一个 Redis
服务器以及一个 MongoDB
持久化储存数据库。scala
实现的ETL 服务每天运行几次,把数据装到 MongoDB 中,把没有同步的数据带走。在上述例子中,我们应该怎么分配呢?首先,服务器和界面应该是有用户接口的,它们应该被挂在同一个域名的不同子域名上。界面和服务器都指向对应的负载均衡器,每一个均衡器应该指向一个 Auto Scaling Group
这个东西在压力大的时候自动创建新的实例,有实例崩溃的时候尝试复活。
在跨域设置好之后,界面和服务器的问题基本就解决了,服务器应该在 Subnet 内用直接的内网 IP 链接对应的服务。同理,ETL 也应该在这个子网内运行,不对外暴露接口或者 IP。