通向高可扩展性之路(推特篇) ---- 一个推特用来支撑1亿5千万活跃用户、30万QPS、22MB每秒Firehose、以及5秒内推送信息的架构

原文链接:http://highscalability.com/blog/2013/7/8/the-architecture-twitter-uses-to-deal-with-150m-active-users.html

写于2013年7月8日,译文如下:

“可以解决推特所面临的挑战”的玩具般的方案是一个常用在扩展性上的比喻。每个人都觉得推特很容易实现。稍微具备一些系统架构的知识我们就可以构建一个推特,就这么简单。但是根据推特软件开发部门的VP Raffi Krikorian在 Timelines at Scale 中所做的极其详尽的演说来看,这一切并不是那么简单。如果你想知道推特到底是怎么工作的,那么现在就开始了:

推特的成长是渐进式的,以至于你可能会忽略他们的增长。它一开始仅仅是一个三层结构的Ruby on Rails网站,然后慢慢就变成了一个靠谱的以服务为导向的核心网站,以至于当我们想确定网络是不是出问题时会去尝试连接推特。多大的一个变化啊!

推特现在在世界范围内有1.5亿活跃用户、每秒处理30万请求来生成时间轴以及一个可以每秒输出22MB的Firehose。每天这个系统要接受4亿条消息。不出5分钟,Lady Gaga的一条消息就会出现在她3千一百万的粉丝面前。

一些看点:

1. 推特不再想要仅仅成为一个网站应用。它希望成为一套全球移动客户端使用的API,成为这个星球上最大的实时消息系统之一。

2. 推特主要是消费机制,而不是生产机制。每秒仅有6千的写请求,但是会有30万关于时间轴的读请求。

3. 那些拥有巨量粉丝的名人们越来越多。这样的人发一条消息,需要被扩散到所有他的粉丝面前,速度可能因此变慢。推特试图把它控制在5秒以内。但这并不是总能成功,尤其是当越来越多的名人之间互相转发消息时。一个后果就是可能回复比原本的消息更早被收到。推特正在试图针对高价值用户做出一些改变,将一部分的工作从写请求转到读请求时候来做。

4. 你自己的时间轴主页存在一个Redis集群中,最多可以有800条记录。

5. 推特可以从你是谁的粉丝以及你点击什么连接中知道很多东西。当双方没有互粉时,可以根据隐性社会契约推测出很多事情。

6. 虽然用户很关心推特上的消息,但是其实这些消息的内容跟推特的基础架构几乎没有什么关系。

7. 一个如此复杂的堆栈需要一个非常复杂的监控和调试系统来跟踪性能问题的根源。而且现在已经不合时的旧决策常常会继续影响这个系统。

那么推特到底怎么工作的?你可以从下面这个Raffi的精彩演说的大纲中了解一下。

挑战:

1. 在1.5亿活用用户和30万关于时间轴(包括主页和搜索)的QPS面前,简单的实现会很慢。

2. 事实上推特曾经试过由大量SELECT语句组成的简单实现,但是行不通。

3. 解决方案是一个以写为基础的扩散过程。当消息到达时通过很多步骤决定消息应该被存在哪里。这样读的时候就会又简单又迅捷,不需要任何计算。这样的做法导致写的速度慢于读的速度,写性能大概在4千QPS。

组织结构:

1. 平台服务组负责推特的核心可扩展基础设施:

  a. 他们负责时间轴服务、消息服务、用户服务、社交图服务、以及所有支撑推特平台的服务。

  b. 内部客户和外部客户用的API几乎是一样的。

  c. 有超过1百万应用注册使用推特的第三方API

  d. 保障产品组不需要为产品规模太大而烦恼

  e. 容量规划,设计可扩展的后台系统,在网站朝着计划外的方向发展时及时更换基础设施。

2. 推特有一个架构组,负责推特的整体架构,维护一个技术负债书(上面是他们想要去除的技术)

推送?提取?

1. 人们每时每刻都在推特上记录内容。推特的职责就是找到一个办法如何把这些内容同步发送出去,发送到你的粉丝面前。

2. 真正的挑战来自实时性。目标是要把一条消息在不超过5秒内送达一个用户。

  a. 送达意味着以最快的速度收集内容,把它送到互联网上,然后再把它从互联网上下载下来。

  b. 送达意味着把iOS,黑莓以及安卓的提醒、邮件还有短信都送进运行于内存中的时间轴集群里。

  c. 推特平均每个活跃用户发送短信的数量是这个世界上最高的。

  d. 美国的选举是产生最多互动内容的话题之一。

3. 两种主要时间轴:用户时间轴和主页时间轴

  a. 一个用户时间轴是一个用户发送的所有消息。

  b. 一个主页时间轴是一个临时的将所有你关注的人的用户时间轴合并所形成的东西。

  c. 这里运用了一些业务逻辑。例如,你不关注的人的回复会被屏蔽。一个用户重复发送的消息也会被过滤掉。

  d. 在推特这样的规模下做到这些事情很有挑战。

4. 以提取消息为基础:

  a. 特定时间轴。例如twitter.com和home_timeline API。被发送给你的消息一定是因为你请求接收到他们。提取消息为基础的送达:你通过一个REST API请求向推特申请获得这些数据。

  b. 查询时间轴。 搜索API。查询资料库。尽快的返回所有符合一个特定查询的所有消息。

5. 以推送消息为基础:

  a. 推特运行着一个最大的实时事件系统,可以按每秒22MB的速度通过Firehose推送消息:

    - 打开一个连接推特的通信管道,他们可以把所有的公共消息在150毫秒内发送给你。

    - 在任何时刻,大约有1百万个这样的通信管道与推送集群相连。

    - 使用类似搜索引擎的friehose客户端。所有公共消息都从这些通信管道中发出去。

    - 这不是真的。(因为你无法接受这样的事实。)

  b. 用户信息流连接。TweetDeck和Mac版推特就用这种方式驱动。当你登陆时,他们会查询你的社交图,然后只把你关注的人的消息发送给你,重新创建主页时间轴。与主动提取消息不同是,你会通过一个持久的连接来获得相同的时间轴。

  c. 查询API。发送一个对所有消息的查询。当有新产生的消息符合这个查询时,它们就会通过为这个查询注册的通信管道被发送出去。

以提取消息为基础的时间轴的总体表现:

1. 消息通过一个写API进入推特。它会通过一些负载均衡器以及一个TFE(推特前端终端)和一些他们内部的服务。

2. 这是一个很直接的路径。完全预先算好的主页时间轴。所有的业务逻辑都在消息进来的时候执行。

3. 扩展过程随后发生。消息进来之后被放在一个Redis集群中。一条信息会在三台不同的机器上备份三次。在推特这样的规模下,一天会有很多机器出故障。

4. 扩展过程会查询寄去Flock的社交图服务。Flock维护粉丝以及关注人名单。

  a. Flock 返回一个接收者的社交图,然后对Redis集群中存储的所有时间轴重复这一步骤。

  b. Redis集群有一些TB级的RAM。

  c. 可以同时对4千个目的地进行查询。

  d. 使用了Redis原生的list结构。

  e. 我们假设你有2万的粉丝,然后你发了一条消息。那扩展程序将会在Redis集群中查询所有那2万个用户的地址,然后把这条消息的ID插到那些用户的列表中。所以每写一个消息Redis集群就会执行2万个插入操作。

  f. 他们会插入消息的ID,产生消息的用户ID,以及4个字符用来表示这是一个转发、回复、或者别的东西。

  g. 你的主页时间轴也在Redis集群中,有800条记录。如果你足够耐心地一直往后翻页,你就会发现这个限制。RAM是影响时间轴能存多少记录的主要限制。

  h. 每一个活跃用户都存在RAM中来降低延时。

  i. 活跃用户指的是那些在30天内登录过推特的人。这个天数可能会因为推特网站的繁忙程度或者缓存的大小而改变。

  j. 如果你不是活跃用户,那你发的消息并不进入缓存中。

  k. 只有你的主页时间轴会需要读写硬盘。

  l. 如果你的资料被挤出Redis集群了,那你重新进入Redis集群时会需要走一个叫重建的流程:

    - 查询社交图服务,找到你关注谁,从硬盘上吧所有这些人都导入Redis中。

    - 他们用Gizzard来管理基于MySQL的硬盘存储,并用它来掩盖SQL交易并提供全局复制。

  m. 通过在一个数据中心内复制三次,即使有一个机器出了故障,他们也不需要重新生成那台机器上的所有时间轴。

  n. 如果一条消息是转发,那么会存一个指向原消息的指针。

5. 当你查询你的主页时间轴的时候,你用到了时间轴服务。时间轴服务只需要找到一台记录了你的主页时间轴的机器。

  a. 运行三个不同的哈希环,因为你的时间轴存在三个不同的地方。

  b. 他们找到第一个他们能最快到达的机器,然后尽快返回。

  c. 代价就是扩散会需要久一点,但是读取操作很快。大约只要2秒就可以从一个冷缓存到达浏览器。从一个API请求只需要400毫秒。

6. 由于时间轴只包含消息ID,他们还需要复原这些消息,找到对应的内容。给定一串ID,他们可以做一次多项获取,并行地从T-bird中获取消息内容。

7. Gizmoduck是用户服务。Tweetypie是消息对象服务。每个服务都有自己的缓存。用户缓存是一个memcache集群,存着所有用户数据。Tweetypie在自己的memcache集群中存有大约一半上个月产生的消息。这些消息是给内部客户用的。

8. 有时在出口处会做一些读取时的过滤。比如在法国过滤纳粹相关的内容,这种内容在送出去时会被删除掉。

搜索的总体表现:

1. 不同于提取消息。所有的计算都在读的时候执行,所以写操作很简单。

2. 当一条消息到达的时候,Ingester把消息分割,然后找到所有他们想要用来索引的关键字,然后把这条消息扔到一个Early Bird机器上。Early Bird是一个改良版的Lucene。索引被存在RAM中。

3. 在扩散时,一条消息可能存在N个人的主页时间轴中,取决于多少个人关注你。但是在Early Bird中,一条消息就被存在一台机器上(除了复制)。

4. Blender创建搜索时间轴。它需要在整个数据中心内分散收集数据。它查询每一个Early Bird,看他们是否有符合这条查询的内容。如果你查询“New York Times”,所有的机器都会被查询,结果会被返回、排序、合并和重新打分。重新打分基于社交因素,例如转发、点赞和回复的数量。

5. 有一个活动时间轴,它的活动信息是基于写操作来计算的。当你点赞和回复消息时,他们会更新你的活动时间轴。类似于主页时间轴,它是一串活动事件的ID,比如点赞ID,回复ID等。

6. 这些都会输入Blender,在读操作时用来重新计算、合并以及排序,然后返回一个你看到的搜索时间轴。

7. 发现是一个基于推特对你的了解而制定的搜索。他们对你的了解来源于你关注的很多人、对连接的点击等,这些信息都被用在发现搜索中。它还会基于这些信息给结果重新打分。

搜索和提取消息正好相反:

1. 搜索和提取消息看起来非常相似,但是他们有一个特征与对方相反

2. 主页时间轴:

  a. 写操作:当一个消息到达时,需要O(n)个步骤写入Redis集群,n是你的粉丝数量。对于Lady Gaga和Barack Obama这样的用户来说意味着千万级的插入操作。所有Redis集群都有硬盘备份,flock集群会把用户时间轴也存在硬盘上。但是总的来说,时间轴会在Redis集群的RAM中被找到。

  b. 读操作:通过API或者网页,只需要O(1)个步骤就可以找到正确的Redis机器。推特优化了主页时间轴的读取路径,使其十分方便。读取操作只需要10多个毫秒就可完成。推特主要是一个消费机制,而不是生产机制:每秒30万的读请求对应的是每秒6千的写请求。

3. 搜索时间轴:

  a. 写操作:当一个消息到达ingester时,只有一个Early Bird机器会响应。写操作是O(1)的。一个消息在5秒内可以完成排队等待以及写入对应的Early Bird机器。

  b. 读操作:当一个读操作到来时,需要在整个集群内做O(n)次的读取。大多数人不使用搜索,所以可以先快速把消息存储起来等待搜索。但是在读的时候就会付出代价。读操作需要数百毫秒。搜索不会从硬盘上读取内容。整个Lucene索引都在RAM中,所以分散搜集式的读取还是很有效率的, 它们不用从硬盘上读取。

4. 消息的内容几乎与大多数的基础设施没有任何关系。T-bird存储了所有消息的内容。一条消息的大部分内容存在RAM中。如果不是,那就去询问T-bird然后做一个select查询就可以把它们重新取出。消息的具体内容是什么除了在搜索、潮流、或者实时事件管道外几乎没什么影响。主页时间轴一点也不关心具体内容。

未来:

1. 如何使现在信息管道更快更有效率?

2. 扩散会很慢。试图把它控制在5秒以内,但是不是每次都能成功。总的来说很难,尤其是当名人发消息的时候。这种情况也越来越常见。

3. 推特的关注图是一个不对称图。消息只会发给一个特定时间点上关注的人们。推特可以通过你关注Lance Armstrong但是他不关注你知道很多东西。当双向关注不存在时,可以从这一隐性社会契约中推出很多东西。

4. 大基数的社交图是一个挑战。@ladygaga有3千1百万粉丝。@katyperry有2千8百万粉丝。@justinbieber有2千8百万粉丝。@barackobama有2千3百万粉丝。

5. 当上述这些人发消息的时候,数据中心内要写多得多的消息。当他们互动时(这个经常发生),这个挑战就更难了。

6. 这些高扩散用户是推特最大的挑战。名人发的消息经常比它们的回复更晚被看见。它们引起了竞争条件。如果一条Lady Gaga发的消息需要数分钟才能扩散到所有她的粉丝中去,那人们就会在不同的时间点看到她的消息。一些最近在关注她的人可能比很久之前关注她的人可以早5分钟看到这条消息。我们假设有个人早早地收到了这条消息然后回复了,如果原消息还在扩散过程中,那这条回复就会插在原消息之前被扩散,先被那些还没收到原消息的人收到。人们就会很疑惑。推特按照ID排序,因为他们的用户几乎总是单调增加的,但是这并不能解决这么大规模的问题。高价值用户的扩散队列总是满的。

7. 试图找出一个方法合并读和写路径。不在扩散高价值用户。像Taylor Swift这样的人并不在乎扩散了,反倒是可以在读的时候再合并她的时间轴。这样就可以平衡读和写的路径,节省10%以上的计算资源。

解耦:

1. 消息被复制传递了很多次,大多数是在互相独立的组之间。搜索、推送、兴趣邮件以及主要时间轴等组可以相互独立的工作。

2. 系统因为性能原因解耦。推特以前是完全同步的,但是两年前因为性能原因而被停止了。消化一条消息到API中需要145毫秒,然后所有客户端都会被断开连接。这是由于一些历史原因引起的。写路径是基于Ruby和MRI,一个单线程的服务器,所以每当一个Unicorn工作程序被新分配时,处理能力就会减少一些。他们希望尽快释放一个客户端的连接。一条消息进来,Ruby处理它,把它塞进一个队列,然后断开连接。他们一个机器只能跑45-48个程序,因而一台机器只能同时处理那么多的消息,所以他们希望可以尽快释放连接。

3. 现在消息被传入一个异步的通道,然后所有我们之前讲过的服务会从中提取消息。

监控:

1. 办公室有很多显示器实时显示系统运行状态。

2. 如果你有1百万的粉丝,只需要几秒就可以让他们都收到你的消息。

3. 推特输入端数据: 40亿条消息一天;平均每秒5千条;高峰时每秒7千条;有大型活动时超过每秒1万2千条。

4. 时间轴送达数据: 每天300亿次送达(大约2千1百万次每分钟);p50的送达1百万粉丝所需时间为3.5秒;每秒30万次送达;p99会达到5分钟

5. 一个叫做VIZ的系统监控每一个集群。时间轴服务从Scala集群中取出数据的请求时间的中值是5毫秒。p99是100毫秒。p99.9是数百毫秒,这是因为他们需要从硬盘中读取了。

6. Zipkin基于谷歌Dapper系统。他们可以用它来跟踪一条请求,看这条请求经过了哪些服务,分别花了多久,然后他们就可以有一个很细致的针对每条请求的性能报告。你可以深入挖掘每一条请求,并理解所有不同的时间花在了哪里。有很多时间都是用来debug这个系统,看一个请求所用的时间都花在了哪里。他们也可以根据不同的阶段汇总数据,然后观察一个扩散或者送达需要多久。他们整整花了两年把活跃用户时间轴的获取时间降低到2毫秒。很多时候都是与停止GC、memcache查询做斗争,或是理解数据中心的拓扑、或是真的架设集群来获得这样的成功。

  

时间: 2024-12-26 13:18:23

通向高可扩展性之路(推特篇) ---- 一个推特用来支撑1亿5千万活跃用户、30万QPS、22MB每秒Firehose、以及5秒内推送信息的架构的相关文章

通向高可扩展性之路(谷歌篇)

原文链接:http://highscalability.com/google-architecture 原文写于2008年11月22日,以下是译文: 平台: 1. Linux 2. 很多种语言: Python, Java, C++ 平台上有什么? 一些数据: 1. 2006年时大约有450,000 台廉价商用服务器 2. 2005年的时候谷歌已经索引了80亿网页.现在大概没人知道他们以及索引了多少网页了. 3. 现在谷歌有超过200个GFS集群.一个集群可以有1000甚至5000台机器.成千上万

支撑5亿用户、1.5亿活跃用户的Twitter最新架构详解及相关实现

如果你对项目管理.系统架构有兴趣,请加微信订阅号"softjg",加入这个PM.架构师的大家庭 摘要:Twitter出道之初只是个奋斗在RoR上的小站点,而如今已拥有1.5亿的活跃用户,系统日传输tweet更多达4亿条,并已完成了以服务为核心的系统架构蜕变. Twitter如今在世界范围内已拥有1.5亿的活跃用户,为了给用户生成timeline(时间轴)需支撑30万QPS,其firehose每秒同样生成22MB数据.整个系统每天传输tweet 4亿条,并且只需要5分钟就可以让一条twe

蚂蚁金服财富技术部,诚招Java研发工程师。校招内推!!!

蚂蚁金服财富技术部,诚招Java研发工程师. 团队是蚂蚁金服财富技术部核心团队,支持亿级互联网交易清算,在这里不仅能学习到先进的互联网技术,也能了解许多终身受益的金融知识. 内推对象 2020届毕业生(毕业时间为2020.1.1-2020.12.31) 杭州.上海.北京 三地可选 内推方式: 1.微信打开链接,自助内推 蚂蚁金服财富BU内推链接:https://alibaba.tupu360.com/campusActivity/getActivityInfo?activityCode=62hV

Python之路【第九篇】:Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy

Python之路[第九篇]:Python操作 RabbitMQ.Redis.Memcache.SQLAlchemy Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度.Memcached基于一个存储键/值对的hashmap.其守护进程(daemon )是用C写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信. Memc

ORACLE数据高可用之路

这篇是计算机中Oracle类的优质预售推荐>>>><ORACLE数据高可用之路> 编辑推荐 本书特别适合Oracle中高级系统管理人员.应用开发人员阅读,同时对关心服务器技术.关注数据高可用性与数据安全的企业技术人员.相关IT专业的高校教师和研究生也有重要的参考价值. 内容简介 现代数据服务面临的两大问题是数据保障和不间断服务,即数据服务的高可用性(High Availability).本书论述Oracle在此方面的两类解决方案:数据卫士(Data Guard)和数据集

基于SID的高可扩展性数据模型

前言 此文根据TMF SID规范撰写,欢迎大家提出建议和意见. TMF文档版权信息 Copyright © TeleManagement Forum 2013. All Rights Reserved. This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist i

Python之路【第二篇】:Python基础(一)

Python之路[第二篇]:Python基础(一) 入门知识拾遗 一.作用域 对于变量的作用域,执行声明并在内存中存在,该变量就可以在下面的代码中使用. 1 2 3 if 1==1:     name = 'wupeiqi' print  name 下面的结论对吗? 外层变量,可以被内层变量使用 内层变量,无法被外层变量使用 二.三元运算 1 result = 值1 if 条件 else 值2 如果条件为真:result = 值1如果条件为假:result = 值2 三.进制 二进制,01 八进

JAVA必备——EJB,通向大型软件的路!

从接触java开始,就对java的标准,神交已久,今天先给大家简单介绍下,什么事ejb,然后咱们一起完成一个小例子,完成对ejb的熟悉过程,在这其间一起体会ejb带给我们的编码变化! 简介(来自百度): EJB是sun的服务器端组件模型,设计目标与核心应用是部署分布式应用程序.凭借java跨平台的优势,用EJB技术部署的分布式系统可以不限于特定的平台.EJB (Enterprise JavaBean)是J2EE的一部分,定义了一个用于开发基于组件的企业多重应用程序的标准.其特点包括网络服务支持和

Python之路【第九篇】:Python基础(26)——socket server

socketserver Python之路[第九篇]:Python基础(25)socket模块是单进程的,只能接受一个客户端的连接和请求,只有当该客户端断开的之后才能再接受来自其他客户端的连接和请求.当然我 们也可以通过python的多线程等模块自己写一个可以同时接收多个客户端连接和请求的socket.但是这完全没有必要,因为python标准库已经为 我们内置了一个多线程的socket模块socketserver,我们直接调用就可以了,完全没有必要重复造轮子. 我们只需简单改造一下之前的sock