博客原文:http://allenlsy.com/scaling-instagram
在没有正式在中国市场登陆的情况下,Instagram 已经突破10亿用户了。这个产品的用户活跃度非常之高。它从工程的角度来说,它跟朋友圈和微博有两个不同的需求。1. 它的用户几乎均匀的分布在全世界,而中国产品的用户绝大多数在中国。2. 它是公开社交,这点更像微博,而朋友圈是熟人社交。
可以看出,Instagram 是一个极其庞大的网站
下图是Instagram 的全球数据中心分布。绿色的是使用 Facebook 的数据中心,红色的是使用 AWS,黄色的计划建设的。
为了测试整个网站的稳定性,Facebook 也会经常做 Chaos engineering 测试:在 production 环境下关闭一个 data centre,看网站是否能正常运行。
AWS 曾经在 2017年3月出现过一次较长时间的 outage。像 instagram 这个用户规模的网站如果完全依赖于 AWS,那么可能出现灾难性的后果,那将完全指望 Amazon 尽快修好自己的 server。
上面这个图是大体上的架构。
在 Instagram 大体有两类 services。一类是 storage 存储,一类是 computing 计算。
PostgreSQL 存储更多需要做 join 操作的数据,比如用户之间的 follow 关系。这里使用 master 和 replica 的结构,实现 eventually consistency。replica 分布在不同的 data centre。Django 将数据写入 master,而从 replica 读取,这样读写分离。实际应用中发现,master 到 replica 这个延迟对业务没什么影响。
Cassandra 存储用户发的 post,用户活动记录等等。Cassandra 自己是一个没有 master 的 NoSQL 数据库,各 replica 之间实现 eventually consistency 。Cassandra 的 consistency 是通过 Quorum 机制实现的,所以根据业务的不同,通过调节 quorum 数量可以配置不同程度的 consistency。
Datastax 这篇文章 介绍了Cassandra 和 quorum 机制
不同于存储,每个 region 在计算方面是相对独立的。在每个 region ,Instagram 都部署了 Django,Celery 和 RabbitMQ。Load balancer 会把 request 发给本地的 Django,backend job 和 queue message 也是在当地 region 运行。
因为 cache 的目标就是为了提速,也就是说从 CAP 理论来讲,追求 low latancy,所以会牺牲 consistency。 进而也就是说,如果一个美国用户发了一条 post ,欧洲用户晚那么几分钟收到也没什么关系。
现在假设一个场景。一个用户发了一条 post 。web server (这里指 Django)将这条 post 被写入了 PostgreSQL,也写入了当地 data centre 的 memcache 里面。此时另一个用户访问这条 post 时:
那如何将这条 post 复制到其他地区的 memcache呢?
而当数据从 memcache 被清除的那一刻,如果流量很大,那么所有的 web server 瞬间全部转向 PostgreSQL 询问。这时 database 的压力非常大, 极有可能影响其他业务。
这个在英文里叫 thundering herd problem,中文常常叫缓存的雪崩效应。
解决的办法是使用 memcache lease 机制。当第一台 web server 找 memcache 要数据时,memcache 说“我没有数据,你去 database 拿最新数据来更新我吧”,而再之后的 web server 再来问, memcache 说“我没有数据,但是有人去拿数据了。你要么等等,要么用我这里过期的数据吧”
Instagram 会尝试提高每台服务器的效率,而不是一味的增加服务器数量。比如使用高效的代码,更加底层的代码,从而提高CPU使用率。
为了帮助 scale up,Instagram 对 CPU 使用率做了监测、分析和优化。
纵向扩展的另一个方面叫做 less server (减少服务器数量)。一台服务器上要运行很多 process。这些process 之间,在内存中,有时是可以共享同一段代码,有时使用自己的独立内存空间。如果让一台服务器上的 process 尽量多的共享代码,比如让同类的 process 运行在同一台服务器上,那么在 cluster 中就可以减少服务器数量。
网络延迟也是可以优化的一方面。
比如 Instagram app 的首页分为几个部分。为了提高 app 响应速度,客户端 app 是通过异步的方式,从不同的 service 请求不同的数据,再展示在 app 上,而并不是从同一 service 同步获取。
Instagram,如以往文章中提到的 Google 开发流程一样,也是使用 Trunk based development。新功能直接在 master branch 开发,使用 CI 控制代码质量。这使得各个功能的开发团队都能使用最新代码。同时,Instagram 会持续监测代码的性能。
代码上线的过程是:工程师开发功能 -> 测试团队测试,反馈 -> 内部员工 alpha 测试 -> Canary deployemnt (灰度发布/金丝雀发布)-> 100% 上线。上线的过程中会逐步进行 load test 负载测试。上线过程使用了自动化的 Continuous delivery (持续支付)流程,所以每天能 deploy 40-60 次。deploy 一次需要大概10分钟,就能 deploy 到 20k+ 服务器上。
微信公众号:网站架构札记