Web Scalability for Startup Engineers Tip&Techniques for Scaling You Web Application

第1章和第2章讲述可伸缩系统的核心概念与软件设计原则。强烈建议认真阅读这两章,这部分内容包含了开发一个可伸缩的Web系统甚至开发一个良好软件的基本原理和设计原则,是其它一切技巧和方法的元规则。第9章讲述可伸缩的系统运维及可伸缩的个人和团队,如果你正处在一个高速发展的创业团队中,如果你对从技术走向管理感兴趣,我相信你可以从本章的内容中收获多多。

习惯的旧秩序被互联网打破了;这是一个创新的时代,任何奇思妙想都有被用户接受成为现实的可能

线下的思维转换为线上的思维

互联网催生了一种新型的生活方式,也催生了适应于互联网的一种新的人群。

连接人群和线上生活的纽带就是一个一个的互联网及移动互联网的应用。

作为应用的典型IT形态,互联网应用经历了独占型应用、SOA应用,在云时代就是云原生(Cloud Native)的应用。

构建一个良好的可伸缩的应用,不仅仅是一个优秀工程师的职责,同时也代表了对一种生活方式的认可。

弹性架构的概念,软件设计的原则,以及如何构建一个优质的互联网应用。

一个最初由两三个工程师开发的产品雏形,如何经过逐渐地重构、演化、迭代、伸缩,最终成为一个巨无霸系统?

第一章和第二章讲述可伸缩系统的核心概念与软件基本设计原则,这部分内容包含了开发一个可伸缩的Web系统甚至开发一个良好的软件的基本原理和设计原则,是其它一切技巧和方法的元规则。

可以在工作中遇到问题时快速浏览,寻找方法和灵感。

可伸缩系统的设计是一种权衡的艺术,必须对第一种方案的优缺眯都了如指掌,才能在面对实际问题时做出最合适的选择。

高并发可伸缩系统的设计看似纷繁复杂庞大无比,实际上关键的核心技术也就那么几样,如果深入掌握了这些关键技术,就抓住了可伸缩系统设计的核心。这几样关键技术,可能需要在不同场景,从不同视角反复思考琢磨,才能真正掌握。

成竹在胸

核心概念

极具弹性的系统,在快速变化的内外部环境中保持快速响应的能力。

那些在传统企业中需要花费整年时间才能搞定的事情,创业公司可能必须在几个星期内搞定。如果足够成功并且足够幸运,可能需要在几个星期内把你的系统的处理能力扩大十倍。当然,也可能在几个月后又把系统的处理能力缩小回去。

就伸缩性技术本身而言,目前很多公司提供这方面的基础服务,比如亚马逊云、微软云、Google云,以及阿里云,以及阿里云,腾讯云等,这些云服务可以让你完全不必考虑伸缩性方法的问题而只需关注自身的业务需求。

要完全理解伸缩性需要一个渐进的过程。

在阅读过程中把这些基础设施图及架构图再画一次,这不仅有助于理本书的内容,如果只是匆匆一扫而过,可能会忽略很多有用的信息。

什么是伸缩性

伸缩性是指系统可以根据需求和成本调整需求和成本自身处理能力的一种能力。伸缩性常常意味着系统可以改变自身的处理能力以满足更多用户访问、处理更多数据而不会对用户体验造成任何影响。此外,还有一件重要的事情必须提醒工程师们注意,伸缩性不仅需要伸(增强系统处理能力,即扩容),有时候也需要缩(缩减系统处理能力,即缩容)。同时,这种伸缩还必须相对比较省钱和快速。

正如在不同场景下对伸缩性有不同要求,可以在不同维度上对伸缩性进行度量。

伸缩性主要从以下几个方面度量:

  1. 处理更多数据

  2. 处理更高的并发

    度量并发的指标通常是:应用系统可以同时服务多个用户。

    对于一个Web应用系统而言,并发度的意思是最多有多少个用户可以同时访问你的网站而不会感到访问速度变慢。

    由于服务器的CPU数目是有限的,能同时运行的线程数也是有限的,因此处理高并发是一件非常有挑战的事。如果还要同步某些代码的执行以保证数据的一致性,那些这个挑战将变得更加艰难。

    更高的并发也意思着系统需要同时打开更多的连接,启动更多线程,处理更多消息,CPU需要更多次上下文切换。

  3. 处理更高频次的用户交互

    用户交互是指你的系统和你的用户之间的交互频次。这个维度和并发度看起来很像,但是稍有不同。交互频次衡量你的用户和你的服务器交换信息的频繁程度。与交互频次相关的最大挑战是响应得延迟。

    如果应用的交互频次在增长,你就必须让应用响应得更快速,这就要求系统有更快的读写速度进而将系统并发度推到一个更高的高度。

    例如如果你做的是一个Web站点,用户大概需要第15秒或2分钟从一个页面跳转到另一个页面;

    但如果你做的是一个多人交互的移动游戏,用户可能需要每秒种和服务器通信好几次,因此交互频次相对独立于用户并发度。

通常会综合上述三个维度云考量一个系统的伸缩性。一般说来,实现系统伸缩性的过程,扩容比缩容更重要也更常见。

伸缩性和性能密切相关,但它们并不是一回事。

  • 性能更多的是衡量系统处理一个请求或者执行一个任务需要花费多长时间

  • 伸缩性则更多关注系统是否能够随着请求数量的增加(或减少)而相应地拥有适应的处理能力

EG:你有100个并发用户,每个用户平均第5秒发送一个请求,那么系统吞吐量就是每秒20个请求。

  • 性能指的就是系统需要花费多少时间处理这每秒20个请求

  • 伸缩性指的是在不影响用户体验的前提下,系统最多能够处理多少个并发用户及用户最多能发送多少个请求

软件产品的伸缩性也许会受限于软件开发团队的规模。如果要系统具有伸缩性,那么对应的软件开发团队也必须具有伸缩性,否则就没有足够的工程师资源去快速影响需求变更。

尽管看起来软件开发团队的伸缩性和技术无关,但事实上系统架构和设计会影响团队规模。

如果你的系统设计是紧耦合的,所有人都在一份代码上工作,那么你就很难扩展你的工程师团队

由于团队的沟通效率和团队规模成指数关系,因此当完成同一个工作的技术团队的规模达到8-15人时,效率就会变得非常低。

为了更好地体会伸缩性对创业公司的影响,让我们试着从公司视角观察。问你自己:制约我们公司持续发展的问题有那些?

答案不仅包括我们前面提到的处理同吞吐量导致的技术架构方面的问题,也包括开发过程、团队、代码结构等一系列问题。

从单一服务器到全球用户的Web 架构演化

这里展示的很多伸缩性演化架构阶段仅仅在你开始做规划时有参考意义。

很多情况下,真实世界并不完全按照这个方式去演化,更可能会经历多次重写。还有一些时候,系统在设计和诞生之初就处于某个演化的特定阶段并一直保持不变,或者在发现架构制约的时候直接向上跳跃一到两个阶段而不是逐步演化

尽量避免完全重写应用,特别是创业公司。重写总是比你预期的时间要长,也比你预估的难度更大。基于我的经验,一次重写带来的麻烦需要两年才能终结。

单一服务器

除DNS外,网站服务器需要响应各种Web页面、图片、CSS文件和视频文件,所有的响应都只在这一台服务器上处理,所有的网络流量都只经过这一台服务器进行传输。

最便宜的主机方案是共享主机,用户只购买一个用户帐号而没有管理权限,这个帐号和其它客户的帐号安装在一个服务器上。这种主机方案适用于那些只需要一个最小的Web站点或者只有一个着陆页(Loading Page)的网站。但是这种方案限制太多,不值得推荐。

瓶颈:

  • 你的用户在持续增长,因此访问量在持续增长。每个用户访问都会对服务器造成负载压力,服务每个用户又需要更多的计算资源,包括内存、CPU时间,以及磁盘读写(I/O)。

  • 你的数据库存储的数据在持续增长。在这种情况下,数据库由于需要更多的CPU、内存和I/O请求而变慢。

  • 你要扩展系统增加新的功能,这使得每一次用户请求都要消耗更多的系统资源

  • 上面这些因素常常会叠加在一起。

使用更强的服务器:垂直伸缩

一旦你的应用达到服务器的极限(由于网络流量、数据处理规模或者并发度等因素的增长),就必须决定如何去伸缩系统。

有两种不同的伸缩性方案:

  1. 垂直伸缩

  2. 水平伸缩

通过升级硬件和网络吞吐能力可以实现垂直伸缩。由于不需要改变应用架构,也不需要重构任何东西,所以通常被认为是最简单的短期伸缩性方案。

垂直伸缩的方案有很多:

  • 通过使用RAID(独立冗余磁盘阵列)增加I/O吞吐能力。I/O吞吐量和磁盘存储是数据库服务器的主要瓶颈。使用RAID并增加磁盘数量有助于将读写请求分布到多个磁盘上。最近几年,RAID10变得格外流行,这种RAID方案即提供了数据冗余存储又提高数据吞吐能力。从应用角度看,整个RAID看起来就是一个数据卷标,但是在实际底层实际包含了多个磁盘共同对外提供读写访问。

  • 通过切换到SSD(固态硬盘)改善I/O访问速度。随着固态硬盘技术越来越成熟,价格越来越低,SSD也变得越来越流行。根据不同的其准测试方法,SSD随机读写速度大约比传统磁盘快10到100倍。应用通过替换SSD硬盘可以节约更多I/O等待时间。不过,顺序读写的速度提升就没有这么高了,所以现实中应用性能提升也没有特别巨大。事实上,大多数开源数据库(比如MySQL)会优化数据结构和算法,尽可能多地执行顺序硬盘操作而不是随机操作。而某些数据存储系统,比如Cassandra,做的更彻底,所有的写操作和多数读操作都只使用顺序I/O操作,这也使得SSD更缺乏吸引力。

  • 通过增加内存减少I/O操作(如果你的应用部署在独立的物理服务器上,128GB内存是一个比较实惠的常规配置)。增加内存意味着文件系统有更多的缓存空间,应用程序有更多的工作内存。此外,内存大小对于数据库服务器效率的提升也格外重要。

  • 通过升级网络接口或增加网络接口提高网络吞吐能力。如果你的服务器需要处理大量视频等媒体内容,也许需要升级网络供应商连接,甚至升级网络适配器以获得更高的吞吐能力。

  • 更新服务器获得更多处理器或者更多虚拟核。拥有12或者24线程(虚拟核)的服务器是一个比较实惠且合理的升级方案。CPU和虚拟核越多,能够同时执行的进程数就越多,系统因此变得更快,这不仅仅是因为多个进程可以不必共享同一个CPU,还因为操作系统不需要在同一核执行多个进程而执行不必要的上下文切换。

垂直伸缩的一些严重的制约:

  1. 成本。当越过某个点后,垂直伸缩会变得格外昂贵。

  2. 垂直伸缩是有极限的,这是个比较大的问题。无论你愿意花多少钱,内存都不可能无限地增加下去。类似的限制还有CPU的速度,每台服务器的虚拟核数目,硬盘的速度。简单的说,到了某个极限,没有任何硬件能力能够继续增加。

  3. 操作系统的设计或者应用程序自身制约着垂直伸缩最多只能达到某个点。举个例子,你不能通过增加CPU数目而无限增加MySQL的处理能力,因为同时锁竞争也会增加(特别是如果你使用MyISAM这种比较老的MySQL存储引擎)

如果多个线程共享诸如内存、文件这类资源时,需要用锁进行同步访问。低效的锁管理会导致锁竞争成为瓶颈。

应用操作应该使用细粒度的锁,否则,应用需要花费很长的时间去等待释放锁。一旦锁竞争成为瓶颈,增加更多的CPU核数也不会改善应用的处理能力。

高性能的开源应用或商业应用可以扩容到几十个CPU核,然而,在购买新硬件前最好还是要确认下系统的扩容伸缩能力。由于高效的锁管理是一项有挑战的任务,需要大量的经验和详细的调试,所以自己开发的应用通常在锁竞争方面做的差一点。在一些极端的情况下,因为在设计之初就没有考虑任何高并发的场景,结果导致增加CPU核对应用处理能力提升没有任何帮助。

垂直伸缩不会对系统架构产生任何影响。你可以垂直伸缩扩容任何一台服务器、网络连接或者路由器而无须修改任何代码或者重构任何东西。唯一要做的就是用更强大更快速的硬件替换现有的硬件。

单一服务器,但是是更强大的服务器

服务分离

服务分离背后的核心理念是你应该将整个Web应用切分成一组不同的功能模块,然后将它们独立部署。这种基于功能将系统划分成独立可伸缩的模块的方式称为功能分割

另一个简单的方案是通过在不同的物理机上安装不同类型的服务,使得一个系统的不同部分被分离部署在不同物理服务器上。要这种场景下,一个服务可以是一个类似Web服务器这样的应用(比如Apache)或者是一个数据库引擎(比如MySQL)。这就使得应用服务器和数据库分离到不同的服务器上。

类似地,也可以把FTP、DNS、缓存及其它服务器部署在不同的物理机器上。

对单一服务器部署进行可伸缩扩容,对可分离的服务进行分隔部署是一种比较轻的解决方案。不过,这种伸缩方案并不能一直持续进行下去,一旦你的所有服务类型都已经分别部署在独立服务器上,就没有办法用这种方法扩容下去了。

缓存是一种重要的服务,目标是通过快速返回提前生成好的内容降低请求响应延迟。缓存是构建可伸缩系统的一种重要技术。

服务器通过在第三方数据中心,数据中心由一组安装不同功能的服务器组成。每个服务器都承担不同的角色,比如Web服务器、数据库服务器、FTP、缓存等。

对单一服务器部署而言,服务分离是一种巨大进步。相比以前,可以将负载压力分摊到更多的服务器上,而且可以按需进一步对每个服务器进行垂直伸缩。

如果某个微型网站受欢迎,访问量大,就会把它分离出来,部署在一台独立的Web服务器和一台独立的数据库服务器上。

一个Web应用利用功能分割将负载分布在更多服务器的场景。应用的每个部分都使用不同的二级域名,这样就可以基于Web服务器的IP地址进行流量分发。

不同的功能模块也许部署在不同的服务器上,这些不同的功能模块也会有不同的垂直伸缩需求。显然,一个系统越是能够分割成不同的部件,每个部件就越有弹性越好。

CDN内容分发网络:静态内容的伸缩性

随着应用不断发展用户不断增长,可能通过CDN服务减轻应用网络流量负载压力。

CDN的全称是Content Delivery Network,即内容分发网络。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。

CDN是一种提供静态文件(比如图片、JavaScript、CSS及视频)全球分布的服务。它的工作原理有点像HTTP代理。用户如果需要下载的图片、JavaScript、CSS或者视频内容,可以通过连接CDN服务器下载而不是连接应用服务器。如果CDN服务器没有用户需要的内容,CDN服务器会请求服务器获取这部分内容然后再缓存到CDN服务器上。一旦文件缓存在CDN服务器上,后面的用户就再也不需要连接应用服务器了。

通过将应用集成到某个CDN服务,可以显著减少应用服务器需要的网络带宽。可以用更少的Web服务器提供Web应用静态内容。CDN会从距离用户最近的服务器提供静态内容,进而加速这些用户的页面加载时间。

我们不是一定要增加很多的服务器或者学习如何对HTTP代理进行伸缩。我们只需要简单地使用第三方的服务,然后依赖它提供的伸缩性能力就可以了。虽然这看起来有点像"伸缩性游戏的小把戏”,但它真的是非常强大的手段,特别是在创业公司的早期开发阶段,可能根本就没有足够的时间和金钱去调查这些技术。

分散访问流量:水平伸缩

水平伸缩是指通过增加服务器提升计算能力的一类架构方法。

水平伸缩被认为是伸缩性的圣杯,水平伸缩可以克服垂直伸缩带来的单位计算成本 随着计算能力增加而迅速飙升的总是。另外水平伸缩总是可以增加更多服务器,这样就不会像垂直伸缩那样遭遇到单台服务器的极限。

水平伸缩可以通过修改应用架构在后期进行“添加”,但是大多数情况下,必须付出相当的开发代价。

一个真正意义上的可伸缩系统不需要很强大的服务器,甚至相反,它们运行在大量的廉价商业服务器上,而不是少量的强大的服务器上。

水平伸缩技术带来的好处要在系统发展的后期才能体现出来。一开始,水平伸缩因为技术比较复杂需要更多的工作量,会花费更多的成本。

  1. 这些成本体现在可水平伸缩的架构比基本的架构需要部署更多的服务器

  2. 水平伸缩的架构需要更有经验的工程师去构建并维护它们

不过,一旦你的系统的计算能力达到某个点,水平伸缩就变成更好的策略。

使用水平伸缩,可以不必花费购买顶级服务器的高昂的价格,也不会触及垂直伸缩会出现的天花板(买不到更强大的硬件)

水平伸缩使用CDN这样的第三方服务不仅更节约成本,而且更透明。

云服务商愿意为高流量的客户提供更低的价格,因为这些客户之前的付费已经覆盖了必要的维护集成成本。对于访问量很大的客户,他们也确实很在意价格高低。

具有水平伸缩特性的系统和先前架构演化阶段提及的系统的不同之在于:

数据中心的每一种服务器角色都可以通过增加服务器进行扩容伸缩

事实上,在不同演化阶段可以部分地实现水平伸缩,有的服务实现水平伸缩,而有的服务则不实现。

达到真正意义上的水平伸缩很困难而且需要丰富的经验。因此,构建水平伸缩的系统先从那些容易做到的地方做起。比如Web服务器、缓存;暂时难以做到的地方,比如数据库及其它的持久存储

在演化的这一阶段,一些应用使用轮询DNS服务实现Web服务器流量分发。

轮询DNS不是实现Web服务器流量分发的唯一手段

轮询DNS是DNS服务器的特性之一,它允许将一个域名解析到多个IP地址中的一个。一般的DNS服务器会将一个域名解析到一个单一的域名。然而轮询DNS允许将一个域名映射到多个IP地址上,每个IP地址都指向不同的机器。因此,每次用户请求域名解析,DNS都会返回这些IP地址中的一个。目的就是将每一个用户访问的流量分发到Web服务器集群中的某一台上,不同的用户在不知情的情况下连接到不同服务器上。一旦用户接收到一个IP地址,他就会只与这台选中的服务器通信---相当于服务器是有状态的,如果服务器宕机也会有其它的总是,服务器集群上访问压力也会不均匀。

服务全球用户的伸缩性架构 GeoDNS服务

架构演化的最后阶段就是打造一个全球最大的Web站点,实现全球用户的可伸缩性。一旦你服务的用户从几百万扩展到全球,你需要的数据中心就不止一个。一个数据中心可以部署很多的服务器,但是其它大洲用户的体验可能并不好,而且多个数据中心也能让你比较从容地应对那些可能的宕机事件(水灾、火灾引起的停电)

服务全球用户需要面临很多挑战,用到很多技巧,其中一个技巧是使用GeoDNS服务。

GeoDNS是一种基于客户地理位置进行IP地址解析的DNS服务。一般DNS服务器收到一个域名,比较baidu.com,对台解析成一个IP地址。GeoDNS从用户视角看也是这样,然而,GeoDNS会基于用户的地理位置返回不同的IP地址。一个欧洲用户和一个澳洲用户得到的IP地址可能不同,结果是每个用户都会访问到距他最近的一个Web服务器。GeoDNS的目标就是将用户分发到离他最近的数据中心进而实现最小的网络延迟。

基础设施层面的另一个扩展是在全球范围部署若干边缘缓存(edge-cache)服务器,进一步减少网络延迟。边缘缓存服务器的使用依赖于应用的自然特性。边缘缓存服务器最高效的用法是像反向代理服务器那样缓存整个页面。当然,边缘服务器也会提供其它服务。

边缘缓存是一种距离用户较近的HTTP缓存服务器,便于部分缓存用户的HTTP流量。从用户浏览器发起的请求到达边缘缓存服务器,边缘缓存服务器决定从缓存中直接返回响应页面,还是通过发送背景请求到Web服务器获取响应页面的其它部分最后进行组合。

边缘缓存服务器还可以决定某个页面是不可缓存的,也可以决定是否可以代理整个Web服务。边缘缓存服务器可以缓存整个页面也可以缓存页面片段。

随着应用未来的持续发展,你也许会考虑将主数据中心切分成多个数据中心并把它们部署到离用户较近的位置。通过将应用和数据存储部署到离用户较近位置的方式,可以实现更短的访问延迟和更少的网络成本。

单个数据中心如何支撑可伸缩性

数据中心基础架构高层概览1-10

  1. 前端:应用栈睥第一个部分,包含了直接与用户设备交互的一系列组件。前端可能在数据中心内,也可能在数据中心外,这要看部署细节和第三方服务的使用情况。这些组件不包含任何业务逻辑,主要目的是提升系统处理能力和伸缩能力。

    负载均衡器是一种软件或者硬件组件,可以将访问某个IP地址的访问流量分发到多个服务器上,这些服务器则隐藏在负载均衡器的后面。负载均衡器可以将用户访问负载平均地分发到多个服务器上,并且允许动态地增加或者移除服务器。由于用户只能看到负载均衡器,所以可以在任何时候添加新的Web服务器而无须停止服务。

  2. Web应用层:整个应用栈的第二层是Web应用层,由Web应用服务器集群构成,主要职责是处理用户的HTTP请求生成最后的HTML页面。这些服务器通常用轻量级的Web开发框架(PHP、JAVA、Ruby、Groovy等)构建,实现最小的业务逻辑,主要任务是生成用户界面。整个Web应用层的主要用途是处理用户交互并转换成内部服务调用。这个Web层越简单功能越少,整个系统越好。复杂业务逻辑应该在Web服务层完成,实现更好的复用及减少需求变更,因为展示层的变更是最频繁的【与用户交互的Web页面或App】.

  3. Web服务层:应用栈的第三层是Web服务层,由各种Web服务组成。这是非常关键的一层,包含了最主要的应用逻辑。展示层剥离出来的逻辑,就集中在这一层。高内聚的业务逻辑,更容易实现功能分隔;功能分隔后的服务,可以独立地对它进行伸缩。譬如电子商务应用中的产品类目服务和用户信息服务是有完全不同的伸缩性需求的。

  4. 附加组件,对象缓存和消息队列。

    对象缓存服务器既在前端服务器使用,也在Web服务中使用,主要目的是减轻数据存储服务器的负载压力及通过对部分数据进行预先计算实现响应加速。

消息队列被用来 将某些处理延迟到稍后的阶段处理,并且将处理操作委托给消息处理者服务器。发送给消息队列的消息通常来源于前端应用及Web服务,然后这些消息被特定的消息处理者机器处理。

定时任务,不会去响应用户请求,它们是离线的作业处理服务器,提供类似异步通知、订单处理,以及其它一些允许较高延迟的功能。

  1. 数据持久层:一般来说,这是最难以进行水平伸缩的一层。

数据中心基础架构


图1-10中数据中心基础架构鸟瞰图中各组件的布局是经过深思熟虑的,主要目的是帮助那些相对比较慢的降低负载访问压力。

只有在非常必要的情况下,Web服务层才会连接搜索引擎及主要数据存储去读写必要的信息。

有一点需要特别注意,就是没有必要为了伸缩性实现图1-10中的所有组件。相反,要尽可能少地使用不同种类的技术,因为每增加一种技术,就是在增加系统的复杂度,也是在增加维护的成本 。使用很多不同的技术看起来很酷,但是却让发布、维护、调试变得更困难。

如果应用只是需要一个简单的搜索功能,也许只要一个前端服务器和一个搜索引擎集群就能够满足所有的伸缩性需求。

如果能增加服务器实现现有的每一层的伸缩性,并且也能满足全部的业务需求,为什么还要不厌其烦地使用各种额外的组件呢?

应用架构概览

应用架构是将业务逻辑放在应用架构的核心位置,是关于业务模型的演化,不是关于某个框架或者某个特定技术,也不是关于java、PHP、PostgreSQL或者数据库表结构的。

业务需求驱动着各种决策。

没有正确的领域模型和正确的业务逻辑,数据库、消息队列及Web框架都没有任何意义。

不管应用是一个社交网站,还是一个药品服务,或者是一个视频APP,它总归是有某些业务需要一个领域模型。通过将这个模型放到架构的核心,确保各种组件围绕这个核心展开,服务于这个业务,而不是其它什么东西。

如果把技术放在核心位置,也许会得到一个很赞的Rails应用,但不太可能得到一个很棒的药品服务应用。

市面上已经有很多不错的关于领域驱动设计和软件架构的书,可以帮助更好地熟悉软件设计的最佳实践。

领域模型是关于应用要解决的业务问题的本质描述。

领域模型表示一个应用的核心功能,重点在于业务而不是技术。领域模型解释了关键术语、角色和操作,而不关心技术实现。

一个自动柜员机(ATM)的领域模型的关键词是:现金、帐户、负债、信用、身份认证、安全策略等。

同时,领域模型不关心硬件和软件实现。

在系统内部,应用被分解成多个(高度自治的)Web服务。

应用架构高层鸟瞰图1-11

应用架构的核心是主要业务逻辑

  1. 前端:主要职责是成为用户的接口,用户通过网页、移动APP或者Web服务调用完成和应用的交互。是介于公开接口和内部服务调用的处理层。

    前端可以被视作是应用的“皮肤”或者应用的插件,是系统向用户呈现的功能展示,因此不应该是系统的核心或者重点。

    一般来说,前端应该尽可能保持简单。

    通过保持前端简单,可以复用更多业务逻辑。

    服务的简单与职责单一,使服务只关注逻辑而不是视图呈现。

    业务逻辑只存在Web服务层,可以避免视图和业务强耦合的问题,并且可以实现前端的独立伸缩,即只关注处理较高的并发访问请求。

    可以认为前端是一个可移除的插件,可以用不同的语言重写、可以输入各种类型的前端。

    可以移除一个基于HTTP的前端输入而插入一个移动应用的前端或者一个命令行前端。

    即前端展示要与核心业务逻辑分离。

    前端不应该关注数据库或第三方服务。允许前端代码出现业务逻辑会导致代码难以复用及系统更高的复杂度而难以维护。

    可以允许前端发送事件消息给消息队列,以及允许使用后端缓存,消息队列和缓存都是提升性能和改善伸缩性的重要手段。

    无论是缓存整个HTML页面还是HTML片段,都会比在构建HTML时缓存数据库查询更节省处理时间。

  2. Web服务:处理主要流程和实现业务逻辑的地方。要点服务职责单一,方便复用和伸缩。

    SOA就像雪花,不会有两个完全相同。--David Linthicum

    Web服务在整个应用架构中处于中心位置,这种架构通常被称为面向服务的体系架构(SOA)。

    SOA是一种以低耦合和高度自治的服务为中心的软件架构,主要目标是实现业务需求。

    SOA倾向于所有的服务都基于有清晰定义的约定,并且都使用相同的通信协议。

    在SOA定义中,不太关注SOAP、REST、JSON或者XML,这都是实现上的细枝末节而已,不论使用什么技术或者协议,只要你的服务是松耦合的并解决了一组特定的业务需求就可以了。

    SOA不是所有问题的唯一答案,还有其它一些架构:比如分层架构、六角架构和事件驱动架构。

    分层架构

    分层架构:是一种将不同功能划分到不同层次的架构。低层的组件暴露一组API给高层组件使用,不过低层组件永远不会依赖高层组件提供的功能。

    分层架构的一个例子是操作系统及其各种组件。

    每一层都消费其低层提供的服务,但是低层永远不会消费上层提供的服务。

    另一个比较好的例子是TCP/IP编程栈,每一层都依赖低层提供的协议并增加新的功能。

    分层可以强制结构化并减少耦合,低层组件变得更简单和系统,其它部分更少耦合。替换低层组件只要实现相同API就可以了。

    分层构架的一个重要影响方面是越到底层稳定性越强。

    变更高层组件的API很容易,因为依赖它们的东西很少。但是变更低层的API就不划算了,因为有大量代码依赖这些已经存在的API。

    六角架构

    六角架构认为业务逻辑是架构的中心,所有数据存储、客户端、其它系统之间的交互都是平等的。业务逻辑和每一个非业务逻辑组件之间都有一个约定,但是没有底层和顶层的划分。

    在六角架构中,用户和应用的交互 与 应用和数据库系统的交互没有区别。它们都存在于应用业务逻辑之外而且都遵循一个严格的约定。定义好这些边界,就可以用一个自动化测试驱动代替一个人 或 用某个存储引擎代替数据库系统而不会对系统核心造成任何影响。

    事件驱动架构(EDA)

    简单地说,是以一种不同的方式去思考行动,即 对已经发生的事件做出反应。

    传统编程模型中,我们认为我们是请求某项工作要完成的人

    譬如,createUserAccount(),我们期望在我们等待结果的过程中,这个操作会被执行完成,

    然后我们继续后面的过程。

    EDA中,我们不会等待事情被做完。

    无论什么时候,我们和其它组件交互,我们只是宣布某件事情已经发生,然后就处理后面的过程了。

    所有架构的目的都是将系统切分成更小的独立的功能单位

    目的:构建更高层次的抽象以实现隐藏复杂性、减少依赖、各部分独立伸缩,以及每个部分并发开发

    可以认为Web服务层由若干高度自治的应用组成,每个Web服务自己就是一个应用。

    Web服务也可以彼此依赖,不过说到底还是少互相依赖为好。

    Web服务提供一个更高层次的抽象,可以让它看清整个系统并理解它。

    每个服务都隐藏了自身实现的细节并呈现一个简化的高层次的API。

    Web服务的主要目标: 解决业务需求。 如何实现,只是途径

  3. 支撑技术

消息队列、应用缓存、主数据存储、搜索引擎等,这些通常是用一些其它技术实现,一般都是一些第三方的产品,通过配置和我们的系统集成起来。

数据库(主数据存储)仅仅是一种技术,是实现的细节而已。

从应用架构的视角看,数据存储是一个让我们读写数据的地方

我们并不关心需要多少服务器,如何进行伸缩,怎么进行数据复制及容灾,甚至如何存储数据。

数据存储、缓存、消息队列,都是一种即插即用的扩展组件

如果决定更换持久层存储或者更换缓存后端,应该做到只更换数据连接组件就可以,保证整个架构不受影响。

数据存储不是应用架构的核心,不应该处于系统架构的支配地位。

通过将数据存储的抽象化,可以自由选择各种数据库,不论MySQL还是其它数据库。

如果应用逻辑有不同的需求,也可以考虑NoSQL数据存储或者内存型存储。

第三方服务在我们的控制之外,处于我们系统边界之外。因为我们不能控制它们,我们也就不能指望它们一直动作正常、没有BUG、像我们期望的那样快速伸缩。

通过设计一个间接的访问层,

将第三方服务和它们的系统隔离开来,

可以最大程度地降低使用风险和系统依赖

架构是从软件设计的角度看

基础设施是从系统工程师的视角看

每一种视角都是从不同方面展示同一问题:如何构建可伸缩的软件。

可伸缩Web应用的关键:架构、基础设施、技术、算法、业务需求之间的冲突


第二章 软件设计原则

许多实际项目中遇到的伸缩性问题其实可以归因于违反了软件设计的核心原则。软件设计原则比伸缩性更抽象更通用

1. 简单

使事物尽可能简单,但是不要过于简单。---阿尔伯特.爱因斯坦

软件天然就是错综复杂的。

判断一个东西是不是过于简单的时候,首先要回答的是对谁而言及对什么时候而言。譬如,对你而言还是对客户而言过于简单?是对现在开发而言还是对将来维护而言?

简单是让别的软件工程师以一种最容易的方式使用你的方案

简单是当系统变得更庞大更复杂的时候依然能够被理解

简单不是走捷径,不是为手边的问题找一个最快的方案。

重温写过的代码,区分复杂度,以此寻找简单的方案。从自己的错误开始是学习的每一步。

培养敏锐的感觉和能力,从而快速判断出长期而言什么是更简单的方案。

如果有机会能够找到一个导师或者能够和擅长发现简单的人一起共事,会进步更快。

简单产品可以从以下4个基本步骤开始

1.1 隐藏复杂与构建抽象

隐藏复杂与构建抽象是实现简化最好的方法之一。

人类的大脑处理能力有限的,要么对系统整体有个了解而不知道细节,要么知道系统的一小部分细节而不了解整体。

保持简单可以让你收放自如地从整体到细节各种粒度地查看系统。

不过,如果系统很庞大,是无法保持整体简单的,你能做的只是保持局部简单。

局部简单的主要方式是确保在设计和实现两个方面上,任何单个的类、模块、应用的设计目标及工作原理都能被快速理解。

  1. 看到一个类时,能够快速理解它的工作原理而无须知道系统其它部分的全部细节。只看着这个类就明白这个类能干什么。

  2. 看一个模块的时候,能不看这个模块本身,而是把这个模块当作一大堆类来看,而每一个类都是可理解的。

  3. 再缩小。当看一个应用的时候,能一眼看出那些关键的模块和它们的主要功能,而无须知道模块里的类的细节。

  4. 看整个系统的时候,能快速看出顶层的应用和它们的职责而无须知道每个应用是如何运行的。

节点表示类,边表示类之间的依赖。

好的设计原则是类之间的依赖关系尽量少

为了实现局部简单,需要将不同的功能分割到不同的模块中,这样当你从一个比较高的抽象层次去观察应用时,无须操作每个模块的职责是什么,只需考虑这些模块是如何交互的就可以了。

模块的层面,可以忽略模块内部的细节,只关注模块间的交换即可。

在庞大又复杂的系统中,当你创建一个独立服务的时候,需要添加一个抽象层。

依赖约定,而不是实现。

服务暴露这个更高层次的抽象,实现这些抽象所承诺的功能,从而隐藏其复杂性。

1.2 避免过度设计

当你试图预测每一个可能的用例和那些极少发生的场景时,

可能会忽视那些最主要的业务场景,

会情不自禁地被那些想象出来的问题牵着鼻子走,不知不觉地就过度设计了,最后会开发出一个比实际需求复杂得多的大而无当的系统。

好的设计方法是可以在后期逐渐添加新的功能特性,而不是一开始就开发一个超级大的系统。

早期先构建一个合理的抽象层次,然后迭代地增加新特性,

要比一开始就设计好全部的功能为将来各种可能进行开发好的多。

开发易于理解的简单系统并能证明其将来是可扩展的才是真正的难题

"这个设计是否可以更简单并且可以在将来依然保持弹性"

每次进行软件设计前都要问自己。

过度设计经常发生在人们以为自己在做正确的事,却选择了错误的方向或者太过关注未来的需求

“这里我需要如何做出权衡”

“这里我是否真的需要”

也建议你和业务相关方多接触,尽量去了解什么是最大的风险及还有什么没有搞清楚。

否则,你会按照那些没有用的教条花费大量的时间去构建一个没人用的系统。

本章的大部分设计原则都要花费一定功夫才能达成,你需要搞清楚合理的复杂和过度设计之间的界限。

因为界限不是那么黑白分明易于寻找,而是需要在一大片灰色地带中自己的斟酌权衡。----所以要做到这一点其实非常难

1.3 尝试测试驱动开发TDD

接受TDD的方法论可以提高简化性。

不必在所有地方都搞TDD-----在一些方法上搞TDD就可以获得一个全新的视角

TDD是一种开发实践:先写测试代码,然后写功能实现代码。

好处:

1.没有单元测试就没有代码,所以也就没有无用的代码。

由于开发先写了测试,就不会写那些没必要的代码,否则就又要去写测试代码。

Web Scalability for Startup Engineers Tip&Techniques for Scaling You Web Application --读书笔记的更多相关文章

  1. DOM Scripting -- Web Design with JavaScript and the Document Object Model 第2版 读书笔记

    1. childNodes  nodeValue <p id="p1">hello p!</p> alert(document.getElementById ...

  2. 【web.xml】报错java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener

    今天搭建新的项目,虽然在web.xml中配置了ContextLoaderListener以及IntrospectorCleanupListener 如下: web.xml中部分代码: <!-- ...

  3. Web高级征程:《大型网站技术架构》读书笔记系列

    一.此书到底何方神圣? <大型网站技术架构:核心原理与案例分析>通过梳理大型网站技术发展历程,剖析大型网站技术架构模式,深入讲述大型互联网架构设计的核心原理,并通过一组典型网站技术架构设计 ...

  4. Web Scraping with Python读书笔记及思考

    Web Scraping with Python读书笔记 标签(空格分隔): web scraping ,python 做数据抓取一定一定要明确:抓取\解析数据不是目的,目的是对数据的利用 一般的数据 ...

  5. 【读书笔记】iOS-使用Web Service-基于客户端服务器结构的网络通信(一)

    Web Service技术是一种通过Web协议提供服务,保证不同平台的应用服务可以互操作,为客户端程序提供不同的服务. 目前3种主流的Web Service实现方案用:REST,SOAP和XML-RP ...

  6. NGINX高性能Web服务器详解(读书笔记)

    原文地址:NGINX高性能Web服务器详解(读书笔记) 作者:夏寥寥 第4章  Nginx服务器的高级配置 4.1 针对IPv4的内核7个参数的配置优化 说明:我们可以将这些内核参数的值追加到Linu ...

  7. Nikto是一款Web安全扫描工具,可以扫描指定主机的web类型,主机名,特定目录,cookie,特定CGI漏洞,XSS漏洞,SQL注入漏洞等,非常强大滴说。。。

    Nikto是一款Web安全扫描工具,可以扫描指定主机的web类型,主机名,特定目录,cookie,特定CGI漏洞,XSS漏洞,SQL注入漏洞等,非常强大滴说... root@xi4ojin:~# cd ...

  8. 【读书笔记】读《高性能网站建设指南》及《高性能网站建设进阶指南:Web开发者性能优化最佳实践》

    这两本书就一块儿搞了,大多数已经理解,简单做个标记.主要对自己不太了解的地方,做一些记录.   一.读<高性能网站建设指南> 0> 黄金性能法则:只有10%~20%的最终用户响应时间 ...

  9. 【ASP.NET Web API教程】1.1 第一个ASP.NET Web API

    Your First ASP.NET Web API (C#)第一个ASP.NET Web API(C#) By Mike Wasson|January 21, 2012作者:Mike Wasson ...

随机推荐

  1. 【转载】Android进程保活招式大全

    原文地址:http://dev.qq.com/topic/57ac4a0ea374c75371c08ce8 目前市面上的应用,貌似除了微信和手Q都会比较担心被用户或者系统(厂商)杀死问题.本文对 An ...

  2. AtrousConvolution和dilated convolution

    唉,真烦哪些炒概念的,把整个世界都给弄乱了. 这里说一下dilated convolution和atrous convolution. 这两种是一样的,至少keras源码中是一样的.在keras中调用 ...

  3. 微信小程序自定义下导航页面切换效果的合理写法

    上图::: 导航模板内容页面的定义: <template name="naviBot">   <view class='navwrap t_cen font_26 ...

  4. 装饰器 decorator

    装饰器 def document_it(func): def new_function(*args, **kwargs): print('Running function:', func.__name ...

  5. CodeForces813E:Army Creation (主席树---上一题的加强版)

    As you might remember from our previous rounds, Vova really likes computer games. Now he is playing ...

  6. Servlet读取配置文件的三种方式

    一.利用ServletContext.getRealPath()[或getResourceAsStream()] 特点:读取应用中的任何文件.只能在web环境下. private void text3 ...

  7. vijos1842(火柴排队)

    描述 涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度.现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:∑i=1n(ai−bi)2∑i=1n(ai−bi) ...

  8. ARM、DSP、FPGA的技术特点和区别

    在嵌入式开发领域,ARM是一款非常受欢迎的微处理器,其市场覆盖率极高,DSP和FPGA则是作为嵌入式开发的协处理器,协助微处理器更好的实现产品功能. 那三者的技术特点以及区别是什么呢?下文就此问题略做 ...

  9. Win10资源管理器中的库文件夹按照修改日期排序

    win7之后添加的库十分的好用,可以将下载,音乐,文档设置在我的电脑(win10叫此电脑)首页,快速进入. 我对文件夹设置了按照时间排序,这样进去就可以直接看到最近下载了什么文件.但是win10用时间 ...

  10. linux磁盘存储管理基本命令和工具

    1 磁盘在linux表示方法 (1) IDE硬盘:hd[a~z]x,主设备号+次设备号+磁盘分区编号/hd(0-n,y) (2)SCSI硬盘:sd[a~z]x/hd(0-n,y) 注:主设备号可以唯一 ...