第一章 缘起开源
第一节 写了个好玩的东西叫Codis
我记得是有一天下午在喝咖啡的时候,同一个小组里的伙伴随口聊到的这个想法,这里面其实有一个特别创新的点,解决了一个大量用户并发访问情况下数据的无痛迁移问题。所以当时它就是一个动机,非常随机的一个动机。而且Codis从开始写到发布,第一个版本一共用了不到两个礼拜的时间。
——黄东旭,PingCAP联合创始人,CTO
2013年,还在豌豆荚当工程师的黄东旭遇到了一个挑战:由于业务量的急剧增长,有些服务器的缓存告急。如果缓存耗尽,就会造成用户在使用豌豆荚的服务比如搜索或浏览功能时,出现服务器性能急剧下降的问题,甚至完全失去响应,这显然是用户不能接受的。豌豆荚当时的一个设计理念,就是尽可能地把数据全都放在缓存里面,因为这样访问速度会更快,也才能保证更顺畅的用户体验。不用缓存的话,用户每次访问可能就需要花费100毫秒甚至更长的时间;如果希望只用1毫秒访问的内容就显示出来,那就只能用缓存技术。
但在当时,缓存服务只有单机版,而要解决这个问题,必须能够把缓存做成一种弹性水平扩展的分布式服务。这么一来,如果一台服务器不够就可以加一台或多台,直到缓存够用为止。
和所有的软件工程师一样,刘奇和黄东旭花了不到两个礼拜的时间,写了个叫Codis的软件,实现了这样的服务,彻底解决了这个业务问题。但和大部分软件工程师不一样的是,他们没有止步于此。黄东旭发现,Codis属于基础软件的范畴。因为缓存问题并不是只有豌豆荚的某个具体业务,而是所有业务都会或多或少、或早或晚遇到的问题,所以,他们做出了一个与众不同,但非常重要的决定:
把Codis开源。
Codis是第一天就开源了的,它的成功其实是我们后面事业的基础,因为Codis在开源以后,成为全中国能叫出名字的互联网公司都在用的东西。但它其实是我们花了一两个礼拜做的。这么快的时间,也没做任何推广,就因为它确实是刚需。
——黄东旭,PingCAP联合创始人,CTO
Codis在开源一年以后,已经红遍大江南北;传播范围之广,连原作者都始料未及。时间来到八九年后的今天,黄东旭还经常在各种技术会议上被问及Codis的各种技术细节的问题。“有时和会议主题一点关系也没有,而且这个项目也早已不再由我作为主要的维护者了,但是真的经常还是会有人问我关于Codis的事情,因为他们还在使用,很多都是用户自己根据需求在维护。”黄东旭说到这里的时候,在无奈中又透着些许自豪。
开源的力量,就是以这样的方式给黄东旭以极其强烈的冲击。仅仅因为自己对某个需求的理解更深入一些,并给出了一个通用的、能够满足现实世界中人们的需求的解决方案,他的影响力就不胫而走,因而他无须花费巨大的人力和财力,用户就会主动而热情地选择他的产品,这简直是一种魔力!
也许,Codis的诞生过程的一个更加重要的意义在于,它让三个后来成为事业合伙人的豌豆荚工程师结合成了一个团队。黄东旭当然是其中一员,另外两人,就是刘奇和崔秋(见图1.1)。
Codis并不是从零开始做起的,而是建立在当时最流行的单机版缓存服务Redis的基础上的。最简单的理解就是:Codis利用了Redis已有的接口协议,但没有改造Redis本身,而是在其上层架设一套设施,把缓存服务做成分布式的。也就是说,在每一台服务器上,缓存服务还是由Redis来完成,但是将缓存中的数据从一台服务器扩展到更多的服务器上,以及这些服务器之间缓存数据的转移和同步,则是Codis实现的了。应用程序在使用Codis时,可以采用和单机版Redis几乎一样的方式,只是它面对的是一个“容量无限”的Redis缓存服务。可想而知,这样的服务会给应用程序的性能带来多大的提升,并且还几乎不需要对应用程序做任何的改动!难怪Codis一经推出,就会受到非常热烈的欢迎。
图1.1 左起:黄东旭、刘奇和崔秋
但是,软件开发可不是说说那么容易。其中有一个比较大的挑战,那就是作为一种分布式缓存,Codis需要能够弹性地自动扩容,既能实现扩容数据在不同系统间迁移,又能保证客户端访问数据的一致性。比如,在一台机器上有一块数据,数据扩容后,增加了两台服务器,这时需要把原来的部分数据挪到新的服务器上。在挪数据的过程中,还得保证客户端通过原来的服务器能够访问以前的数据。如何实现这一点,很让人伤脑筋。
灵感来得很突然,一天下午刘奇跟同事讨论之后,脑子里忽然就有了思路,知道了怎么写一个算法来实现。
这个创新的算法,刘奇并没有给它命名,但它却是Codis项目的起源。简单来说,这个算法是给Redis增加一个迁移(migrate)命令,这个命令相当于做了一个将缓存的键值从Redis A到Redis B的原子迁移。这个操作听起来很简单,实际上挑战很大,因为刘奇和他的同事希望尽可能在不改变或少改变Redis的前提下,保持上游的Redis一直使用新版本,这就涉及数据平滑迁移的问题。传统的数据迁移,一般是整片的数据搬运,两台机器停止写入,做数据交换。但是这个过程需要一定的交换时间,这点恰恰是刘奇和他的同事不能容忍的,他们觉得这个交换的时间曲线不够平滑,也让算法整体的设计变得复杂。而刘奇想出来的算法,相当于把一个大的迁移作业分解成无数个小的迁移作业,像蚂蚁搬家一样,一点点地完成数据交换,原本可能需要几天才能完成的大事务,用这个算法只需要几小时就完成了。打个比方,一般情况下,Redis的数据交互就像买东西时A给B一笔钱,B给A一个东西,然后A再给B一笔钱,B再给A一个东西。而Codis是直接把A要购买的物品清单全部列好,然后一次性发送,大大减少了交换时间,降低了连接建立和并发计算的开销。
黄东旭开玩笑说:“这个是非常老师傅的编程技巧,我以前都不知道。 ”
Codis使用一种现在已经炙手可热但当时还不甚流行的语言——Go语言来实现。Go语言的缔造者中,有一位全世界程序员公认的大神级人物——肯尼思·汤普森(Kenneth Thompson),他是UNIX操作系统的主要开发者。UNIX操作系统不仅是一切主流操作系统(包括苹果的macOS,还有Linux及其前身MINIX等)的事实基础,更重要的是其设计哲学几乎影响了整个软件行业和在这个行业中的所有人。尤其是在行业内深耕的、具备相当素养的工程师,都一定会自觉或不自觉地受到UNIX设计哲学的影响。Go语言的另一个主要设计者和早期实现者罗布·派克(Rob Pike),虽然没有直接参与最初版本的UNIX的开发,但同样也属于贝尔实验室UNIX开发组的最资深成员,并且他是字符编码UTF-8的主要实现者。UTF-8是目前整个互联网上的字符编码的事实标准,它的灵活性和包容性使得从古埃及圣书体到只在小范围内使用的苏州码子,以及层出不穷的网络表情符号中的每一个古今中外人类所使用过的字符都能够被编码并在网络中传播。
所以,Go语言的出身决定了它具备极高的品质。而且,Go语言也是一种从第一个版本起就开源的程序设计语言,所以来自世界各地的很多程序员也是第一时间就发现并开始使用它,然后立刻发现了它所具有的很多美妙的语言特性,比如GoRoutine,这是一种从语言层面上就支持并发编程模型的特性。在并发中使用最频繁的、也是占用时间最多的上下文切换,其他语言基本上是通过线程的切换来完成的,但是通过在语言中内建的资源调度器,Go语言使用比线程轻量得多的GoRoutine完成了上下文切换,比前者节省了高达80%左右的时间。可想而知,这会给涉及密集上下文切换的重度并发程序带来怎样的性能提升。尤有进者,Go语言的并发模型的基础是一种有着严格数学背景的、称为CSP(Communicating Sequential Processes,通信顺序进程)的原语,所以在实际工程中具备几乎无懈可击的可靠性。这一切都深深吸引着这三个豌豆荚的工程师。图1.2展示了Go语言负责人转发并评论PingCAP的推文。
图1.2 Go语言负责人转发并评论 PingCAP推文
黄东旭和刘奇都在豌豆荚的基础架构组,这个组的英文名字叫作Technical Infra。刘奇最早还在京东的时候就开始使用Go语言,在京东期间他还把Go语言的标准库源代码(又称源码)几乎都读了一遍。刘奇去豌豆荚面试时,黄东旭是面试官。根据刘奇的回忆,他在面试过程中黄东旭除了考查他算法,大部分时间在和他交流Go语言的各种使用心得。而崔秋则是在广告和游戏业务组,也十分热爱Go语言,用崔秋自己的话说,通过几次内部的黑客马拉松比赛,他马上就和同样热衷于Go语言的黄东旭他们成了“臭味相投”的同道中人。正好崔秋所在部门的业务最早遇到了缓存问题,所以崔秋成了Codis的首批用户,而Codis又是开源的,所以,同样擅长编程的崔秋不仅在业务中重度使用Codis,还动手对Codis的源代码做了不少改进。就这样,三人基于对技术的共同爱好越走越近。
黄东旭小传
黄东旭来自广西南宁,是一位狂热的编程爱好者。在小学期间他无意中接触到Basic编程的图书,之后便对计算机相关的书籍产生了浓厚的兴趣,这位少年通往编程世界的大门也随之打开了。
我每周都会让我母亲带我去书店,去看那些计算机相关的书,一待可能就是半天或一个晚上。
1997年,黄东旭拥有了自己人生中的第一台计算机。小学毕业时,他已经能玩转基本的C语言、Pascal;初中毕业时,已经能够用Visual Basic做一些小工具的开发;高中毕业时,在Linux、GNU及自由开源软件运动的社区里他结识了很多志同道合的网友。
大多数同龄人很难理解黄东旭的这些所作所为,“怪咖”这个称号也就伴随了黄东旭几年的时间。
所以还是蛮孤单的,后来我也习惯了这种状态,享受起这种孤独的时光。我更喜欢跟机器打交道。机器让我觉得非常舒服,觉得一切尽在自己的掌控之中,所以很有安全感。它就相当于是你的特别好的伙伴。就算我开着计算机不干什么事情,就是看着它,也会觉得很温暖。
也正是因为年少时的这段经历,让黄东旭成为一个外表乖巧,内心却又比较叛逆、固执、任性的人。他还有一个鲜为人知的身份——摇滚爱好者(见图1.3),并且听摇滚乐与他喜欢开源软件之间还有着某些特别的联系。
图1.3 摇滚爱好者黄东旭
我接触开源是在初中,当时正好也开始听摇滚乐。我那时还住校,然后周末的晚上就会溜出去看演出。
现在的黄东旭,吉他、贝斯……基本上他都会玩。摇滚代表一种独立自由的人生姿态。他觉得坚持独立思考是这个世界特别稀缺的品质。比如音乐和审美,大多数人可能不知道自己喜欢什么音乐,或者不知道什么是美,或者大家认为的美其实是别人说的美,可能没有经过自己的思考。他觉得审美也是一种能力,尤其是独立的审美能力。他对计算机和摇滚乐的喜爱,源于他对秩序和美的渴求,这也是开源核心价值的组成部分。
UNIX哲学
20世纪60年代,贝尔实验室、麻省理工学院、通用电气公司合作了一个名为Multics的项目。由于合作中存在诸多问题,最终Multics项目失败了,但项目组的核心成员之一肯尼思·汤普森不甘失败,他认为这个项目中还是有一些非常有价值的想法和积淀的。于是,他用汇编语言重新写了一组核心程序,同时包括一些工具和驱动, 以及一个小型文件系统,并起名Unics。后来,肯尼思·汤普森的同事丹尼斯·里奇(Dennis Ritchie)发现这个东西太好用了,但是移植性很成问题。因为每次将Unics安装到不同的机器时都要重新用汇编语言写一遍,很不方便,于是,丹尼斯·里奇就把它用C语言重写了一遍,同时把名字改为UNIX,这一年是1973年。
经过多年的发展,UNIX的技术越发成熟,如今已发展成为一个支持抢占式多任务、多线程、虚拟内存、换页、动态链接和TCP/IP(传输控制协议/网际协议)网络的现代化操作系统。它以直接或间接的方式,构成了目前所有主流操作系统的基础。UNIX中的一些重要设计因素到如今都还在使用,甚至形成了一组称为“UNIX哲学”的技术原则,其核心思想一言以蔽之,就是“松耦合”,也就是以简洁自洽的方式设计模块化组件,每个组件只完成一项工作,彼此之间边界清晰,然后用管道的方式,把复杂的工作用各项单一工作串联成流水线。换言之,就是软件组件之间各司其职,以松散的方式独立开发和迭代,只保持接口的相对稳定性,这样即便某个组件出了bug(漏洞),也能单独对这个组件进行修正升级,而不会影响其他组件,这就可以大大减小整个系统的“爆炸半径”。
UNIX哲学对黄东旭还有像他一样优秀的同事,都产生了深远的影响。