可伸缩系统的架构经验||消息队列

  队列(Queue)又称先进先出表(First In First Out),即先进入队列的元素,先从队列中取出。加入元素的一头叫“队头”,取出元素的一头叫“队尾”。利用消息队列可以很好地异步处理数据传送和存储,当你频繁地向数据库中插入数据、频繁地向搜索引擎提交数据,就可采取消息队列来异步插入。另外,还可以将较慢的处理逻辑、有并发数量限制的处理逻辑,通过消息队列放在后台处理,例如FLV视频转换、发送手机短信、发送电子邮件等。

可伸缩系统的架构经验

FEB 27TH, 2013 | COMMENTS

Larson首先认为,一个理想的系统,对于容量(Capacity)的增长应该与添加的硬件数是线性的关系。换言之,如果系统只有一台服务器,在增加了另一台同样的机器后,容量应该翻倍。以此类推。这种线性的容量伸缩方式,通常被称之为水平伸缩“Horizontal Scalability”。

在设计一个健壮的系统时,自然必须首要考虑失败的情况。Larson认为,一个理想的系统是当失去其中一台服务器的时候,系统不会崩溃。当然,对应而言,失去一台服务器也会导致容量的响应线性减少。这种情况通常被称为冗余“Redundancy”。

负载均衡

无论是水平伸缩还是冗余,都可以通过负载均衡来实现。负载均衡就好似一个协调请求的调停者,它会根据集群中机器的当前负载,合理的分配发往Web服务器的请求,以达到有效利用集群中各台机器资源的目的。显然,这种均衡器应该介于客户端与Web服务器之间,如下图所示:

Is> “user.%s” % user_iduser_blob = memcache.get(key)if user_blob is None: user = mysql.query(“SELECT * FROM>”%s””””

数据库缓存

数据库缓存对于应用代码没有污染,一些天才的DBA甚至可以在不修改任何代码的情况下,通过数据库调优来改进系统性能。例如通过配置Cassandra行缓存。

内存缓存

为了提高性能,缓存通常是存储在内存中。常见的内存缓存包括Memcached和Redis。不过采用这种方式仍然需要合理的权衡。我们不可能一股脑儿的将所有数据都存放在内存中,虽然这会极大地改善性能,但比较起磁盘存储而言,RAM的代价更昂贵,同时还会影响系统的健壮性,因为内存中的数据没有持久化,容易丢失。正如之前提到的,我们应该将需要的数据放入缓存,通常的算法是least recently used,即LRU。

CDN

提高性能,降低Web服务器负载的另一种常见做法是将静态媒体放入CDN(Content Distribution Network)中。如下图所示:

CDN可以有效地分担Web服务器的压力,使得应用服务器可以专心致志地处理动态页面;同时,CDN还可以通过地理分布来提高响应请求的性能。在设置了CDN后,当系统接收到请求时,首先会询问CDN以获得请求中需要的静态媒体(通常会通过HTTP Header来配置CDN能够缓存的内容)。如果请求的内容不可用,CDN会查询服务器以获得该文件,并在CDN本地进行缓存,最后再提供给请求者。如果当前 站并不大,引入CDN的效果不明显时,可以考虑暂不使用CDN,在将来可以通过使用一些轻量级的HTTP服务器如Nginx,为静态媒体分出专门的子域名如static.domain.com来提供服务。

缓存失效

引入缓存所带来的问题是如何保证真实数据与缓存数据之间的一致性。这一问题通常被称之为缓存失效(Cache Invalidation)。从高屋建瓴的角度来讲,解决这一问题的办法无非即使更新缓存中的数据。一种做法是直接将新值写入缓存中(通常被称为write-through cache);另一种做法是简单地删除缓存中的值,在等到下一次读缓存值的时候再生成。

整体而言,要避免缓存实效,可以依赖于数据库缓存,或者为缓存数据添加有效期,又或者在实现应用程序逻辑时,尽量考虑避免此问题。例如不直接使用DELETE FROM a WHERE…来删除数据,而是先查询符合条件的数据,再使得缓存中对应的数据失效,继而根据其主键显式地删除这些行。

Off-Line处理

这篇文章还提到了Off-Line的处理方式,即通过引入消息队列的方式来处理请求。事实上,在大多数企业软件系统中,这种方式也是较为常见的做法。在我撰写的文章《案例分析:基于消息的分布式架构》中,较为详细地介绍了这种架构。在引入消息队列后,Web服务器会充当消息的发布者,而在消息队列的另一端可以根据需要提供消费者Consumer。如下图所示。对于Off-Line的任务是否执行完毕,通常可以通过轮询或回调的方式来获知。

为了更好地提高代码可读性,可以在公开的接口定义中明确地标示该任务是On-Line还是Off-Line。

引入Message Queue,可以极大地缓解Web服务器的压力,因为它可以将耗时较长的任务转到专门的机器上去执行。

此外,通过引入定时任务,也可以有效地利用Web服务器的空闲时间来处理后台任务。例如,通过Spring Batch Job来执行每日、每周或者每月的定时任务。如果需要多台机器去执行这些定时任务,可以引入Spring提供的Puppet来管理这些服务器。Puppet提供了可读性强的声明性语言来完成对机器的配置。

Map-Reduce

对于大数据的处理,自然可以引入Map-Reduce。为整个系统专门引入一个Map-Reduce层来处理数据是有必要的。相对于使用SQL数据库作为数据中心的方式,Map-Reduce对可伸缩性的支持更好。Map-Reduce可以与任务的定时机制结合起来。如下图所示:

平台层

Larson认为,大多数系统都是Web应用直接与数据库通信,但如果能加入一个平台层(Platform Layer),或许会更好。

首先,将平台与Web应用分离,使得它们可以独立地进行伸缩。例如需要添加一个新的API,就可以添加新的平台服务器,而无需增加Web服务器。要知道,在这样一个独立的物理分层架构中,不同层次对服务器的要求是不一样的。例如,对于数据库服务器而言,由于需要频繁地对磁盘进行I/O操作,因此应保证数据库服务器的IO性能,如尽量使用固态硬盘。而对于Web服务器而言,则对CPU的要求比较高,尽可能采用多核CPU。

其次,增加一个额外的平台层,可以有效地提高系统的可重用性。例如我们可以将一些与系统共有特性以及横切关注点的内容(如对缓存的支持,对数据库的访问等功能)抽取到平台层中,作为整个系统的基础设施(Infrastructure)。尤其对于产品线系统而言,这种架构可以更好地为多产品提供服务。

最后,这种架构也可能对跨团队开发带来好处。平台可以抽离出一些与产品无关的接口,从而隐藏其具体实现的细节。如果划分合理,并能设计出相对稳定的接口,就可以使得各个团队可以并行开发。例如可以专门成立平台团队,致力于对平台的实现以及优化。

Posted by 张逸 Feb 27th

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2013年11月20日
下一篇 2013年11月22日

相关推荐