Quora的技术探索
关于问答类的应用,最早接触的是stackoverflow和知乎 ,而Quora作为知乎的原型,因为其创始人来自FaceBook而吸引了我。事实上关于Quora的技术分析,冯大辉和陈皓都已经有所详细的阐述:《Quora 用了哪些技术 ?》《Quora使用到的技术》。通过他们的文章,我看到了一篇更详细的说明《Quora’s Technology Examined》。看完以后感觉有很多东西值得深入的去学习和整理。于是决定将这篇文章先翻译出来,作为后面web学习的引子吧。下面开始吧:
Quora因为其流畅的系统已经给IT创业界掀起了一场风暴。Quora为什么这么给力呢,除了有大量聪明的提问者和回答者的支持外,更因为其来自FaceBook的联合创始人精心设计的后端系统;
所有的聪明人都在使用这个智能的工具,这一点不足为奇。但是有很多人都在思考,Quora为什么能运行的如此流畅呢。NoSQL 的研究者们挠着头问道“为什么 Quora 使用 MySQL作为其数据存储,而不是像 Cassandra, MongoDB, CouchDB等这些NOSQL工具?“.
[Adam D'Angelo(Quora创始人)的回答:]
1、如果你在应用层进行分区,MySQL的可扩展性不是问题。FaceBook报道说在2008年的时候运行的MySQL服务器有1800台,而只有2个DBA。你不能跨分区做连接,但是NoSQL数据库无论如何也做不到。Facebook 还未确认使用Cassandra 作为其住数据源,而是可能仅仅用于收件箱搜索业务。
2、这些分布式数据库像Cassandra,MongoDB,CouchDB实际上还不是很稳定和可扩展。Twitter 显然一年前就试图从MySQL 迁移到Cassandra 。如果有人报道说使用这些系统中的一个作为主存储库超过1000太机子,超过1年,我就会重新考虑我的选择。
<< 2011.8更新: 当我写了这些后,foursquare 报道了一次11个小时的因为MongoDB引起的宕机事件。另外,一个创业的朋友,其用户量正处于爆炸式增长期,想转到MongoDB ,但因为其不稳定,一个月后放弃了。Twitter 也放弃了往Cassandra 迁移。Facebook 正在脱离Cassandra,HBase还不错,不过如果你没有相应深入理解人员,仍然是一种冒险 >>
3、应用的在线数据存储部分是最不应该冒险尝试新技术的地方。一旦你丢失了你的数据库或出现了故障,这将是一次无法恢复的灾难。If you're not the developer of one of these new databases, and you're one of a very small number of companies using them at scale in production, you're at the mercy of the developer to fix bugs and handle scalability issues as they come up.
4、其实你可以用单个MySQL数据库走得更远,而不用担心在应用层分区。你可以纵向扩展你的机器,通过增加内核和内存。如果你在数据库前有一套缓存系统(则更容易进行扩展),你只要考虑写入就够了。你可以使用S3或一些其他的分布式哈希表存储那些最大的对象。你不必承担未来10倍系统规模需求,只要你有信心在规模增长的时候可以进行扩展。
5、很多对大量MySQL机器进行人工分区的问题可以通过构建一层位于应用层与MySQL之间的数据访问层(实现数据库负载的自动分散)就可以大大缓解。FriendFeed 就是一个实现这一技术的很好的例子。
6、Personally, I believe the relational data model is the "right" way to structure most of the data for an application like Quora (and for most user-generated content sites). Schemas allow the data to persist in a typed manner across lots of new versions of the application as it's developed, they serve as documentation, and prevent a lot of bugs. And SQL lets you move the computation to the data as necessary rather than having to fetch a ton of data and post-process it in the application everywhere. I think the "NoSQL" fad will end when someone finally implements a distributed relational database with relaxed semantics.
在这篇博文中,我将根据这些可用的关于Quora 的信息片断,从技术的角度深入研究Quora 。他们究竟做了哪些技术决策?他们的技术架构究竟是怎么样的?他们使用了哪些框架和语言?他们是如何做到使查询这么快捷?
Quora的组成部分
Quora大体有如下及部分功能…
- 你可以提问题
- 你可以解答问题 (如果你想匿名也可以)
- 你可以对已回答的问题进行评论
- 你可以顶或者踩这些问题的回答
- 问题可以被归类为各个主题
- 你可以写一个 post(a informative statement, rather like a orphaned answer or blog post)
- 你可以follow问题,主题或其他用户
- 在顶部有一个反应超快的能自动完成(录入内容)的搜索框,可以加倍问题录入速度
最后一点,超快自完成搜索框是Quora的一个典型特征。当你开始输入一个问题的时候,你可以立即看到是否有其他人已经问过这个问题或已经存在这个主题或post了。
引擎盖下面究竟有些什么?
搜索框
只有问题,主题标签,用户名字或post标题被索引并提供搜索。并不支持全文索引,因此无法搜索问题内容和答案。被索引的文本都做了标记化,所以即时单词顺序不同,依然可以匹配。通过前缀匹配技术,在输入完整个词之前就可以看到最匹配的结果。例如,敲“mi”可以立即看到“Microsoft”。
其搜索还会有一些非常简单的模糊匹配的算法,因为“nears”可以匹配“near”,但是“pony”与“ponies”无法匹配。“主题别名”允许相似的主题名进行匹配,如“startup”和“start-up”。当然,这些“主题别名”需要人工事先输入,否则无法匹配。
另外,如果有重复的问题,其中一个问题会自动跳转到另一个问题(Quora的一个亮点),但是在搜索中还是会出现。由于没有n-gram索引算法,轻微的拼写错误将导致不匹配,例如:搜索“google”的时候如果输入“gooogle”,将无法找到。
原先他们使用了一个开源的搜索服务器,叫Sphinx。其支持上述的那些功能。现在他们因为受实时性方面一些限制放弃了。新的解决方案是他们自己开发的,在前缀索引和匹配控制方面得到了提升,这个算法由Python实现。
[Adam D'Angelo(Quora创始人)的回答(Nov 13, 2010):]
我们的搜索引擎是自己写的。除了Thrift和Python’s unicode library(解决unicode问题),没有用到其他库.
快捷的查询
就像我刚才提到过的,search-box得快吗?我做了一些测试发现其响应速度让我印象深刻。问题通过AJAX发送一个GET request。Responses 返回封装了HTML的JSON包。可能是因为需要做关键字匹配及高亮显示,其解析JSON是在服务端,而不是在客户端用JavaScript,因为这对JavaScript来说太过复杂了。例如:敲入“categories” ,可以使关键字“category”高亮显示;
我通过Linode的机器每50毫秒进行一次查询,并观察其响应。Quora 并没有对你发送的requests进行“节流”。在浏览器上,我发现敲入“Microsoft”9个字符会向Quora的搜索服务器发送9次请求,而与你的敲入速度几乎没有关系。正像你后面会看到的,服务端会进行控制,所以如果后端过负载的时候,它可以减少更新结果的频度,而这一切都无需修改前端的JavaScript。
Quora使用长连接,当你开始敲入查询内容的那刻就会建立一个HTTP连接。这个连接一致保持着,并用于进一步的http请求。如果60秒没有使用,这个连接才会关闭。当再次输入的时候,新的连接又会建立。
为了模拟在搜索栏中输入一个单次,我发送了下面的请求,例如“butler”是6个request(“b”, “bu”, “but” … “butler”).
"butler" (6 chars) duration: 0.393 secs 0.065 secs per query "butler monkeys" (14 chars) duration: 0.672 secs 0.048 secs per query "fasdisajfosdffsa" (16 chars) duration: 0.746 secs 0.046 secs per query
最后一个查询是用来测试:如果在缓存中没有的内容是否会使整个搜索变慢的。我发现没有变慢。这意味着:一种情况是这时他们没有使用缓存,缓存仅仅是用来减轻后端搜索引擎的负载的。还有一种情况是他们使用了一种智能的处理(例如:如果没有“fasd”的匹配结果,就不对“fasdi”做匹配处理了,直接中止)
Quora打算实现全文检索吗?
[Adam D'Angelo(Quora创始人)的回答(Sep 1, 2010):] 最终会的,我还没有实现是因为我们优先处理其他方面的事情,但是未来我们肯定会实现的。
Webnode2 And LiveNode
Webnode2 和LiveNode 是Quora的内部系统,用来生成和管理内容。Webnode2 生成HTML,CSS和JavaScript ,它和LiveNode紧密结合。LiveNode是负责管理web页的内容显示。Charlie Cheever说如果他可以从新开始,他 第一件事要做的就是重写整个LiveNode.
Quora的工程师看上去对他们搞的这些东西非常的满意,并且 他们也在努力地找到这些东西的弱点。有一个有意思的关于LiveNode的问题是,如果A和B同时正在看相当的一个问题,那么用户A的一些交互动作会影响B的页面。例如,如果A顶了一下某个答案,那么这个答案可能会往上移动。这样的一个显示变化会通过AJAX更新B的浏览器。如果B此时展开了评论,可能会受到影响。
LiveNode 由这些东西写成:Python, C++, and JavaScript. jQuery ,Cython也用到了。
因为Quora 想要对他们的LiveNode开源并准备把他们的代码分开,做这个事可能需要太多的工作和时间。
Charlie Cheever 指出 WebNode2 和 有一个叫做 “free and easy website builder” 的 Webnode 的 webnode.com没有任何的关系。
Amazon Web Services
Quora的主机都在Amazon EC2 和S3 上。当然从长期来说这不如运行自己的服务器来得划算,但是对像Quora这样的快速成长的公司来说简直是完美的设计。
Ubuntu Linux
Quora 使用Ubuntu Linux 作为他们的操作系统选择。没有什么值得惊讶的,因为Ubuntu Linux在Amazon EC2上的管理和部署很容易。Adam D’Angelo指出他在高中和大学的时候都使用Debian Linux ,为什么换成ubuntu呢?看Adam D’Angelo怎么说的:“it works and there hasn’t been a compelling reason to switch”.
静态内容
你只要看一下Quora 任何一个页面的HTML代码,就会看到他们正在使用Amazon的分布式内容分发网络,Cloudfront。URLS是这样的:
http://d2o7bfz2il9cb7.cloudfront.net/main-thumb-670336-25-7kmigSSkkdusoE6gHRkdQsXfjuTCaxQs.jpeg
CloudFront 用于所有静态的图片、CSS和JavaScript (除了Google的Analytics JavaScript,是在Google的主机上的)。图片上传到EC2的机器上,然后调整尺寸后上传到S3。这是通过Python S3 API进行管理的。
HAProxy 负载均衡
Quora在第一线使用了HAProxy,作为负载均衡器,而后面是Nginx。
Nginx
Nginx作为反向代理服务器,部署在在负载均衡器后面。如果想知道更多关于这一方面的信息,我推荐读:“Using Nginx As Reverse-Proxy Server On High-Loaded Sites“.
Pylons And Paste
Pylons, a lightweight web framework, is used as their main web-server behind Nginx. They use the default Pylon + Paste stack.
Pylons, 一个轻量级的web框架作为他们的主要web服务器,部署Nginx后面. 他们使用默认的 Pylon + Paste stack.
选择Pylons,就像你在万圣节选择一个南瓜,他们去除了其内部的templates 和ORM,然后加入了自己的技术,用Python写的。看这里LiveNode and webnode2 reside.
MochiMedia(一个在线游戏网站)也使用了 Pylons.
Python
因为Charlie 和Adam来自FaceBook, PHP应该说是一个很好的选择。然而正如Adam 说的。“Facebook is stuck on that for legacy reasons, not because it is the best choice right now“。从这些经验来看,他们知道技术选型,尤其是编程语言对公司长期发展非常重要。他们也考虑过C#,Java,和Scala。选择C#则不仅仅是语言了。那需要他们引入微软的一大堆东西。Python胜过Java的原因是它比java更富有表现力,也更容易快速写出代码。Scala则太年轻。Adam 提到速度和缺乏类型检查是Python的弱点,但是他们都已经知道这个语言相当不错。针对性能要求高的后端组件他们使用C++来写,因为Python的速度不够。他们觉得Ruby和Python很接近,但是他们有Pyhton 的经验,对Ruby缺乏经验,因此Python胜出了。他们使用的是Pyhton 2.6。
使用Python的另一个好处是Python的数据结构和JSON可以很好的映射起来,代码易读性很高。而且有很多的库,调试器和重载器。Quora浏览器和服务器之间的通信主要使用JSON格式,因此这也成为一个重要因素。
没有使用IDE,大部分开发者使用Emacs文本编辑器。显然这是一个个人选择,随着团队的壮大,也许会改变。
另外,他们提到了PyPy,一个让 Python更快更灵活的项目。
Thrift
Thriftis used for communications between backend systems. The Thrift service is written in C++.
Thrift 用于后端服务器间的通讯。Thrift 服务由 C++开发。Facebook同样使用了这个技术。
[Adam D'Angelo(Quora创始人)的回答(Sep 4, 2010):]
主要是如果你想将请求间的数据保存在内存中,并使你的Pyhton代码无状态。需要写一个Python封包,封装一个C库,涉及一些基于引用计数的内存管理。这需要清楚Python内核的人,但是写一个thrift 接口则是非常的简单。同时通过这种方法,你也隔离了异常,不至于使Python代码崩溃。
Tornado
Tornadoweb框架用于实时更新。这是他们的Comet服务器,用于处理大量的需要长时间poll和push更新的网络连接。
Long Polling (Comet)
Quora显示的不仅仅是静态页面。当其他人提交了像提问、回答、评论这些新的内容时,每个页面都要更新。就像Adam D’Angelo 说的,当前处理这种情况最好的技术就是“long polling”。和“polling”不同,传统的“polling”,浏览器需要重复的发送请求到服务器:“有更新的吗?”,然后服务端回答:“没有”。几秒钟后,浏览器又来问:“那现在呢?”“没有”,“那现在呢?”“还是没有!”。这种模式将浏览器作为驱动方。这是弊端,因为浏览器并不知道需要等待多久再去轮询。如果浏览器轮询过于频繁,将过度增加服务端的负载。如果轮询频度不够,当有新的内容时,服务端只能在那里等待客户端来轮询,用户将无法及时看到更新内容。
“Long polling”,也叫“Comet”,这种模式将服务端作为控制端,客户端等待响应。客户端和服务端的会话是相同的,不同的是原来客户端在做下一次轮询前只能等待,现在是服务端等着做出响应。服务端在等待是否有更新到来时,在很长一段时间(如:60秒)内保持连接。当更新到来时,服务可以立即做出响应。当客户端接收到更新响应的时候,可以立即发起一个新的请求。服务端再一次延迟响应,直到有更新返回或时间到期没有响应。
long-polling的优点是减少了前后端交互的次数。让服务器端来控制时间,所以,内容更新可能会只是几个毫秒。这也使其用于处理聊天应用或者那些真正需要实时更新的应用。
但是,这种模式的不足之处是——这会让服务器端出现大量的TCP链接,想一想,Quora也快是百万级用户的应用了,只需要10%的在线用户,你就需要一个可以处理10万并发量的架构。注意,如果一个用户在其浏览器里打开了多个Quora网页的话,那么,这个链接器会是非常致命的。
当然,好的消息是已经有一些技术专门为Long Polling设计,这些技术可以让你在那些等待的连接中只会消耗非常非常少的内存(因为那些等待连接并不需要所有的资源)。例如:Nginx 是一个单线程的事件驱动的小型服务器,每一个链接只花非常小的内存。每一个Nginx的进程只会在一个时候处理一个连接。这意味着其很容易扩展成一个可以处理成千上的并发量的服务架构。
Adam D’Angelo, Quora (Sep 29, 2010)
There is no reliable way to do this without having the client polling the server. However, you can make the server stall its responses (50 seconds is a safe bet) and then complete them when a message is ready for the client. This is called “long polling” and it’s how Quora, Gmail, Meebo, etc all handle the problem.
If you have a specialized server that uses epoll or kqueue, you should be able to hold on the order of 100k users per server (depending on how many messages are going). This is called the “c10k” problem. http://www.kegel.com/c10k.html
MySQL
就像Adam D’Angelo 的老雇主FaceBook一样,Quora 重度使用了MySQL。在Quora上的一个问题的回答中“When Adam D’Angelo says “partition your data at the application level”, what exactly does he mean?“, D’Angelo深入的讲述了在分布式存储的情况下如何使用MySQL(或者说关系型数据库)
最基本的建议是按需对数据进行分区,如果可能尽量把数据存储在一台机器上,并且使用一个带主键的hash表对横跨多个数据库的大数据集进行分区。必须避免表连接。对此,他认为FriendFeed的架构是一个很好的例子。FriendFeed的架构在Bret Taylor 的文章“How FriendFeed uses MySQL to store schema-less data“中有所阐述。D’Angelo 也说道,你不应该在一个社交网站中使用NOSQL 数据库,除非你有上百万的用户。
不仅仅Quora和FriendFeed 侧重使用MySQL,是否听说过“Google”?真的很难想像,在the words of Google上是这么说的:“Google使用MySQL在一些和搜索无关的应用上”。Google已经为MySQL发布了与复制,同步,监视和提升速度等方面相关的补丁。
Adam D’Angelo, Quora (Oct 10, 2010)
One option is to simulate some load. Write a script that mimics the kinds of queries your application will be doing, and make sure it can handle the amount of load you want it to be ready for (especially as the size of the dataset changes).
Memcached
Memcached用于MySQL的缓存层.
Git
Git用作版本控制.
JavaScript Placement
如果你看过Quora的页面代码,你会看到JavaScript 代码都在页面的底部。Charlie Cheever建议这会让你的页面显得载入得很快,因为其先显示内容,然后在载入Javascript。
Charlie Cheever 遵循 “让web站点最快的14条原则”
Steve Souders, 《高性能网站建设指南》一书的作者列出了以下原则: rules for making websites faster. Quora的联合创始人 Charlie Cheever也提到过, 想必这也是Quora速度快的一个原因吧。
“One resource we used as a guide is Steve Souders’ list of rules for high performance websites:http://stevesouders.com/hpws/rules.php” – Charlie Cheever, Quora
Steve Souders’ 14 rules are…
1、Make Fewer HTTP Requests
2、Use a Content Delivery Network
3、Add an Expires Header
4、Gzip Components
5、Put Stylesheets at the Top
6、Put Scripts at the Bottom
7、Avoid CSS Expressions
8、Make JavaScript and CSS External
9、Reduce DNS Lookups Minify JavaScript
10、Avoid Redirects
11、Remove Duplicate Scripts
12、Configure ETags
13、Make AJAX Cacheable
结论
Quora是一个现代科技创业的伟大例子。他们的团队非常小,但是对他们使用的技术理解非常到位。他们对已经选择的技术做了深思熟虑,同时对于哪些组件选择既有产品还是自己重新开发有一个非常好的洞察力。他们似乎渴望将他们内部使用的技术分享到开源社区,对此我将持续关注。