从Facebook海归到国内互联网公司高管(四)

关于技术栈,各个方面比较多,这里先讲infra。文章较长,不喜技术者慎入。

我在国外只在FB做过工程,infra方面跟G比较类似,跟国内比的话应该是两个极端。硅谷其他公司,个人理解或多或少介于两者之间,欢迎知情者补充。

要作技术对比,理解人才背景和发展历史很有帮助。硅谷的技术圈是一个联合国,聚集了各个国家的顶尖人才,土生土长的美国人反而是少数。中国虽然人多,数理化的底子也好,但毕竟只是一个国家。然后国内互联网公司比美国多了不知道多少倍,不管美国有的还是没有的,中国都有,还不止一个版本。所以有限的资源还分得很散。

现在很多人说互联网寒冬,应该是跟之前人口红利时期疯狂扩张对比。那时候,我们公司研发一年从100扩大到1000。滴滴Uber大战期间,从无到有搭建各个部门,都是几百几百的进人,没人脉的海归都不给管理岗。平安搞智慧城市半年就进了500人。这种例子遍地都是。相比之下,FB增幅长期都坚持每年不超过30%。

这种市场推动下,国内大部分技术人员都不是985或者计算机专业。国内又没有一个良好的转专业或者选修机制,课程水平大家也都知道。那为啥感觉国内各种app功能比美国多无数倍,也不见得一天到晚当机或者长得不忍直视呢?这不得不佩服国人的智慧,演化出一套构架让任何小白看一本Java入门就能开始贡献产品级代码。

也正是因为这套架构,让程序员变成了一个劳动密集型工种,让996变得家喻户晓,程序员单位时间生产力跟美国截然不同。

代码管理

代码管理的重要区别就是 FG 是一个代码库加主干开发,国内除了个别玩Go的,一般至少一个项目一个,很容易就到四位数级别。关于repo数量,很多争议。单个repo主干开发,确实影响迭代速度。我在FB升级numpy,折腾了一个多月解决各种dependency,最后还是有两个build搞不定,强行升级让他们团队自己搞了。国内也缺少git/hg高手,check in的冲突都不一定能摆平。开源的gitlab也不scale。老板催急了肯定就顾不了那么多了。

多repo国外也很常见。但是在国内,合作意识本来就不足,repo多了雪上加霜。如果你回来做管理,可能处理最多的问题就是下属抱怨事情不能推进因为其他部门不配合。有时候甚至故意不给代码库权限,让你没法估计工作量。我一个朋友公司,infra想要int改long,业务研发一直拖着,多次惊动CTO出面也无济于事。业务优先你能怎么办?跨部门提交代码的事情国内我从来没听说过。

这种情况下,各个部门重造轮子是常有的事。自己造轮子能壮大团队,谈合作只会得罪人,你选哪个?哪怕CTO级别,手心手背都是肉,往往也就睁只眼闭只眼了。

除了不合作这种软性问题,多repo也有技术硬伤。首先是公共lib的管理,一个公司几十个JSON lib,不兼容导致生产事故是常有的事。即便有同一个lib,跨repo的变更没法保证原子性。还是那个int改long的事,四位数的repo改了整整三个月,中间因为RPC类型不匹配出过两次严重down机事故。在FB的话,codemod自动生成一个大diff,assign到不同部门,大家各自review然后approve就完事了。

要解决这个问题,不光要有repo的技术,还要有意识。否则代码合并了,每个项目一个目录,没人维护公共的东西比如lib和RPC,没有意义。

微服务

微服务主要是指业务逻辑层的架构,也就是承接前端的各种请求,根据业务逻辑,拆解成底层各种数据访问或者其他网络请求的这个模块。

FB的这个部分,从创始到现在,一直是一个叫www的单一php代码库完成。只是编译系统从最早的php原生解释器,到hiphop编译器(php->C++),到just-in-time的hhvm编译器。FB所有桌面和app的全部业务逻辑都集中在一个代码库里面,无可否认,学习周期还是比较长的。而且经常重构,基本一年不碰,可能就不知道怎么写了,写出来也不符合新规范要求。

国内大部分公司都经历了业务逻辑层从单体到微服务的转型,刚开始是因为单体服务牵一发动全身,实在没人搞得定,后来服务越拆越细。有个统计,针对8个数千人研发团队的公司,除了新浪,其余都是四位数的微服务。其中5个公司微服务比研发人员多。有个公司仅7000台服务器就跑了3000个微服务。

具体每个微服务在做什么?举个例子,比如我们要做一个社交用户管理系统,那首先要有个用户数据库保存用户基本信息。为了保证服务稳定和数据隔离,就会有个专门访问这个用户数据库的微服务,所有应用都必须通过这个微服务访问用户数据库。为了保证性能,还会加个缓存,一般是Redis,然后又有个专门访问这个缓存的微服务。在这两个数据底层服务之上,有个业务层的微服务负责新增、修改、删除用户等前端请求。如果需要批量修改数据,又会有个批量修改的微服务。批量修改往往是异步服务,又会增加一个队列,和专门访问这个队列的微服务。这就五个微服务了。用户需要交朋友,那就需要一个用户关系数据库和刚才罗列的对应这个新数据的五个微服务。然后现在删除老用户同时需要异步批量删除朋友关系,所以又要一个新的微服务专门处理删除老用户的请求。再加上状态更新、聊天、推荐等系统,很容易就三位数了。

服务细了,门槛就低了,能写的人也就多了。现在Spring Boot/Cloud 发展很快,加上Java各种牛逼annotation,我看到的用户系统,虽然可能有10+个微服务,自己写的代码也就100多行。相比之下,FB的用户系统,恐怕现在已经没有几个人能彻底搞明白了。

但是问题也就来了,比工程师人数还多的微服务,出了问题怎么办?事故定位就是个大难题,经常有事故完全不知道原因,只是碰巧重启或者回滚就好了。配置管理、依赖关系分析也非常棘手。就算服务写得天衣无缝同时运维团队超级牛逼,执行一点故障都没有,资源浪费就没法解决了。每个服务都只敢按照尖峰流量配置资源,平均CPU利用率在1%以下很常见。下面讲multi-tenant服务会进一步讨论资源利用率问题。

测试系统

国内几乎所有的公司都有测试团队,不像硅谷工程师自测是主流。这倒不是问题的关键。FB很多给新人的task就是给系统写测试案例,说白了也就是测试的活。

最主要的问题,一个是缺少自动测试的框架,下面CI/CD会进一步讨论,另⼀个更致命,没有便捷的测试环境,开发⼈员无法独自跑集成测试。FB所有业务逻辑在⼀个www代码库,加上hhvm也就⼗多G,⼀个机器器就能跑起来。每个人都有⾃己的服务器可以测所有功能。如果上千个微服务,上千个JVM,需要多少机器才能跑起来?
所以⽐较常⻅见的做法是有个独立的测试环境专⻔做集成测试,几百上千台机器。开发环境、测试环境、⽣产环境的同步⼜是个大问题了。同步太频繁肯定经常break,太不频繁测试环境也就没意义了。加上代码库繁多⽽且不停添加,同步很难自动化。各种配置也是手工为主。结果就是⽣产环境跟测试环境到底什么区别,没⼈知道。然后很多变更是没有集成测试直接上生产的,所以回滚是常有的事。有⼈述职时候提到的业绩就是从“逢发布必回滚”到某个比例的回滚。
另外一个有意思的现象,也是因为开发没有⾃己的测试环境,Hackthon没法做demo。总不能每个Hackthon队伍都给⼏百台机器跑demo。所以Hackthon最后往往就变成了算法题的形式,跟公司业务没有任何关系。

持续集成和持续发布(CI & CD)

持续集成和持续发布,3-4年前在国内是很⽕的概念,现在开会如果再讲这个,可能就会被认为已经out了。大部分做infra的⼈都会说⾃自己公司有CI/CD。我不知道CI/CD的准确定义,每个公司都不太⼀一样。这⾥主要讲一下FB的系统。
FB是单repo加主干开发,所以任何变更直接集成,全局可见。为了保证主干的稳定性,每个diff提交之前会强制跑本地测试,全部案例通过才能提交。提交以后开始在后台⾃动跑集成测试,有错误会高亮提醒reviewer注意。如果是前端的diff,也会自动在各种device上跑测试。CD的话,www之前是一周全⾃动发布一次,外加每天两次hotfix需要⼈工approve。发布流程都是各种灰度直到全量生产。现在是完全自动持续发布。后端各种服务也有⾃动CI/CD⼯具但是没有强制使用。国内CI很难做因为上面说了微服务架构没有简单⽅法拉起测试环境。发布⽅面倒是很多公司都能做到灰度了,但是还是⼈工触发为主,因为回滚⽐较常见。发布还是⼀个⽐较严肃的事件,因为测试环境问题,发布之前往往是没法⾼保真的集成测试的。
大家可能经常听说国内产品上线,几乎所有人都必须在场,通宵联调,就是这个原因。

Single vs. Multi-tenant服务

这⾥主要讨论⼀些通用的后端服务,⽐如数据存储,检索等。阿⾥对应部⻔叫技术中台。这些服务的⼀个重要区别就是FG是⾃研的单实例服务,⽽国内往往是直接⽤开源或者对开源⼀定程度封装,然后每个应⽤部署一个实例的⽅式运行。如果熟悉SaaS术语,就是multi-tenant跟single-tenant的区别。

1*yGEFY0VfHAUuk7XyluuuvA

Single-tenant⽅式服务出问题互不影响。缺点就是管理成本⾼⽽且资源利用率低。Multi-tenant对服务本身质量要求很高,出了问题全局不可用,不出问题也要有QoS等机制确保每个tenant的服务质量。但是管理成本和资源利⽤率就⾼多了。

我之前在FB写了一个multi-tenant 的跟LBS有关的服务,⼤概十来个应用。上线以后没管,几个⽉以后上去看,QPS到了千万,CPU利用率峰值已经到70%。Ads知道了怕出问题想要单独部署一个实例,我死活不同意,说没人运维。有一次,某个业务不⼩⼼iOS地址解码的访问量增加了三倍把苹果的服务打爆了。我的服务因为多租户,偶尔一个应用流量涨数倍大盘不会有很多影响,一直没出过问题,后来ads就不提要求了。

下⾯讨论几个重要服务的multi-tenant实现方式。

数据库、缓存、队列服务

数据库是一个⼤痛点。国内⼏乎所有工程师对“SQL慢查询”这个概念耳熟能详⽽且恨之入骨。⽣产事故诱因里面起码排名前三。

在FB,www代码⾥面是没有SQL语句的。因为FB把数据抽象了,根据社交网络的特点,只有点和边这两个表。⽐如⽤户是点,朋友关系是边。新的业务,只要申请⼀个新的点和边的类型就好了。然后有专⻔API做数据的读写,不用关⼼数据到底存在哪个机器上,也更不⽤提前申请数据库资源。

事实上,FB的DB系统经历过多次⼤改造,从单数据中心到分布式slave到分布式master,引擎也从InnoDB换成了RocksDB,其他各种MySQL升级不断。这些事情,因为有了点和边的抽象,产品研发团队是完全无感知的,也从没影响过生产。我之前在infra都不知道DBA这个岗位的存在。在国内,每次DB调整都是⼤动作,要集中全公司的精英研发⼒量,规划执⾏一年以上很正常。

缓存国内⼀般⽤用Redis。这个东⻄高级特性非常多,每个组的玩法都不一样,运维压⼒很大。FB的话因为数据库抽象了,所以缓存很好统一,⾃己写了一个writethrough cache叫TAO,研发使用的都是TAO的API,完全不需要考虑数据⼀致性的问题,也不需要知道后台存储的⽅式。

队列服务的话开源要不Kafka要不RabbitMQ,都不是主流语⾔,有能力驾驭的运维不多,发生了事故经常查不出原因。FB的异步服务,程序里面只要声明需要异步执⾏的代码就好了,不要管理队列。

写高并发的multi-tenant服务还是只有C++。 C++11以上的很多feature能够大幅度减少代码复杂度,尤其smart point是必须的。国内懂C++和这些feature的人很少。看看CSDN还有知乎上讨论C++的帖子就知道了。

海归能做什么?

理解这些问题以后,回国面试不妨跟面试官讨论讨论,相信一定会有很多共鸣,因为这些事每家都遇到。FB回国的有不少在做infra方面的创业,很多是为了提高工程效率。

但是,实事求是的说,这么多年了,跟业务日新月异的变化比,国内infra的改变微乎其微。在我看来,关键问题倒不一定是技术能力。我曾带领三个人C++半年复现了FB的Scuba系统,单表的任何SQL查询,哪怕数十亿记录也只要亚秒级的时间。最主要的问题还是管理,而且越早介入越好。

所以我更想做中小公司技术负责人而不是大公司哪怕VP。变化从来都是从小公司开始的。我现在招人就是硅谷的技术面试+文化的形式,还会先来一轮网上笔试。比leetcode初级还简单的题也能淘汰一半左右。目前猎头推了50份简历只录了一个,很多一些知名大公司的人,发现基本数据结构都不知道。这样能走多远坦白说我也不知道,但比天天开发 me too 的功能就开心太多了。