Instagram 的平台架构和扩展

在没有正式在中国市场登陆的情况下,Instagram 已经突破10亿用户了。这个产品的用户活跃度非常之高。它从工程的角度来说,它跟朋友圈和微博有两个不同的需求。1. 它的用户几乎均匀的分布在全世界,而中国产品的用户绝大多数在中国。2. 它是公开社交,这点更像微博,而朋友圈是熟人社交。

Instagram 截止2018年6月的规模

  • 10亿月活跃用户,80%以上用户不在美国。女性用户大约是男性的1.5倍
  • 每天100亿次以上的点赞
  • 每天10亿次以上的上传图片或视频
  • 最大账号,1.1亿以上的粉丝数量。比如下面两位:

01

可以看出,Instagram 是一个极其庞大的网站

下图是Instagram 的全球数据中心分布。绿色的是使用 Facebook 的数据中心,红色的是使用 AWS,黄色的计划建设的。

为了测试整个网站的稳定性,Facebook 也会经常做 Chaos engineering 测试:在 production 环境下关闭一个 data centre,看网站是否能正常运行。

AWS 曾经在 2017年3月出现过一次较长时间的 outage。像 instagram 这个用户规模的网站如果完全依赖于 AWS,那么可能出现灾难性的后果,那将完全指望 Amazon 尽快修好自己的 server。

上面这个图是大体上的架构。

  • Django 做 web server,使用 Python
  • Casaandra 是 NoSQL 数据库
  • PostgreSQL 是关系型数据库
  • memcache 做缓存
  • RabbitMQ是消息队列
  • Celery 是后台任务系统

在 Instagram 大体有两类 services。一类是 storage 存储,一类是 computing 计算。

Storage 存储

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 机制

Computing 计算

不同于存储,每个 region 在计算方面是相对独立的。在每个 region ,Instagram 都部署了 Django,Celery 和 RabbitMQ。Load balancer 会把 request 发给本地的 Django,backend job 和 queue message 也是在当地 region 运行。

Memcache

因为 cache 的目标就是为了提速,也就是说从 CAP 理论来讲,追求 low latancy,所以会牺牲 consistency。 进而也就是说,如果一个美国用户发了一条 post ,欧洲用户晚那么几分钟收到也没什么关系。

现在假设一个场景。一个用户发了一条 post 。web server (这里指 Django)将这条 post 被写入了 PostgreSQL,也写入了当地 data centre 的 memcache 里面。此时另一个用户访问这条 post 时:

  1. web server 总是先尝试从 memcache 读取数据。如果读者用户也在当地,那么可以直接从 memcache 读到 post
  2. 而如果读者在其他地区,此时因为延迟,读者不一定能及时读到 post

那如何将这条 post 复制到其他地区的 memcache呢?

  1. 发布者地区的 data centre 会通过 PostgreSQL 复制机制,将数据复制到其他地区,也就是图中右边部分的 DC2
  2. DC2 的 PostgreSQL 会告诉当地的 memcache 说,相关的数据过期了(比如说这个发布者的所有最近 post 这个数据已过期),应把它从 memcache 里清除。DC2 的 web server 之后发现 memcache 里没有需要的数据,会从 PostgreSQL 读取,然后更新 memcache

而当数据从 memcache 被清除的那一刻,如果流量很大,那么所有的 web server 瞬间全部转向 PostgreSQL 询问。这时 database 的压力非常大, 极有可能影响其他业务。

这个在英文里叫 thundering herd problem,中文常常叫缓存的雪崩效应。

解决的办法是使用 memcache lease 机制。当第一台 web server 找 memcache 要数据时,memcache 说“我没有数据,你去 database 拿最新数据来更新我吧”,而再之后的 web server 再来问, memcache 说“我没有数据,但是有人去拿数据了。你要么等等,要么用我这里过期的数据吧”

Scale UP 纵向扩展优化

Instagram 会尝试提高每台服务器的效率,而不是一味的增加服务器数量。比如使用高效的代码,更加底层的代码,从而提高CPU使用率。

为了帮助 scale up,Instagram 对 CPU 使用率做了监测、分析和优化。

  • 监测:在服务器上将 CPU 使用率信息发送给专门的平台统计。当使用率出现异常变化时,会触发警报。有时候这些变化是因为发布了新功能造成的。这也有助于 Instagram 分析哪些功能还有提高空间。
  • 分析:在 Django 中使用了 Python cProfile 库,它可以用来分析函数调用栈的各层中所使用的时间(这个过程叫做 profiling),生成的结果可以被其他工具绘制成图(如上图)。但要指出的是,profiling 对性能是有挺大影响的。所以不要将 profiling 代码一直运行,而是偶尔运行。
  • 优化:Instagram 在服务器端,针对逻辑使用了一些优化技巧。比如用户请求一张图片的 url 时,根据用户的位置,服务器直接返回离用户最近的 cdn url 地址。再比如将一些经常被调用的函数使用 Cython 或者 C/C++来写,减少实际调用的 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+ 服务器上。

参考资料:Scaling Instagram

微信公众号:网站架构札记

最近发布