速来报名!AICon北京站鸿蒙专场~ 了解详情
写点什么

从 0 到 100——知乎架构变迁史

  • 2014-12-31
  • 本文字数:3153 字

    阅读完需:约 10 分钟

也许很多人还不知道,知乎在规模上是仅次于百度贴吧和豆瓣的中文互联网最大的UGC(用户生成内容) 社区。知乎创业三年来,从0 开始,到现在已经有了100 多台服务器。目前知乎的注册用户超过了1100 万,每个月有超过8000 万人使用;网站每个月的PV 超过2.2 亿,差不多每秒钟的动态请求超过2500。

ArchSummit 北京 2014 大会上,知乎联合创始人兼 CTO 李申申带来了知乎创业三年多来的首次全面技术分享(幻灯片下载)。本文系根据演讲内容整理而成。

初期架构选型

在2010 年10 月真正开始动手做知乎这个产品时,包含李申申在内,最初只有两位工程师;到2010 年12 月份上线时,工程师是四个。

知乎的主力开发语言是Python。因为Python 简单且强大,能够快速上手,开发效率高,而且社区活跃,团队成员也比较喜欢。

知乎使用的是 Tornado 框架。因为它支持异步,很适合做实时 Comet 应用,而且简单轻量,学习成本低,再就是有 FriendFeed 的成熟案例,Facebook 的社区支持。知乎的产品有个特性,就是希望跟浏览器端建立一个长连接,便于实时推送 Feed 和通知,所以 Tornado 比较合适。

最初整个团队的精力全部放在产品功能的开发上,而其他方面,基本上能节约时间、能省的都用最简单的方法来解决,当然这在后期也带来了一些问题。

最初的想法是用云主机,节省成本。知乎的第一台服务器是512MB 内存的 Linode 主机。但是网站上线后,内测受欢迎程度超出预期,很多用户反馈网站很慢。跨国网络延迟比想象的要大,特别是国内的网络不均衡,全国各地用户访问的情况都不太一样。这个问题,再加上当时要做域名备案,知乎又回到了自己买机器找机房的老路上。

买了机器、找了机房之后又遇到了新的问题,服务经常宕掉。当时服务商的机器内存总是出问题,动不动就重启。终于有一次机器宕掉起不来了,这时知乎就做了Web 和数据库的高可用创业就是这样一个情况,永远不知道明早醒来的时候会面临什么样的问题。

这是当时那个阶段的架构图,Web 和数据库都做了主从。当时的图片服务托管在又拍云上。除了主从,为了性能更好还做了读写分离。为解决同步问题,又添加了一个服务器来跑离线脚本,避免对线上服务造成响应延迟。另外,为改进内网的吞吐量延迟,还更换了设备,使整个内网的吞吐量翻了 20 倍。

在 2011 年上半年时,知乎对 Redis 已经很依赖。除了最开始的队列、搜索在用,后来像 Cache 也开始使用,单机存储成为瓶颈,所以引入了分片,同时做了一致性。

知乎团队是一个很相信工具的团队,相信工具可以提升效率。工具其实是一个过程,工具并没有所谓的最好的工具,只有最适合的工具。而且它是在整个过程中,随着整个状态的变化、环境的变化在不断发生变化的。知乎自己开发或使用过的工具包括 Profiling(函数级追踪请求,分析调优)、Werkzeug(方便调试的工具)、Puppet(配置管理)和 Shipit(一键上线或回滚)等。

日志系统

知乎最初是邀请制的,2011 年下半年,知乎上线了申请注册,没有邀请码的用户也可以通过填写一些资料申请注册知乎。用户量又上了一个台阶,这时就有了一些发广告的账户,需要扫除广告。日志系统的需求提上日程。

这个日志系统必须支持分布式收集、集中存储、实时、可订阅和简单等特性。当时调研了一些开源系统,比如 Scribe 总体不错,但是不支持订阅。Kafka 是 Scala 开发的,但是团队在 Scala 方面积累较少,Flume 也是类似,而且比较重。所以开发团队选择了自己开发一个日志系统——Kids(Kids Is Data Stream)。顾名思义,Kids 是用来汇集各种数据流的。

Kids 参考了 Scribe 的思路。Kdis 在每台服务器上可以配置成 Agent 或 Server。Agent 直接接受来自应用的消息,把消息汇集之后,可以打给下一个 Agent 或者直接打给中心 Server。订阅日志时,可以从 Server 上获取,也可以从中心节点的一些 Agent 上获取。

具体细节如下图所示:

知乎还基于 Kids 做了一个 Web 小工具(Kids Explorer),支持实时看线上日志,现在已经成为调试线上问题最主要的工具。

Kids 已经开源,放到了 Github 上。

事件驱动的架构

知乎这个产品有一个特点,最早在添加一个答案后,后续的操作其实只有更新通知、更新动态。但是随着整个功能的增加,又多出了一些更新索引、更新计数、内容审查等操作,后续操作五花八门。如果按照传统方式,维护逻辑会越来越庞大,维护性也会非常差。这种场景很适合事件驱动方式,所以开发团队对整个架构做了调整,做了事件驱动的架构。

这时首先需要的是一个消息队列,它应该可以获取到各种各样的事件,而且对一致性有很高的要求。针对这个需求,知乎开发了一个叫 Sink 的小工具。它拿到消息后,先做本地的保存、持久化,然后再把消息分发出去。如果那台机器挂掉了,重启时可以完整恢复,确保消息不会丢失。然后它通过 Miller 开发框架,把消息放到任务队列。Sink 更像是串行消息订阅服务,但任务需要并行化处理, Beanstalkd 就派上了用场,由其对任务进行全周期管理。架构如下图所示:

举例而言,如果现在有用户回答了问题,首先系统会把问题写到 MySQL 里面,把消息塞到 Sink,然后把问题返回给用户。Sink 通过 Miller 把任务发给 Beanstalkd,Worker 自己可以找到任务并处理。

最开始上线时,每秒钟有 10 个消息,然后有 70 个任务产生。现在每秒钟有 100 个事件,有 1500 个任务产生,就是通过现在的事件驱动架构支撑的。

页面渲染优化

知乎在 2013 年时每天有上百万的 PV,页面渲染其实是计算密集型的,另外因为要获取数据,所以也有 IO 密集型的特点。这时开发团队就对页面进行了组件化,还升级了数据获取机制。知乎按照整个页面组件树的结构,自上而下分层地获取数据,当上层的数据已经获取了,下层的数据就不需要再下去了,有几层基本上就有几次数据获取。

结合这个思路,知乎自己做了一套模板渲染开发框架——ZhihuNode

经历了一系列改进之后,页面的性能大幅度提升。问题页面从 500ms 减少到 150ms,Feed 页面从 1s 减少到 600ms。

面向服务的架构(SOA)

随着知乎的功能越来越庞杂,整个系统也越来越大。知乎是怎么做的服务化呢?

首先需要一个最基本的 RPC 框架,RPC 框架也经历了好几版演进。

第一版是 Wish,它是一个严格定义序列化的模型。传输层用到了 STP,这是自己写的很简单的传输协议,跑在 TCP 上。一开始用的还不错,因为一开始只写了一两个服务。但是随着服务增多,一些问题开始出现,首先是 ProtocolBuffer 会 生成一些描述代码,很冗长,放到整个库里显得很丑陋。另外严格的定义使其不便使用。这时有位工程师开发了新的 RPC 框架——Snow。它使用简单的 JSON 做数据序列化。但是松散的数据定义面对的问题是,比如说服务要去升级,要改写数据结构,很难知道有哪几个服务在使用,也很难通知它们,往往错误就发生了。于是又出了第三个 RPC 框架,写 RPC 框架的工程师,希望结合前面两个框架的特点,首先保持 Snow 简单,其次需要相对严格的序列化协议。这一版本引入了 Apache Avro。同时加入了特别的机制,在传输层和序列化协议这一层都做成了可插拔的方式,既可以用 JSON,也可以用 Avro,传输层可以用 STP,也可以用二进制协议。

再就是搭了一个服务注册发现,只需要简单的定义服务的名字就可以找到服务在哪台机器上。同时,知乎也有相应的调优的工具,基于 Zipkin 开发了自己的 Tracing 系统。

按照调用关系,知乎的服务分成了 3 层:聚合层、内容层和基础层。按属性又可以分成 3 类:数据服务、逻辑服务和通道服务。数据服务主要是一些要做特殊数据类型的存储,比如图片服务。逻辑服务更多的是 CPU 密集、计算密集的操作,比如答案格式的定义、解析等。通道服务的特点是没有存储,更多是做一个转发,比如说 Sink。

这是引入服务化之后整体的架构。

演讲中还介绍了基于 AngularJS 开发知乎专栏的新实践。后续我们将在网站上发布演讲视频,敬请期待。

2014-12-31 00:22100050
用户头像
臧秀涛 略懂技术的运营同学。

发布了 300 篇内容, 共 135.0 次阅读, 收获喜欢 35 次。

关注

评论

发布
暂无评论
发现更多内容

VUE 数据分页

HoneyMoose

【web 开发基础】php 开发基础快速入门 (4)-PHP常量详解

迷彩

php 常量 9月月更 魔术常量

【web开发基础】PHP快速入门(5)-PHP运算符之算术运算符和字符串运算符详解

迷彩

运算符 php开源 9月月更 web开发基础 算术运算符

Java中synchronized关键字到底怎么用,这个例子一定要看!

wljslmz

Java synchronized 9月月更

数据结构第六章查找,期末不挂科指南

梦想橡皮擦

数据结构 9月月更

也谈“我们开发者根本不想做运维!”

愚夫一得

DevOps 语言 & 开发 文化 & 方法 技术中台 运维‘

2022年中国HR SaaS行业洞察

易观分析

HR SaaS

VolareFinance 测试网教程(更新)

鳄鱼视界

透视星环科技上市:基础工具、技术融合、场景应用三维击穿

易观分析

数据

leetcode 226. Invert Binary Tree 翻转二叉树(简单)

okokabcd

LeetCode 数据结构与算法

数据结构第五章图,期末不挂科指南

梦想橡皮擦

9月月更

Python之如何判断闰年

梦笔生花

9月月更 判断闰年 format格式化字符串

九月书单

图灵教育

科普 计算机 新书

React 新提案 useEvent 已死?不,它将涅盘重生。

清秋

React useEvent RFC 提案

【web 开发基础】php 开发基础快速入门 (3)-PHP程序符号标记和程序注释的使用及空白符详解

迷彩

php开源 9月月更 web开发基础

九月书单

图灵社区

科普 计算机 新书

Spring Security 介绍中的 servlet 和 reactive

HoneyMoose

【编程实践】利用Python看看那些QQ好友都在QQ空间发了啥

迷彩

词云图 selenium Python爬虫 9月月更 结巴分词

2022-09-29:在第 1 天,有一个人发现了一个秘密。 给你一个整数 delay ,表示每个人会在发现秘密后的 delay 天之后, 每天 给一个新的人 分享 秘密。 同时给你一个整数 forg

福大大架构师每日一题

算法 rust 福大大

APISIX是怎么跑起来的

geange

lua api 网关 APISIX 网关 APISIX的源码解析

万字详文,剖析企业数字化的降“本”增效

阿里技术

数字化 降本增效

【kafka异常】使用Spring-kafka遇到的坑

石臻臻的杂货铺

Kafk 9月月更

Python应用之求100以内的奇数和

梦笔生花

9月月更 变量和循坏的应用 递归求和

数据结构第七章排序,期末不挂科指南

梦想橡皮擦

数据结构 9月月更

Java中只有8大数据类型吗?看了本文,你会收获颇丰

wljslmz

Java 数据类型 9月月更

还不了解堆栈和队列吗?数据结构最基础、最重要的概念必须掌握!

wljslmz

数据结构 堆栈 队列 9月月更

【云原生 | 从零开始学Kubernetes】十三、k8s的容器探测以及启动探测

泡泡

云计算 容器 云原生 k8s 9月月更

云渲染比自己的电脑好用太多,这4个因素要考虑

Finovy Cloud

人工智能 云计算 渲染 云渲染

Python应用之九九乘法表

梦笔生花

9月月更 九九乘法表的实现 变量和循坏的应用

OptaPlanner快速入门-helloworld

积木思维

物联网平台常见问题与答案汇总

阿里云AIoT

数据 物联网平台 物联网 协议 mqtt

从0到100——知乎架构变迁史_架构_臧秀涛_InfoQ精选文章