独家深度 | 那些年我做开源和自研走过的弯路和经验

共 14806字,需浏览 30分钟

 ·

2022-02-11 09:41


作者:阿里云数据库OLAP产品部——韩述


工作十年,一直专注于技术研发。一路走来,既参与过从第一行代码写起的纯自研的项目,也做过基于开源产品扩展慢慢转型成自研的项目,还负责过开源社区的管理和维护。


辗转折腾的过程中,有幸以各种不同角色(小白用户、开发者、维护者、管理员)参与过数个顶级开源项目Nginx、K8s、Postgres等的开发和维护。尤其是近几年一直在做开源结合自研类型的基础平台类项目,走过一些弯路,踩过很多坑,也总结了一些经验,本文将重点分享开源结合自研项目的一些经验。



开源 VS 自研




开源好还是自研好一直以来都是一个很有争议的话题,几乎是任何一个产品,尤其是基础平台类产品绕不开的一个抉择。

开源优势:入门门槛低,有很多可以复用的成果。通常而言,功能比较丰富,周边生态也比较完善,投入产出比比较高。一句话总结,投入少,见效快。
开源劣势:内核不容易掌控,门槛较高,通常开源的功能和实际业务有一些GAP,很多开源产品开箱即用做的不够好,需要大量调优。一句话总结,入门容易掌控难。
自研优势:产品核心技术掌控程度高,可以更好的贴着业务需求做,可以定制的更好,基于上述两点,通常更容易做到良好的性能表现。一句话总结,量身定制。
自研劣势:投入产出比略低,且对团队成员的能力曲线要求较高。此外封闭的生态会导致周边支持缺乏,当需要一些新需求时,往往都需要定制开发。一句话总结,啥事都要靠自己。

开源结合自研

事实上,自研的理论上限更高,但同时意味着更高的入门门槛以及前期投入,而开源的上限容易受限于开源项目本身的设计、架构、生态等限制,但是初始创业阶段更容易快速落地。有点类似于开发语言的选择:C/C++更容易写出高性能的产品,但同时会遇到更多功能性问题,而Java则更容易先达到业务目标但是在后期很容易遭遇瓶颈。

如果不计成本,那么自研无疑是最好的选择。但是大多数时候不差钱总归只是一个美好的梦想,而单纯基于开源又往往无法满足需求。于是,现在大部分需要巨大投入的基础平台类产品如中间件、数据库、操作系统等,都会选择一种折中的开源结合自研的发展方式。

这种方式早期基于或者结合开源产品及其生态,快速支撑业务,并从中逐步培养起研发团队,最终成长为高度掌控的自研产品。甚至于有些产品在自研一段时间之后,获了一定的领先性和差异化优势,再次独立开源之后在业界影响力甚大。比如典型的Google的BoringSSL就诞生自开源OpenSSL,阿里的Tengine诞生自开源Nginx,这些产品在自研一段时间之后独立开源,由于其独到的领先性和差异化,同时又能很好兼容已有生态,都成功占据了相当的市场份额。

选择开源结合自研的路线,一般都是出于成本和效益的权衡。但是为什么能够获得比较好的成本和收益之间的平衡呢?下面从三个方面来详细分析一下:

1、论开源社区的三大财富

要想长成为一棵茂密的参天大树,必然需要发达和庞大的根系支持,而往往开源社区恰好已经具备了这一点。如何让自己的产品长在开源社区的强大的根系上,并与之紧密结合,从中吸取营养,再通过光合作用长出新枝,是开源与自研结合的核心魅力所在。


一般来说开源社区至少有三大宝贵财富值得我们继承

  • 优秀而历经考验的Base Code,包括附属的周边生态

  • 源源不断更新的Feature,Bugfix以及它们背后的tester们

  • 世界通行的人才圈子,良性的竞争和循环


如何利用好他们,并能够在其基础上创新、发展,是本文要重点分享的经验。

2、论红花和绿叶

所有做技术项目的人都知道,在一个项目或者产品中,内核相关技术的占比肯定不会超过一半,即使是内核技术中也有相当多的是基础、琐碎却必须的框架层、适配层、业务逻辑等,真正能有重大突破的亮点,少之又少。

而内核技术之外,产品易用性、周边配套工具、兼容性等等又甚至比产品核心技术本身更能决定一款产品的普及程度。负责这些方向的研发人员和团队往往更像是红花边上的绿叶。一直以来这都是一个难以调和的矛盾,要么很容易导致人员和团队的频繁流失,要么导致畸形发展,最终产品变成头重脚轻的畸形儿,核心能力强大而周边配套却极为羸弱。

除此以外,日复一日,年复一年,海量而繁琐的测试工作,艰辛的Bug排查和修复工作又不知道消耗了多少技术人的宝贵青春。

事实上,开源和自研结合的项目,其中很重要的一个点,就是如何让开源去承担绿叶的职责,从而自研的力量可以更多得集中到如何做一朵红花。

经验一:
成为红花的秘诀是让开源做好你的绿叶


让自研成为开源的绿叶中长出的一朵红花,是一门艺术。开源社区,最宝贵的财富不仅仅是代码,更重要的是用户。诚然社区里的世界顶级程序员们是一项重要资源,但是更加稀缺的是那些始终在使用开源产品的用户和业务。他们日复一日年复一年的在做着各种测试,他们在免费使用开源产品的同时,也在默默贡献着自己的力量,与开源社区相互成就着,这是某一个公司内部私有的自研产品难以企及的巨大投入。

翻一翻Github上那一个个Bugfix和一个个Issue,任何一个都有可能是造成一次重大故障的元凶,你的心里慌不慌,背后凉不凉。此外有序的社区版本的发布,新Feature的研发和探索,用户使用反馈问题的修复,这些都将是你产品的最好助力。当然开源社区也同样需要更多的参与者(无论是开发者还是使用者),你和开源社区共同成就着对方。

做好开源结合自研项目的首先一条,就是要平衡好和开源的关系,尽量不要脱离开源的大框架和生态圈,保持与其的版本同步,站在巨人的肩膀上前行。同时又不能完全人云亦云的亦步亦趋,需要有自己的思考和研判,集中力量在关键领域进行突破和创新,才能有所成就。

3、论组织的安全性

大部分公司开门是挣钱的,很多时候需要考虑的不仅仅是技术的先进性,还要考虑业务的连续性,借用HR的一个术语就是组织的安全性。

显然,自研产品更加需要关注这一点,自研产品的核心研发流失,更容易带来灾难性后果。其实,明面上的流失还是其次的,真正可怕的在于,新鲜血液的融入成本。我们经常会在XX吐槽区看到,一个新人吐槽XX公司XX部门,几个老白兔,技术水平很差,但是手里把握着几万行所谓的核心代码,作威作福,感觉自己出头无望云云。

实际上,所谓的老白兔们,又何尝不是被困在了这所谓的自研核心的围城中呢。团队以及团队的Leader也没有动力去改善风气,以及吸引人才。一来外部不熟悉的人才很难上手,来了短期内也派不上用场,二来内部的人才和外部技术不通,内部人才想流失也不是那么容易的,没必要多费心思去挽留。恶性循环,很容易就走入死胡同。

举例来说,如K8s这样的世界TOP级项目,拥有全世界通行的内核框架、接口和使用方法,外部大量优秀的内行人才可待发掘,由于使用相同的技术栈,他们很容易快速融入团队,甚至推动团队带来质变。

内部的人才面对竞争,固然没有那么舒服惬意,但是望着手中的K8s这一走遍天下都不怕的技术硬通货,是不是给自己打工时更有干劲了呢。团队和团队Leader能不努力提升福利,营造良好氛围么,毕竟大门敞开,来去自由。这样很容易形成良性循环,团队产出高,公司赚到钱,大家有收获和成长,皆大欢喜。


从Gartner技术成熟度曲线说起




说了一大堆开源结合自研的好处,是否开源结合自研的产品就一定能成功?当然是否定的,任何一个产品都是经历挑战,一步一步闯关才最终得以成功的。众所周知,世界权威评测机构Gartner有一个技术成熟度曲线,用来描述一个技术方向的生命周期,大部分的技术方向,都很难跳出萌芽期、期望膨胀期(过热期)、低谷期(幻灭期)、复苏期、成熟期这样的规律。



事实上,这个曲线在哲学上确有一定道理,大部分事物都很容易走入这样一个轮回。做一款开源结合自研的产品的过程,很多时候,也会是类似的情况。

萌芽期:一般伴随着需求的产生,为了解决一个或一系列问题,通过选型、对比、论证等,最终引入了一款开源产品,开始试用。通常这时候懵懵懂懂,更多的是小心的试探。大家都是通过对问题的分析,选择一些场景,试用该产品,验证其效果以及稳定性等。对于开发团队来说,这个阶段,对开源产品的源码都不够熟悉,只能做一些参数调优以及小规模的功能改造。

期望膨胀期(过热期):在熟悉了一段时间后,由于开源产品很多都具有一定的成熟度,并且能够被最终选型使用的产品,往往早已在市场上经过激烈的厮杀,至少是一款世界一流甚至世界第一级别的产品。所以,大多数情况下,都能很好的解决一部分或一系列问题,甚至快速发挥举足轻重的作用,让大家尝到甜头。

这时,通常会快速的进行扩张。扩张是两方面的,一方面是对产品的改造,伴随着大量二次开发,往往会对很多模块进行重构、扩展,以更好的适配公司的业务需求。另一方面是业务的大量落地,经过了早期的萌芽阶段,产品的作用和稳定性得到验证,大规模推广期往往都在这时完成。同在在这个阶段中,产品团队的规模会得的很好的扩张,取得不错的业绩。

幻灭期(低谷期):经过了一段时间的大规模二次开发,以及大规模的业务推广。产品会遇到一个严重的瓶颈期。通常也会表现在两个方面。对于开发侧来说,经过1-3年的过热期,很多产品的源码已经被改的面目全非,然而这段时间开源社区也在有序的演进着,此时将出现一个我们通常所说的版本分叉问题。进而导致开源社区新的优异特性,难以被集成,而大量自研的特性又给产品背上承重的维护负担,此时如果再有研发人员的变动,简直是雪上加霜。

社区的代码越来越难合并了,而自研的代码又有大量的问题需要自行投入力量修复,甚至原本开源部分的代码的新Bugfix都已经无法直接复用了。大多数的团队都会在此时不得不放弃对开源版本的支持。然而开源版本同样在飞速奔跑着,不出一两年,大家就会觉得,自研产品越来越臃肿和落后,已经追不上开源的进度了。

另外从业务侧来看,海量的业务涌入,固然带来了不错的业绩表现,但是也带来了大量的线上故障,很多隐藏的问题逐渐暴露出来,运气不好的时候,连续发生重大故障才是真让人崩溃。

此外增长越来越困难了,当低垂的果实都被摘得差不多的时候,大家会发现,整个研发团队,已经慢慢变成了运维团队。有很多(超过一半)的产品,此时就放弃了。通常而言,一是找一个新的产品,重头再来,或者就干脆留上一两个人维护,大部分转战其他领域去了。

复苏期:能熬过幻灭期的产品,很多时候,倒不是研发团队自身做的有多好,更多的是产品本身的竞争力,以及不可替代性。很多时候也看领域发展的,有时候刚好有基础理论的更新:比如席卷全世界的云原生浪潮;比如Linux2.6的异步事件机制;比如大数据领域批处理、流处理的基础理论发生演进,这样刚好有机(借)会(口)可以用下一代的产品,来填上这个坑。

但是如果不幸,新一代的产品换汤不换药,没有本质上变化,就会出现,老产品维护不下去了,而新产品又推不动,没人买单的尴尬情况。复苏期,是一个艰苦难熬的阶段,总结来说,就是不断的填坑,放弃一些性价比低的自研模块和特性,重构部分持续有价值和生命力的模块,并重新和开源融合的过程。

成熟期:从研发角度看,能进入这个阶段的产品逐渐在开源和自研之间找到了较好的平衡。构建了与开源社区共存和相互促进的架构和形态,大规模业务落地的瓶颈问题得到解决,一般这个阶段属于收获期,研发的投入可以逐渐减少,但是产出却能够很好的维持,最关键的是产品形成了良好的架构,不那么依赖于某一个人的熟悉程度,新人能够快速的融入,产品进入可持续发展阶段。


萌芽期



产品的早期,通常有3个核心问题,需要解决:

  1. 定义问题:确定公司的业务目前已经或者未来即将遇到的问题,只有明确了问题,才有解决的必要,也就有了目标,这是所有后续一切行动的基础。

  2. 开源选型:通过一系列方法,包括并不限于查阅资料、试用、阅读源码、找人打听等等各种手段对比选择一款最match之前定义的问题,并且符合公司和业务未来发展情况的Base产品。

  3. 源码掌控:很多时候,上面两条都由项目的Leader代劳了,开发同学遇到的最大挑战往往是,开源代码动辄几十万几百万行,如何熟悉和上手掌握是一个很大的挑战。借用一句传说中的俗语,改不改得动。


定义问题

问题的定义,是联结业务和技术的重要一环,往往是所有技术相关工作中,最重要,但却又是最容易被忽略的。通常而言,有两个需要注意的,一是要尽量看一类问题,去解决共性需求,并且要从领域的角度系统的去看,要尽量避免去看个例问题,去解决个性需求。另一个就是要以发展的眼光来看待问题,信息爆炸时代,业务的增长总是很快的,可能从几千人访问到几千万人访问只需要几年时间,研发必须要提前布局。

开源选型

千里之行始于足下,然而这第一步迈向的方向将会给今后数年,甚至十数年的发展带来深远影响。尤其是一些关键核心项目的技术选型,几乎一旦定型,后期很难有重大变化,因为成本实在太高(人力还是其次,风险才是最大的成本)。

众所周知,选型主要从几个方面去进行,比如 影响力、先进性、活跃度等等,不需要过多赘述。这里重点说些容易被忽视的问题:

  1. 与一般通用的仅仅使用开源项目不同,更多需要考虑的是后续的二次开发,以及持续的维护扩展。我们不仅仅是选择一个开源项目,更重要的是要选择一个优秀的框架,我们需要基于这个框架向前演进,所以源码的质量,架构的先进性就会很大程度上影响我们的倾向。
    举一个具体例子,对于Web服务器的选型,Nginx VS Apache。从流行度上看,Apache的使用规模和生态是远大于Nginx的,但是Nginx是基于Linux2.6以上内核的异步多路复用架构设计的,而Apache是多进程模型。简单来说,Nginx可以轻松应对百万连接,而Apache则显得老迈了(访问量一大就容易遇到瓶颈)。所以全世界TOP级别的公司几乎清一色是Nginx系。
  2. 一定要先定义问题,再做选型。只有问题定义清楚了,才能去想对应解决问题的方案,整个动作才能不变形。我遇到过的重大失败的项目,很多都是,先选定了一个开源产品,然后才来丈量业务,对着已经选定的产品挖掘问题。

    这无异于碰运气,运气好,找到一些场景去解决了。运气不好,你选择的开源产品或者技术路线根本不适合公司或者业务的情况,你就死掉了。就好比医生看病,肯定是首先看症状确定你哪里有问题,然后选择合适的药去医治,而不可能先预设好要用的药,再来找有没有对应的病。


源码掌控

通常来说,问题定义和开源选型在几周至多几月,肯定结束了,真正漫长卓绝,需要奋斗的第一场硬仗,往往都是对开源项目的源码掌控关。根据项目的复杂度和规模,需要半年到数年不等的一个周期。

经验二:
熟练使用Perf,帮助你跨过第一个门槛


无论任何项目或者产品,都无法脱离操作系统,CPU、内存、IO、网络几大件,最终都需要依赖内核提供的接口。熟练使用Perf,可以帮助你在对产品完全不熟悉,无从下手的情况下,跨过第一个门槛。阿里内部有一个叫做扁鹊的产品,可以在进行Perf时,绘制出调用关系,非常好用。如果不使用阿里内部的工具也可以,如下的图,是我使用开源的工具 perf + gprof2dot + graphviz 画出的调用关系图

在Centos或Ubuntu上只需要如下简单几步,就能看到图了


使用Perf出来的运行时调用关系,去熟悉一个产品,可以更加有的放矢的理清主链路(占比高的自然是核心链路),并且可以自顶向下的梳理整个产品的架构,避免一上手就看静态代码的茫然和混乱,当有一定认识之后再与静态类图结合起来看,会更加清晰。而在这个过程中,说不定还会有意外的惊喜,比如发现一些深红色的热点函数,优化一下,也很有成就感不是么?

对项目有一定熟悉之后,就要慢慢开始尝试进行修改了,通常是从Fix Bug开始,这个一般人都知道。有时候产品还没有大规模在业务上部署,所以也遇不到Bug,这时甚至可以去开源社区翻翻Issue,找一些别人提的问题尝试解决,并提交MR给社区,目的是获得社区给你反馈,你改的对不对,好不好,通常社区都有更熟悉的负责人会给你反馈,如果没有反馈,那也是一种反馈(你可能完全南辕北辙了,自己需要找找问题)。

最后需要注意的是,TOP级别的项目,大部分都有很多源码解析的分享,一定要多看看别人消化过一轮的成果,切忌自己一个人埋头到代码里一阵瞎看,不但枯燥乏味效率低下,关键是没有任何反馈,代码都是连环的,你可能中间某一步理解方向是错的,但是自己也不知道,导致后面全部带歪了瞎耽误功夫。

经验三:
分享是提高自己的最好方式


对标题没有写错,分享是提高自己而不是别人的最好方式。当你得知,你写的分享文章,或者分享的Talk,将会被很多人从各个角度研究、提问或推敲的时候,你就不会对一些问题似是而非,懵懵懂懂,就有压力和动力去钻研透彻他们。一场分享下来,听众们往往只是对某一个领域入了个门而已,分享者却往往已经成为了这个领域的专家。

这个世界上,没有人天生就是专家,所谓专家只是他在这个方向上花的时间多,研究的透彻罢了。逼迫自己,甚至是整个研发团队,拆分开源项目,分模块撰写原理、源码解析,甚至整理成书,通过网络、媒体等各种载体公开宣传,一方面可以提升影响力,建立用户心智,扩大招聘范围。更重要的是参与的人,会从中自(被)然(逼)的获得提升。

定期的组织内部分享,通常是研发团队都会采用的方式。但是尝试体系化的编撰一份功能大全或者源码解析指南,也许会让你获得更多的沉淀和提高。甚至于可以更好的保留和传承,即使团队发生变化,后面的新同学,或者接手的团队,都能够很好的继承前人的成果。


期望膨胀期(过热期)



随着产品逐渐有标杆业务落地,后续的推广复制往往水到渠成。业务规模会很容易的快速膨胀,可能一年两年的时间内,使用产品的业务个数能够从个位数迅速增长到几千甚至几万个。汹涌而来的业务需要维护,海量的需求让人崩溃,好在价值的体现让人动力十足,这段时间总是让研发同学痛并快乐着。

事实上,除了一开始的选型定基调比较重要之外,一个产品最关键的时期,就是这个时候。能否更平滑、更快速的度过后面的幻灭期,包括进入成熟期之后能走多远,相当大程度取决于能否处理好这个阶段。

身处过热期,其实面对的直接困难和挑战并不多,因为团队的规模很容易获得大幅扩张,业务需求也源源不断,一切欣欣向荣,所以在这个阶段,最重要是要为未来考虑,多想想两三年之后的幻灭期。

前面说过,幻灭期最大的两个问题:一是大量的业务将放大稳定性风险的概率,很小的一个问题都容易带来灾难性后果,整个项目会背上越来越重的包袱,跑的越来越慢,甚至跑不动了。另一个是和开源社区的分叉问题,一旦和开源分叉,就无法享受到开源红利,这个产品就和自研无异了,需要巨大的投入去照顾方方面面。针对于此,有这样一些经验:

经验四:
找到那个撬起地球的支点才是最牛的


通常情况下,实现一个同样的二次开发需求,会有两种方式,一种完全魔改掉开源的实现,甚至于把某一个模块彻底重写。另一种是完全读懂原作者的精髓,通过在最契合的部位,做一些微调,或者通过插件机制辅助加上少量的的内核改动支持的方式完成。

曾经,宣传自身研发实力的标志就是完全重构XXX开源,整个XXX层都被重写了,大幅提高XXX,云云。然而时间一长,大家就会慢慢发现。新写的模块和开源无法兼容,开源生态的很多插件由于内核的机制被重构过,不work了,很多功能没有被完全兼容。重写的代码,没有庞大的社区用户测试和Bugfix,一旦遇到之前没遇过的场景就问题百出,需要像看护一个宝宝一样,小心试探着使用。

踩过那些坑之后,今天我们越来越清晰的意识到,在做开源项目的时候,大改特改其实并不难,真正难的是如何找到那个撬动地球的支点,高(深)手(得)出(精)手(髓),可能只需要在正确的位置改上10行代码,或者增加些逻辑就能获得巨大的提升,真正的关键,反而是找到那个位置。


当然,也不能说我们就不要或者尽量少的修改开源代码。相反要大胆的改,尤其是关键核心链路,能改,并且改了不出问题,还达到预期的目标,这是研发能力成熟的标志。关键在于,不要为了重构而重构,需要把目标的核心放在客户价值上。

举个简单的例子,开源有一个机器列表管理模块,可能节点数少的时候没问题,节点一多,运行就比较慢成为瓶颈,一种解法是整个模块重构,引入XX算法,重写几W行代码,完全自研XX技术,如何XX牛逼,达到XX效果。结果却是,很难和原先的模块完全兼容,有些功能丢失了,甚至查看机器列表的周边工具都不Work了。

另一种解法,深入到源码的原理中,其实核心的瓶颈很简单,开源用的链表保存,不支持随机寻址,找一台机器就要遍历,解决方法就是增加一种数组类型的保存方式,支持快速在大量机器中寻址。显然后一种方法代码切口很小,只是对数据结构使用的地方做些if else替换即可,原先所有能力都兼容对齐,效果可能也达到了重写整个模块95%以上。

后续在幻灭期、复苏期等等的可维护性,可持续发展性方面远远优异。当然这只是一个小例子,实际中更多的是遇到更复杂的Trade off。但是保持一颗坚信少即是多的心,是必要的。
 
经验五:
尽量保持和开源在面子上的一致性


既然是结合自研,在关键核心领域一定要有核心突破,同时对于确有需求的要敢于出手,对开源进行改造。但是突破不意味着需要把代码改的面目全非,保持在文件结构、风格、函数命名等面子上的一致性是必要的:

  • 非必要的情况下尽量保持开源架构的目录和文件结构

  • 尽量避免重命名文件、函数、变量,而是以新增的方式代替

  • 大规模的修改是可以的,但是尽量以增加的方式,比如增加文件、函数、逻辑,而不是把原来大段的文件、函数、逻辑删除掉

  • 大部分的开源都是设计了扩展能力的,结合扩展能力辅以内核中增加少量代码配合。而不是简单暴力的大段重构

  • 对代码的小修改尽量保留原有的逻辑,而不是删掉重写


如果是C/C++,推荐用预编译宏的方式修改代码


这里命名方式都是有讲究的,公司名 + 模块 + 自研的特性,这种格式很容易在纷繁的代码中区分出,这一段修改是为了XXX特性而做的适配,极大的方便后续合并开源高版本时的工作。同时在后续的维护中可以方便、安全的裁剪掉某个特性。

当一个产品在数以万计的业务被采用时,每一行代码的修改都会变的极为谨慎,早期能够更多的留下信息就会显得极为宝贵。如果是Java或Go,那么只能使用注释的方式,但是还是推荐不要删除掉原有的开源代码,以注释形式保留。这样不但有利于之后维护,更重要的是,方便单独抽离出来,合入高版本开源Base Code中。

你问为什么?好吧,我是不会告诉你,我曾经遇到过多少次合并高版本代码时,一群人围着一段被修改过的代码讨论一小时,不知道这个长的和开源不一样的地方,究竟是应该保留开源高版本的修改,还是保留被前人改过的逻辑。什么你说git blame?你确定blame出来的那个提交者,你还认识?

事实上,过热期是一个产品发展最快的阶段,很多重大的突破(功能质变或者性能有数量级提升)都会发生在这个阶段,如果有幸经历这个阶段,好好享受它吧,不过尽量记住上面两点。


幻灭期(低谷期)



上帝欲使之灭亡,必先使其疯狂。快速增长的业务,大量自研的新特性。伴随而来的不只有成功的喜悦,更多的是接踵而来的故障,经常让人感觉新坑老坑无数危机四伏,越来越沉重的包袱,很快就会压垮团队,很多时候这个艰难的时期会伴随着人员的流失,导致雪上加霜。

运气最糟糕的时候,再遇到一个开源的隐藏坑被自研代码触发,然后重大故障之后,你会怀疑人生,甚至于觉得是不是路线是错误的,应该推倒重来,全部自己写,或者干脆只有纯净的开源版,一点都不要再做修改了。

想要熬过幻灭期,除了在前面的过热期,要保持克制,并打下良好的基础,最关键的是要适当做减法。比如合并同列项,砍掉低效的自研模块,拉齐开源版本等等。

经验六:
必要的时候开始做减法


随着产品的发展,自研的代码量和模块数,会快速膨胀,尤其是在过热期。显然,团队会因此背上越来越沉重的负担。解决方案也很简单 —— 减少自研代码量。途径有三个:

  1. 放弃自研而采用高版本的开源对应的实现。对于曾经开源没有的能力或者优化点,当时是通过自研实现的,而可能过了1,2年,开源也有了,这种情况就可以做合并同列项。相应的自研模块可以完成历史使命光荣退役,改用开源在高版本中的类似能力替代。

  2. 把自研的模块和优化回馈开源社区。有些自研团队,会对自己研发的代码严格保密,作为构筑竞争力的保证,这样做其实不是最佳的,适当的将已经成熟的代码回馈给开源社区既是有利于全世界的用户们,也是对自己最好的解脱,实实在在是一个双赢的局面。可以放下包袱投入下一阶段的攻坚。真正的壁垒,不是造一个火箭保密藏起来,而是始终有引领下一代潮流的能力,永远超越过去的自己更快、更好、更先进。

  3. 砍掉一些价值不大,却又严重破坏和开源一致性的模块和优化。在过热期,很容易上很多KPI产物的项目,遗留很多低价值能力,实际效果微小,远不值得为之维护一份特殊的逻辑,一定要坚定的砍掉,轻装上阵。


最终,在幻灭期要想重生,在做了以上三个动作之后,还剩下的自研代码,必然是。高价值(有质变),高适用(特别适合所在公司业务场景),有核心竞争力的,值得长期维护,并继续演进。


通常来说,团队需要一个契机,完成这个工作,开源社区大版本迭代是一个不错的机会,基于高版本的开源Base Code,将自研的代码,有序合入,在这个过程中,完成上面三点工作。

经验七:
稳定性是一个系统的工程


在幻灭期最容易遇到的另一个问题是故障频发,一来是过热期上了大量自研的改造往往埋了很多坑,二来业务规模快速膨胀,业务多了,自然难免有疏漏,加上版本可能越来越多,管理越来越困难。有些产品可能就死在了一个又一个的故障中,或者干脆畏首畏尾,不敢做新的研发和发布了。

然而稳定性是一个系统的工程,并不是说你不做研发和发布就能避免的,也不是说简单的提高代码质量,少写BUG就可以做到的。简单来说至少包括几方面:

系统架构:单租户还是多租户的模式很大程度上会影响出问题时候的爆炸半径,即使是多租户模式,有没有地域、可用区之类的容灾、隔离、甚至逃逸能力也同样重要。架构的设计决定了你后续能灰度的粒度,爆炸半径,故障恢复的耗时等等。

灰度发布机制:倒不是要什么特别流程,关键是如何灰度,以及灰度的节奏。比如你一个核心交易业务,极其敏感,稍有抖动就会被发现,那你分10批,每批间隔1小时,是足够的(因为有问题很容易就会暴露)。而有些依赖用户反馈的底层系统,你分10批,每批间隔一周,都不过分。需要根据你产品的特性和变更的影响灵活判断。

监控诊断:有些核心敏感业务,需要做到严格的发布监控,除了常规的QPS,RT之类。关键业务要达到一个什么程度呢,比如某个版本上线了,线上的error log数量有变化,或者出现一些以前没有内容,或者比如原来10%是啥,20%是啥,剩下是啥,新版本导致比例发生变化了,都要去仔细研究,找到背后的原因。因为日志的变化通常意味着逻辑的变化,如果是预期外的,往往这里面就隐藏了一个潜在故障。

严控配置变更:我经历过的数个重大故障,虽然都有代码坑在哪里,但几乎无一不是全局配置变更最终触发的。因为配置变更经常忽略或者不具备灰度的能力。往往一个配置的变化,会突然让线上所有系统走进一个从未被测试过的代码路径,导致灾难性后果。所以对配置的变更,尤其是全局配置的人工变更,尽量要避免。取而代之的是系统的自动化,常态化能力,系统自己根据情况按租户选择合适的配置,而不是人工对全局配置改来改去。


复苏期



经历过幻灭期的折磨,可能还能坚持下来的产品和团队已经不多了,能走到这一步,恭喜你,你负责的产品,是一个确确实实有业务价值,有生命力的产品。在这个阶段,虽然产品发展最快的时期,但是你的个人能力将得到最快的发展,因为你可以站在一个很不错的时间点,看到前人的成功经验,总结和纠正他们的失误。而你的想法还可以轻易的在海量的业务上尝试,正确的话会有很好的收获,错误的话,也会很快得到教训。非常适合年轻技术人成长和学习的一个阶段。

相比于过热期疯狂的跑马圈地,幻灭期的重新整顿,是一个重新出发的好机会,也是一个重大创新最容易出现的时机(随着早期的架构完善,研发人员和团队的成熟,业务规模的稳定,对技术突破的诉求和可以发挥的空间达到最高)。重点要做的,就是梳理清楚过去的成果,结合好开源社区的轨道,将两者并轨发展。以最大限度的减轻包袱,有充足的精力去看下一步的创新点,集中力量去获取新一轮的自研先进性成果。

经验八:
定期同步开源版本的升级是必要的


同步开源版本的好处是显而易见的,最新的Bugfix可以免去你总是排查了一周,最后在社区找到一个别人都已经修复了2年的问题,此外最新的Feature可以避免你重复造多少轮子。尤其是社区会有大量绿叶类型的增强,能够给自研的工作,带来很多互补的增益效果。另外最关键的一点是,你所有的研发工作,始终都是在最新的开源版本的基础上进行的,就意味着你也可以很容易的将你的成果给到开源社区,形成共赢的局面。

通常而言,有几种跟进开源的策略,一是实时跟进,开源的所有变更,实时跟进合并,二是大版本跟进,隔上1-2年,和开源差距大了,集中进行一波合并。这些都可以根据具体情况选择。

我个人而言,更倾向于小版本跟进的策略,通常开源社区都会有一个小版本发布周期,1-3个月左右会Release一个版本,一个小版本一般包含几十至多一百个左右的Commit。合并的工作量通常不会超过一周,由团队成员轮流负责的话,一个10人左右的研发团队,一年也就轮到1-2次。并且,团队的成员,时常看看开源改了啥,对自己的技术和视野都是一种成长,尤其是新同学,帮助更大。

经验九:
打造先进性和创新性也是有技巧的


之前的经验中,很多都是在分享如何和开源保持同步一致性,从而更高效的与开源协作。但是无论如何,作为一个自研项目,必须还是以要构筑自身的核心竞争力和先进性为根基的,至少也需要构筑与开源的差异性。

在先进性方面,开源结合自研的项目更有优势。会写的代码的,基本都知道面向对象的继承机制。开源项目,只要保持好和开源的同步性,那么很容易做到——开源有的我都有,开源没有的我也有。开源项目本身大多已经是TOP级的了,在其基础上,如果能够在某些点或者面上,具有独到的创新或者技术先进性,就很容易做到业界的领先了。虽然说,创新是一个很有挑战且没有固定套路的创造性工作,但还是有一些经验可以参考的。

技术的进步来源于业务:脱离了业务基础的技术就如同空中楼阁,很难有好的发展,相应的,业务的需求和挑战往往就是创新的发动机。举几个例子抛砖引玉:

  • 业务规模:最典型的可能是就是K8s,别看开源的K8s如火如荼,事实上超过几千的个节点的大集群,也没怎么玩过,而今天的中国,已经有很多企业需要管理数万节点,这就需要自研的时候,针对集群规模进行大量优化,甚至于重构,以支撑规模。

  • 稳定性:可能对于很多开源产品来说,99.99%的保证已经不错了,但是如果恰好你的业务,或者你所在的公司至少需要99.9999%,别小看只是多了2个数字而已,很可能从架构,到整体设计,都需要一次飞跃,才能达到。虽然实现困难,可是一旦成功,反而将成为一项先进的核心技术优势,轻易难以撼动。

  • 性能优化:对于很多商业产品来说,性能有时候可能有着远远超越成本降低的意义。比如你打开一个网页,是1秒加载完成还是2秒完成加载,其意义可能远超50%成本节约,因为可能三分之一的人在1秒之后会选择关闭网页不等加载了。流失掉这些客户的损失是无可估量和难以弥补的,那么优化这些性能,哪怕每快那么一点点,都将意义重大。这里面就很容易催生技术的革新。


与顶级商业产品对标:在很多领域中,开源产品往往受到方方面面的原因制约,在部分企业级核心能力方面比较薄弱,既然做自研,那么首先可以进攻的方向,就是这些已经被其他商业产品广泛证明有效的特性。虽然可能不是完全原创,但是依然可以通过依托开源的生态,做到在相应的领域中,具有独到的竞争力。举例来说,在开源数据库的基础上,增加异地多活的灾备能力,虽然并不是独创,但是在XXX生态圈中至少是领先的。

利用好内核和硬件的红利:无论任何应用软件,即使是基础软件,通常也是建立在硬件和Linux内核的基础之上的,近年来,随着需求的推动,无论从Linux内核到CPU/内存/网络/磁盘等硬件都在革新。举例来说机械硬盘->SSD的演进,就诞生了多级缓存,冷热存储等等很多创新爆点。网络的RDMA,DPDK等低延迟、高吞吐技术,大幅提升分布式系统的性能表现,给分布式存储带来很大机会。Linux内核,从早期的epoll到近年来的io_uring,也在一步一步的向前发展着。通常情况,当底层的技术发生革命性变化的时候,都是上层应用系统的创新机会。

稳定性和健壮性也是先进性:并不是所有的技术先进性一定是,性能有多高,算法有多牛。很多时候,部署架构和运维方式的升级,带来的稳定性和健壮性的提升,在实际业务中价值更大。前面说过,稳定性是一个系统工程,其中首要一点自然就是对产品的架构设计的要求。通常开源产品只是具备一定核心功能,而如何高效,稳定的将这些功能应用到生产业务中,往往也是自研的一个核心命题。


成熟期



一路走来,能够进入成熟期的产品,十之一二,除了运气,坚持也很可贵。大部分的内核研发,在这个阶段可能已经开始转向新的项目。确实,经过了前面若干阶段,产品已经日渐成熟,更多的是稳定运行和维护即可,对于公司和业务来说,这是真正的收获期,较少的投入即可获得持续而稳定的回报。不过研发工作更多的是艰苦的开荒,到了这种收获的季节,反而比较迷茫。确实此时将目光转向新一代的产品是一个正确的选择,但同时现有的产品也不是完全没有机会。

经验十:
服务化和产品化也是一大竞争力


除了产品的核心能力的突破和创新外,产品化程度则是一个产品成熟度的另一个标志。使用繁琐、与实际业务之间不匹配的问题在业务规模有限的情况下还可以弥补。但是当进入大规模复制推广期,矛盾就很容易凸显。所以当产品进入成熟期,在核心技术上已经很难有大的飞跃时,做好产品的易用性,可规模化复制能力也是一个很不错的方向。

服务化:运维这件事情,是一个繁重枯燥却又容不得半点差错的事情。这也是开源社区不容易触达的领域。上规模的服务化运维平台,除了具有资源弹性的优势,更重要的是自动化带来的稳定性,可以有效避免人工犯错的机会。最关键的是,对于客户来说,除了看看别人的经验分享无从积累踩坑的经验,而规模性的服务化,有点类似于买保险,大家抱团踩坑(一个人触发的问题,会在平台的加持下,自动帮助其他人都避免掉,而不是每个人都来踩一遍同样的坑)。这也是现在大多数云厂商Paas产品的一大卖点。

合纵连横:通常情况下,单个产品的能力范围总有局限性,在实际的业务中,需要组合一系列产品形成解决方案。显然,这不是开源所擅长的。有需求,而开源不具备,这就是自研的机会。联结好上下游,形成一体化解决方案,往往是进入成熟期的产品,新的增长机会。



最后分享一个小故事


我们经常听到一些成功的人在介绍自己的时候会很谦虚的说,自己只是站在巨人的肩膀上,才摘到了一些成果而已。



然而事实上,真相是:


有一半的人看了一眼巨人,觉得没什么了不起,于是开始在巨人的旁边重新挖地基、盖房子,要与巨人争高,结果只是又重复造了一个轮子。


剩下的人中,又有一半,没有找对肩膀的方向,迷失在了向上爬的路上。此时只剩下不到四分之一的人了。


然而,在爬上巨人肩膀的人中又有一半直接就地躺倒,他们也没错,因为已经是十之一二的佼佼者了,更何况每前进一步风险就愈增加一分。


果然,在剩下的人中,又有一半在尝试站起来的过程中,没有站稳,反而摔了下去,功亏一篑。


那些成功在巨人肩膀上站稳的人们,都看到了远处美丽的风景,不过大部分都只是流连于此。


最后,只有那不到 1% 的人能够不流连于前方的美景,他们倔犟而又坚定的仰起头,望向更高的地方,那里才是他们的心之所向,最终向上伸出了手。


确实,只是简简单单的站在巨人的肩膀上,摘了一些果实而已,看上去很容易。



推荐阅读





深度 | 阿里云李飞飞:中国数据库的时与势


专访阿里云王伟民:一站式全链路,阿里云向云原生数据库2.0跃迁


点击“阅读原文
了解阿里云数据库产品家族更多信息

浏览 132
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报