【AICon】探索RAG 技术在实际应用中遇到的挑战及应对策略!AICon精华内容已上线73%>>> 了解详情
写点什么

从 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:2299470
用户头像
臧秀涛 略懂技术的运营同学。

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

关注

评论

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

圆满落幕!回顾 eBPF 技术的发展与挑战

OpenAnolis小助手

Linux 云原生 ebpf 云栖大会 龙蜥社区

跟着卷卷龙一起学Camera--MIPI 03

卷卷龙

ISP camera 11月月更

React组件之间的通信方式总结(上)

beifeng1996

React

百度前端react面试题总结

beifeng1996

React

React源码分析4-深度理解diff算法

goClient1992

React

从纯函数讲起,一窥最深刻的函子 Monad

掘金安东尼

前端 11月月更

📢利用Vite插件助力证书安装

小鑫同学

前端 插件 11月月更

Ansible 部署的时候提示错误 SSH password instead

HoneyMoose

企业级业务架构设计:方法论与实践 学习笔记

程序员架构进阶

业务架构 TOGAF 11月日更 Zachman

决策树-泰坦尼克号幸存者预测

烧灯续昼2002

Python 机器学习 算法 sklearn 11月月更

计算机网络:数据链路层功能

timerring

计算机网络 11月月更 数据链路层

从1开始,扩展Go语言后端业务系统的RPC功能

海风极客

后端 go语言 11月月更

promise执行顺序面试题令我头秃,你能作对几道

loveX001

JavaScript

js异步编程面试题你能答上来几道

loveX001

JavaScript

React组件之间的通信方式总结(下)

beifeng1996

React

ElasticSearch这些坑记得避开

Java 架构

StarRocks 技术内幕 | Join 查询优化

StarRocks

数据库

从0开始,用Go语言搭建一个简单的后端业务系统

海风极客

后端 go语言 11月月更

跟着卷卷龙一起学Camera--信号采样01

卷卷龙

ISP camera 11月月更

Linux中 dir 命令还能这样玩!

wljslmz

Linux 11月月更

如何在知乎平台上做营销推广:推荐几种引流方式

石头IT视角

这样回答前端面试题才能拿到offer

loveX001

JavaScript

React源码分析2-深入理解fiber

goClient1992

React

史海峰:成为技术领导者 从技术到管理的必经之路丨声网开发者创业讲堂 • 第 5 期

声网

技术管理 人工智能’

一次基于Fastjson的JNDI注入

网络安全学海

网络安全 安全 信息安全 渗透测试 漏洞挖掘

跟着卷卷龙一起学Camera--信号采样02

卷卷龙

ISP camera 11月月更

前端必会面试题总结

loveX001

JavaScript

从2开始,在Go语言后端业务系统中引入缓存

海风极客

Go 设计 后端 11月月更

极速体验docker容器健康

程序员欣宸

容器化 docekr 11月月更

年前端react面试打怪升级之路

beifeng1996

React

React源码分析3-render阶段(穿插scheduler和reconciler)

goClient1992

React

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