[译]Web架构入门

程序员专属-极客T恤

图:现代web应用架构概览

上图很好的展示了我们公司(storyblocks)的产品架构。如果你不是一个经验老道的web开发者,你可能会感到复杂。下面我们一起深入每个组件的细节,有助于整个网络应用构架。

用户在google搜索“Strong Beautiful Fog And Sunbeams In The Forest”得到的第一条结果,有可能是来自Storyblocks的拥有的一张照片,因为我们公司向google交钱了。在用户点击搜索结果的背后,浏览器会向dns服务器发送请求,试图与storyblocks服务器取得联系。

请求在到达了我们的服务器群之前,首先命中的是负载均衡服务器(Load Balancer),负载均衡服务器随机选择应用服务器或特定应用服务器处理请求。应用服务器从缓存服务(Caching service)中找到一些与请求相关的信息,如有必要,再从数据服务器(Database)中找到其它信息。在处理该请求时,我们发现某个业务信息(如照片的色彩信息Color profile)没有拿到,于是我们发送了一个请求到后台作业系统。我们的作业服务器(Job Servers)将异步处理该作业队列(Job Queue),返回适当的结果更新之前缓存及数据库搜索到的结果。

接下来,我们试着用照片的标题等信息向我们的全文搜索引擎查找类似的照片。如果该搜索者恰好是storyblocks的用户并已登录,我们会将该搜索过程与该用户账号生成一个页面视图事件。通过数据分发服务(Data “firehose”),将该事件数据分发至云存储服务器(Cloud Storage),或最终会被推送到数据仓库(Data warehouse),用于进行深度业务分析。

最后应用服务器将数据渲染成html视图通过负载均衡服务器原路返回给浏览器。这个html页面包含一些javascript和css等静态资源,浏览器会访问我们cdn服务器获取到这些资源,最后的最后,就是用户最终看到的页面了。

1.  DNS

DNS是“Domain Name Server”的缩写,是互联网核心协议之一。它的作用非常简单,就是根据域名查出IP地址。你可以把它想象成一本巨大的电话本。

举例来说,如果你要访问域名math.stackexchange.com,首先要通过DNS查出它的IP地址是151.101.129.69。

2. Load Balancer 负载均衡服务器

在深入研究负载平衡的细节之前,我们需要回头讨论应用程序扩展的两种方式:水平扩展与垂直扩展。他们是什么,有什么区别?StackOverflow有个帖子讨论了这个问题,水平扩展意味着您可以通过在资源池中添加更多计算机来扩展,而垂直扩展意味着您可以通过向现有计算机添加更多功率(例如,CPU,RAM)来扩展。

在Web开发中你会碰到许多问题:网络中断、服务器随机崩溃、网络降级、整个数据中心偶尔会脱机。简单起见,你几乎总是希望水平扩展你的web应用,因为拥有多个服务器允许您规划中断,以便您的应用程序继续运行。换句话说,您的应用程序是“容错的”。其次,横向扩展允许您通过让每个部分在不同的服务器上运行来最小化地耦合应用程序后端的不同部分(Web服务器,数据库,服务X等)。还有一个原因是,你已经垂直扩展你的服务器了,但已经没办法再垂直扩展了,进而只能选择水平扩展。世界上没有任何计算机足以完成所有应用程序的计算。将Google的搜索平台视为一个典型的例子,尽管这适用于规模小得多的公司。例如,Storyblocks可以在任何给定的时间点运行150到400个AWS EC2实例,这样的计算能力是垂直扩展无法达到的。

好的,回到负载平衡。它让水平扩展成为可能。它们将传入的请求路由到许多应用程序服务器中的一个,这些服务器通常是彼此的克隆/镜像映像,并将响应从应用程序服务器发送回客户端。他们中的任何一个都以相同的方式处理请求,因此只需要在负载均衡服务器集中分发请求,它确保了应用服务器不发生过载的情况。

从概念上讲,负载平衡非常简单。当然它的实现并不简单,这里我们就不深入讨论。

3. Web应用服务器

Web应用服务器的描述相对简单, 它们执行处理用户请求的核心业务逻辑,并将HTML发送回用户的浏览器。 它们通常与各种后端基础设施进行通信,例如数据库,缓存层,作业队列,搜索服务,其他微服务,数据/日志记录队列等。 如上所述,为了处理用户请求,您通常至少有两个,通常还有更多的应用服务器,随时准备被负载均衡服务器调度。

应用服务器中的业务实现需要选择特定语言(Node.js,Ruby,PHP,Scala,Java,C#.NET等)和该语言的Web MVC框架(Express for Node.js,Ruby on Rails ,Play for Scala,Laravel for PHP等)。 但是,深入研究这些语言和框架的细节超出了本文的范围。

4. 数据服务器

每个现代Web应用程序都用到一个或多个数据库来存储信息。数据库一般需要提供数据结构定义,插入新数据,查找现有数据,更新或删除现有数据,跨数据执行计算等的功能。在大多数情况下,Web应用程序服务器会与数据库直接对接,或通过作业服务器对接数据。此外,每个后端服务可能拥有自己的数据库,该数据库与其它应用程序隔离。

这里我们大致了解一下:SQL和NoSQL。

SQL代表“结构化查询语言”,它是在20世纪70年代发明的,它提供了一种查询可访问的关系数据集的标准方法。 SQL数据库将数据存储在通过公共ID(通常是整数)链接在一起的表中。让我们来看一个为用户存储历史地址信息的简单示例。您可能有两个表,user和user_addresses,由用户的id链接在一起。有关简单版本,请参见下图。这些表是链接的,因为user_addresses中的user_id列是users表中id列的“外键”。

如果你对SQL不太了解,我强烈建议你在Khan Academy查找相关的教程。

NoSQL代表“非SQL”,它是一种新的数据库技术集,它可以处理大规模Web应用程序可以生成的大量数据(SQL的大多数变体都不能很好地水平扩展, 只能垂直扩展到某一点)。 如果您对NoSQL一无所知,我建议从这些高级介绍开始:

  • https://www.w3resource.com/mongodb/nosql.php
  • http://www.kdnuggets.com/2016/07/seven-steps-understanding-nosql-databases.html
  • https://resources.mongodb.com/getting-started-with-mongodb/back-to-basics-1-introduction-to-nosql

5. 缓存服务器

缓存服务提供简单的键/值数据存储,使得在接近O(1)时间内保存和查找信息成为可能。应用程序通常利用缓存服务来保存昂贵计算的结果,以便能够从缓存检索结果,而不是下次需要时重新计算它们。应用程序可能会缓存来自数据库查询、调用外部服务、给定URL的HTML以及更多的结果。这里有一些来自现实世界应用的例子:

  • Google会缓存搜索结果,以获取常见的搜索查询,比如“dog”或“Taylor Swift”,而不是每次都重新计算它们。
  • Facebook缓存了您登录时看到的许多数据,比如发布数据、朋友等。请阅读有关Facebook缓存技术的详细文章
  • 我们公司(Storyblocks)缓存服务器端的HTML输出对渲染、搜索结果、超前结果等的反应。

两种主流的缓存服务器技术是Redis和Memcache。我将在另一篇文章中详细介绍。

6. 作业队列与服务器

大多数Web应用程序都需要在幕后异步地做一些与响应用户的请求没有直接关联的工作。例如,谷歌需要爬行并索引整个互联网,以便返回搜索结果。当然,他不会在每次搜索请求时更新索引。相反,它异步地爬网,默默的更新搜索索引。

虽然存在多种可以完成异步工作的体系结构,但最普遍的是我将其称为“作业队列”体系结构。它由两个组件组成:需要运行的“作业”队列和运行队列中的作业的一个或多个作业服务器。

作业队列存储需要异步运行的作业列表。最简单的是先进先出(FIFO)队列,但大多数应用程序最终需要某种优先级排队系统。无论何时,只要应用程序需要运行作业,或者按照某种常规调度,或者由用户操作决定,它就简单地将适当的作业添加到队列中。

例如,Storyblocks 利用作业队列为支持我们的市场所需的许多幕后工作提供动力。我们运行作业系统来编码视频和照片,处理用于元数据标记的CSV,聚合用户统计数据,发送密码重置电子邮件等等。我们从一个简单的FIFO队列开始,尽管我们升级到优先级队列,以确保像发送密码重置电子邮件这样的时间敏感操作尽快完成。

作业服务器处理作业。他们轮询工作队列以确定是否有工作要做,如果有的话,他们从队列中弹出一个任务并执行它。底层的语言和框架的选择与Web服务器一样多,所以我不会在本文中详细讨论。

7. 全文检索服务

许多(大多数)web应用程序支持某种搜索功能,其中用户提供文本输入(通常称为“查询”),应用程序返回最“相关”的结果。支持此功能的技术通常称为“全文搜索”,它利用反向索引来快速查找包含查询关键字的文档。

上图演示如何将三个文档标题转换为权重索引,以便于快速查找从特定关键字到标题中具有该关键字的文档。注意,诸如“in”、“the”、“with”等常用单词(称为停止单词)通常不包括在索引中。

虽然可以从一些数据库直接进行全文搜索(例如,MySQL支持全文搜索),但是通常还是会运行单独的“搜索服务”,该服务计算并存储索引并提供查询接口。今天最流行的全文搜索平台是弹性搜索,尽管还有其他选项,如Sphinx或Apache Solr。

8. 其它服务

一旦应用程序达到一定的规模,就有可能存在某些“服务”,它们被划分成作为单独的应用程序运行。它们不受外部世界的影响,但应用程序和其他服务与它们交互。例如,Storyblocks 有几个操作性和计划性的服务:

  • 帐户服务在我们的所有站点中存储用户数据,这允许我们容易地提供交叉销售机会并创建更统一的用户体验
  • 内容服务存储所有视频、音频和图像内容的元数据。它还提供了下载内容和查看下载历史的接口。
  • 支付服务为客户信用卡提供了一个接口。
  • “HTMLPDF服务”提供了一个简单的接口,它接受HTML并返回相应的PDF文档。

9. 数据

今天,公司的生存和死亡取决于他们如何利用数据。现在,几乎每个应用程序一旦达到一定规模,就利用数据管道来确保可以收集、存储和分析数据。典型的管道有三个主要阶段:

  • 该应用程序将数据(通常是关于用户交互的事件)发送到数据“firehose”,该数据“firehose”提供流接口来摄取和处理数据。通常,原始数据被转换或增强并传递给另一个”firehose”。AWS Kinesis 和Kafka 是用于此目的的两种最常见的技术。
  • 原始数据以及最终的转换/加强数据被保存到云存储。AWS Kinesis提供了名为“firehose”的设置,这使得将原始数据保存到云存储(S3)极其容易。
  • 转换/加强的数据通常被加载到数据仓库中进行分析。尽管大公司经常使用Oracle或其他专有仓库技术,我们你其它创业公司一样使用了AWS Redshif。如果数据集足够大,可能需要一个Hadoop样的NoSQL的MapReduce技术来进行分析。

架构图中未被展示的另一个步骤:将数据从应用程序和服务的操作数据库加载到数据仓库中。例如,在Storyblocks中,我们每晚将VideoBlocks、AudioBlocks、Storyblocks、帐户服务和贡献者门户数据库加载到Redshift中。这为我们的分析师提供了一个完整的数据集,通过将核心业务数据与用户交互事件数据一起定位。

10. 云存储

据AWS称,“云存储是一种通过互联网存储,访问和共享数据的简单且可扩展的方式”。 您可以使用它来存储和访问或多或少存储在本地文件系统上的任何内容,并且可以通过HTTP上的RESTful API与其进行交互。 亚马逊的S3产品是目前最流行的云端存储产品,也是我们Storyblocks广泛依赖的产品,用于存储我们的视频,照片和音频资产,CSS和Javascript,用户事件数据等等。

11. CDN

CDN的全称是Content Delivery Network,即内容分发网络。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度。 例如,在下图中,西班牙的用户从位于纽约市的原始服务器的站点请求网页,但该页面的静态文件是从英格兰的CDN“最近”服务器加载的,从而防止了许多缓慢的跨大西洋HTTP 请求。

请查阅这篇文章进行更全面的介绍。一般来说,Web应用程序应该总是使用CDN来服务CSS、JavaScript、图像、视频和其他静态文件。一些应用程序还可以利用CDN来服务静态HTML页面。

最后

这是Web体系结构101的总结,我希望对你有所帮助。我希望在接下来的一两年里,我会发表该系列201进阶文章,深入研究这些组件。

参考资料

  • https://stackoverflow.com/questions/11707879/difference-between-scaling-horizontally-and-vertically-for-databases
  • http://www.ruanyifeng.com/blog/2016/06/dns.html
  • https://baike.baidu.com/item/CDN

关注微信公众号

码中人 微信公众号