可扩展的web架构 和 分布式系统

原文链接

这是左耳朵耗子推荐的入门文章,咱来读一读吧

挑选一些重点做笔记

本文覆盖了设计大型网站时的一些关键问题,以及实现目标的一些组件

1.1.分布式系统的设计原则

关键原则:

  • Availability:可用性。高可用性需要仔细考虑关键组件的冗余、部分系统失败时的快速恢复、出现问题时的降级
  • Performance:性能。响应速度。
  • Reliability:可靠性。请求可以返回相同的数据。
  • Scalability:可扩展性。能处理多少流量。扩容的时候是否容易。
  • Manageability:可管理性。出现问题的时候容易排查定位。
  • Cost:成本。部署和维护系统需要的成本。

实现某个原则可能会以牺牲另一个原则为代价。基本的例子:简单地通过增加服务器来扩容,代价就是可管理性下降(要维护更多服务器)、更高的成本(买服务器)

1.2.基础

举个例子:图像服务

系统支持上传图像,然后返回url。

系统其他的一些重要方面:

  • 没有上传图片的限制,所以要考虑容量
  • 图像下载要快,延时小
  • 如果用户上传了图像,那可别丢了(可靠性)
  • 容易维护
  • 赚不了啥钱,成本要低

在这里插入图片描述

我们来假设我们要搞一个大系统,像Flickr(什么鬼,我们这里假装是在造快手吧😂)

服务

当我们设计可扩展的分布式系统的时候,必须把每个模块都当做一个相对独立的服务,且这个服务定义了清晰的接口。实际上这样的系统我们成为SOA(Service-Oriented Architecture)面向服务的架构

这样的设计,使得问题容易被分解。每个模块可以相对独立的扩容。很像面向对象的设计。

前面的例子,上传和读取图片的请求都是打到同一个服务器的,但是随着服务规模变大,我们很要必要将其拆分成两个模块。

假设服务压力很大。这样的场景下,我们可以看到大量的写请求是怎么样影响读图片的请求的。(两种请求在竞争资源)就算上传和下载的速度是一样(实际上在大多数网络不是这样的,因为大多都是设计成下载速度:上传速度为3:1),读文件大多会从cache读,而写则最终还是要到磁盘上(而且要求最终一致性的场景下经常是要写好几次)。就算所有东西都在内存或者从SSD读,写数据库永远还是比读慢。

另外一个潜在的问题是,web server,像Apache或者lighttpd有一个同时连接数的上限,默认是大概500,在高并发情况下,写请求会很快消耗掉所有这些连接数。因为读可以是异步的, 或者可以利用其它性能优化像是gzip压缩或者chunked transfer encoding,web server可以更快地切换读服务,在客户端之间切换,进而可以服务比连接数更多的请求(像之前说的默认设置500,但实际上达到1000也是很轻松的)。然而写请求呢,更倾向于在上传期间维护一个打开的连接,所以上传1MB的文件在很多家用网络可能耗时超过一秒,所以web server只能同时维护500个同时的写请求。

在这里插入图片描述

怎么解决这种瓶颈呢?自然就是分开读服务和写服务呗。像上面这个图1.2所示。这就允许我们独立地,分开去扩容缩容(我们永远都是读多写少)但是也帮助搞清楚了每个点是怎么回事,故障排查更容易了,解决读的慢的问题也容易了。

这个方法的优点是我们可以分开的解决问题,我们不用担心在同一个上下文同时写和读图片。优化的策略也可以分开搞,(比如,请求排队,缓存热门图片)

上面这个例子可以很好的工作,当你有两个不同的endpoint的时候。(事实上这跟云存储提供的实现和CDN挺像的)有很多方法解决问题,当然不同的方法之间有不同的tradeoff

比如Flickr是怎么解决读写问题呢?他们是将用户分为不同的shards,每个shard只处理一部分用户请求,用户增加了,那就在集群中加入更多的shards。第一个例子中,很容易根据实际的使用(整个系统的读写数量)扩容硬件,而Flickr的例子中扩容缩容是根据用户的(但是假设每个用户的使用是相同的所以可能会有额外的容量)前一个例子,有个问题是如果其中一个服务挂了,整个系统就挂了(如没人可以写文件了)而Flickr的方案只会影响一部分用户。在第一个例子,更容易在整个数据集做一些操作:比如,更新写服务增加一些元数据或者在所有图片数据中进行搜索。然而Flickr架构中每个shard需要被更新或者搜索

所以其实没有所谓正确的答案,但是这些都帮助我们回到本章开始的那些原则,我们看看系统需要什么(大量读?大量写?两者都有?并发的程度?整个数据集的查询?查询某个范围?排序?)仔细对比不同方案的优劣点,明白系统什么情况下会有问题,在故障发生时有一个有效的应对计划

NOTE: 上面说的这些可以用我们熟悉一点的例子来看,比如数据库。第一个例子类似于读写分离的数据。第二个例子类似于基于用户id做分库分表。

冗余

多个一模一样的服务,最好还是地理上异地的,异地多活

Partition分区

水平扩容(shards:地理分区、付费用户/非付费用户分区)或者垂直扩容(让单个机器变强)

考虑不一致问题,读写并发

1.3. 用啥模块来建设一个快速的可规模化的数据访问服务?

很多web服务实际上最终压力都压在了数据库中

四个重要的家伙:缓存、代理、索引、负载均衡

缓存

最近访问的数据很可能被再次访问。在计算机的各种层次都用到了这个原则。

如果负载均衡将你的请求每次都打到不同的机器,那cache就很可能失效了。解决这个问题要用:全局缓存或者分布式缓存

全局缓存

有点像Redis

两种办法:

1、全局缓存在找不到数据的时候,自己去找数据
2、每个请求先查全局缓存,查不到就去查真正的数据然后写入到全局缓存中

在这里插入图片描述
大多数都用第一种办法。一些case下用第二种整挺好。比如,如果缓存是用来做大文件的,缓存一共就这么大,那命中率肯定就很低。这种情况下用第二种方案就可以解决。另一个例子是存在cache的文件是静态的不应该被换出。(应用有这个需求,那么应用的逻辑就知道这些不应该被换出,由应用来控制)

分布式缓存

每个节点拥有部分缓存数据(不要把鸡蛋放在同一个篮子里)

牛皮例子:memcached

代理

用途:过滤请求、记录请求、有时转换请求(增加/删除headers、加密/解密、压缩)

协同处理多个服务器、提供机会在全局层面来优化请求量。其中一个加快数据访问的方法是,将多个一样的请求整合成一个,请求后返回。collapsed forwarding

在这里插入图片描述

LAN代理:客户端不需要自己的ip来连接Internet,LAN会把相同的请求collapse。

还有一个应用:把对同一个空间的访问合到一起。

在这里插入图片描述
通常要把cache放在Proxy前面,让快点的runner先跑。

可选方案:Squid、Varnish

索引

是一种权衡:增加了存储和降低了写入速度。加快了读的速度

举例:倒排索引

负载均衡

算法挺多:随机找一个、round robin、基于某个标准来选择(内存或者CPU使用率)。开源的:HAProxy

复杂的系统,很可能一个请求走过多个负载均衡:

在这里插入图片描述
主要的挑战是:用户session的流量:电商购物车。其中一种解决方案:同一个用户永远分配到同一个机器,但这样的话一些自动failover的方案就很难搞了。

节点发现、健康度检查等也是相关的内容

队列

写请求可能是需要很长时间的,

异步系统

开源队列:RabbitMQ, ActiveMQ, BeanstalkD, services : Zookeeper, 甚至是数据存储: Redis.

1.4.结论

这些设计都很令人兴奋,有很多好的组件让我们可以造出很多新的应用。本文只是比较表面的蹭一蹭,这个领域也会有更多的创新!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值