很久没写博客了,因为技术上的东西没什么好写的,很多问题网上仔细搜搜都能找到方案,自己水一篇博客和笔记无疑,不如直接记到笔记软件里。另外就是实在低估了用markdown写文章的门槛,还是更喜欢富文本编辑器,毕竟不是写数学论文,markdown显著的提高了写字的心智负担,尤其是不能方便的粘贴图片到文中这点,直接扼杀了写字的欲望。不过看看现在各种博客平台这么多年的颓废状态,以及最早写博客的平台yo2(托管WordPress)已经烟消云散,也没有什么选择。幸好VS Code现在已经比较强大了,用个插件也能解决粘贴图片的问题,就继续在Github Pages上用Jekyll将就一下吧。

接下来写一点这几年工作的思考内容,比较散,写出来而不是记录到笔记的原因,主要是包含了很多我对业界常见问题的个人回答,比较方便和他人引用讨论吧。

存储计算分离

这在几年前是个热门话题,甚至在厂里由高层发起,搞起了运动式的技术改造,我也有幸负责了其中一块,并做出了一些成果,但回过头看,其实也是有很多值得讨论的地方。

在存储计算分离这个概念之前,其实很多产品天然就是这个状态了,存储节点和计算节点是互相独立的,例如HBase,region server可以看做计算层,HDFS可以看做存储层,Phoenix可以继续往上面叠加更多的计算层。但用过HBase的朋友应该都知道,这样的架构,对网络的要求是相当的高,在我厂还在千兆网络时代,单机带宽被打爆导致响应缓慢产生的稳定性问题比比皆是,就是因为每多一层,就要在单机上额外产生一遍甚至多遍的网络带宽,例如,用户写入region server的1份入流量,由RS写入到HDFS三副本会再产生3份出流量,同时后台的compaction会读取1份入流量,再把compaction结果写回HDFS又是3份出流量,可以看到,流量被放大了许多倍。如果这个系统本质是个偏向存储场景的,那即使是25G网络(25Gbps双网卡,共50Gbps)依然是扛不住的,而业界对于100G网络才铺开不久(受限于主板PCI-E 3.0总共128Gbps的带宽),在向类似存储计算分离的架构演进时是一定要把这个问题考虑进来,并做好降级预案的(大概率要你的用户进行配合),QoS调度的价值也要更大。

存储计算分离的好处,其实就是合池(合并资源池)的好处,在上层业务形态不固定的情况下,通过共用一个大的资源池,提供一个很大的buffer,进而实现超卖,以及小部分业务的弹性需求。众所周知,超卖和弹性是否能做好,与所处的资源池里有多少空余资源是有极大关系的。100台10T机器提供1000T的quota的超卖空间,肯定是要远大于1台10T机器填充10T的quota的。传统超融合架构(存储和计算放在一个节点上)在这个场景是比较僵化的,至少资源调度上,从CPU、内存、存储三个维度,减少一个存储维度,做过binpack的朋友应该都能理解这个价值。

但如果业务形态可以比较确定,例如一些大数据场景,能比较清晰的知道CPU、内存、存储的配比,那存储计算分离的价值就大幅度降低,甚至会产生大量的额外成本,硬件成本、网络成本、研发成本,等等。所以存储计算分离依旧不是银弹,还是要根据场景来定。

而在云计算的时代,存储计算分离对于云平台自身是有极大的吸引力的,如上所述,云平台是很难预测上面用户的使用方式的,而且云计算主打的弹性扩缩,也需要一个较大的资源池提供足够的buffer,供一定体量的用户快速扩容,相对来说,存储计算分离带来的硬件成本,是可以比较好的转嫁给用户的,这也是很多云平台,本地盘实例比云盘实例还要贵的原因(当然也有运维成本在里面,本地盘实例很难热迁移),也是上云会比自建IDC成本昂贵的原因之一,用户买单了他可能并不需要的特性。

但在现在云原生概念被炒的火热的当下,新出现的产品基本都是存储计算分离架构的,例如TiDB例如Pulsar,同样也是因为需要用单一架构去支持百变的用户需求的结果,毕竟网络要求可以事先确定好,而超融合架构在后期的扩容不便的问题则近乎玄学了。但也不乏某些产品,打着存储计算分离的幌子,实际也就只是在前面架设了一个协议转换网关而已,呵呵。

上云

上云也是个轰轰烈烈的热门话题,经常被拿来举例的就是某某和某某国内企业,以及Amazon、Netflix是全部跑在云上的。但如同我上面所述,上云固然有其优点,但也会带来各种问题。

  1. 成本上升:云厂商支持了无比庞杂的业务需求,提供了很多很多feature,但并不是所有feature都是某个用户需要的,但研发成本肯定是平摊在了所有产品定价里的,这就显而易见的造成了,上云后成本增加的问题,很可能不管怎么改造,就是做不到“充分利用云的价值降低成本”这一点。
  2. 定制开发不便:企业的技术选型基本就体现了康威定律的威力,因此每个企业都有自己的一些特殊的地方,而一旦用上云产品,这些定制需求就要好好斟酌一下了,当然会有人说这些定制需求不合理,但有很多合理的需求,站在云厂商的角度也是极不好支持的,例如tracing这件事,改造下kafka把producer和consumer的轨迹信息都在kafka这侧存下来,显然比用户自己定制一个sdk让所有产品都用上,要简单的多,但也几乎不可能在云厂商的kafka版本里实现。
  3. 玄学的稳定性:云厂商的infra对用户基本都是黑盒,这没有问题,但云厂商的研发和架构师也都是人,也都会犯错,同时为了向前兼容,很多错误的历史债极为庞大无法消除,就会产生各种奇奇怪怪的稳定性风险。例如AWS的核心管控放在了us-east,于是美东一炸,就会有各种连带效应,国内厂商也不乏此类问题。由于云厂商无法感知上面用户的业务属性(某个云账号是开发测试账号,保障可以差一些多节约一些成本,而另一个账号是生产账号,保障可以多付钱),只能一视同仁,最后在出故障时的恢复也基本是乱序状态,做不到按优先级恢复,用户部署的时候也做不到针对性的优化,而云厂商考虑到自己库存等一系列因素,很多看起来必须的功能却有一些很奇怪的限制,例如阿里云的部署集有单个可用区20台ECS的限制,这对于稍微有点规模的企业基本就是没办法用的状态。

所以各路资讯中大肆宣传的上云案例相比,有更多默默无闻的下云案例和自建云(美团云、京东云、滴滴云、筹办的头条云),当然这种没有商业价值的案例一般也是不会大肆宣传的。

当然并不是说上云就一无是处,对于中小企业,或者研发只是公司的成本部门的情况,上云是一个不错的选择,因为自建的成本是要远高于上云的,实际上即使像Netflix这种上云标杆用户,也因为他家的视频业务属性,只是把业务系统几百个应用放到了云上,而核心的CDN系统,却是完全自建的方案。所以上云与否完全看取舍,如果因为某些政治原因要强行上云,最终只会弄的自己和云平台两边都不舒服。

消息队列和分布式日志

总是能看到很多关于Kafka、RabbitMQ的比较,但我比较认可的,是曾经在hacker news上看到的一条评论,大意是,两者其实是完全不同的产品,硬要拿来比较,就像要比较高铁和飞机一样不合适。

Kafka,包括RocketMQ、Pulsar、Pravega这样的产品,其实不算消息队列,而更接近分布式日志系统,提供了一个基于分布式系统的日志流给最终用户,像一个在NFS上的log文件,producer不断append这个log,单一的consumer不断tail这个log,如果要回溯历史数据,直接seek过去就完事,所以自然也能实现保序的需求。但这样实现的问题也很明显,tail的offset管理是要consumer自己去管理的,因此不能多个consumer共同tail同一个log,offset的在不同consumer之间同步的代价太高了,所以只能在一个topic中启动多个log文件(partition),然后启动对应数量的consumer,来提高消费效率,如果consumer要扩容,就要想办法把topic的partition数量提升上去。当然这里面也有一些取巧的方案,例如RocketMQ默认就是一个broker上topic有8个queue,每扩一个broker就新增8个queue,来略微简化queue扩容的逻辑。

而RabbitMQ,则是一个真正的消息队列,producer只发一个“注册好了一个账号”的消息给exchange,exchange根据定义好的规则发送给两个queue,发送欢迎邮件的consumer监听其中一个queue,赠送注册奖励的consumer监听另一个queue,各自处理各自的逻辑,consumer监听同一个queue可以理论上无限的水平扩容,就像java里多个线程消费同一个Queue一样自然,consumer扩容和消息队列本身是没有直接关联的。整个过程更注重一种on the fly的数据传递,数据持久化只是一种容灾处理。所以RabbitMQ用erlang实现几乎是一件水到渠成的事情,因为erlang的整套语言模型,包括轻量process和inbox机制,和这个场景是天然契合的。

kafka的崛起,是伴随着流式计算(streaming computing)崛起的衍生产物,流式计算需要的是分布式日志,因为虽然号称是流式,但各家实现的时候基本都是按window做小批次的计算,因此确定一个批次的范围就是一件至关重要的事情,而分布式日志天然适配这个场景,确定一个文件的起始offset和终止offset,这段数据就是确定的、可重复计算的,或者说可重放的。而消息队列天生就是不可重放的、无法保序的,套用到java的Queue、golang的channel,基本都是如此,因此也就和流失计算的需求格格不入,错失崛起良机。

自研新产品和消灭重复建设

简而言之,自研新产品的道路,是从少数几个用户场景开始做起,在这几个场景做到超过已有竞对的水平吸引这些场景的用户,然后逐步扩容适配的场景,同时对于不合适的用户不合适的场景,通过优先级控制等方式及时say no,集中精力进行发展,和竞对想办法错开赛道避免直接竞争。在早期是处于实现“人无我有,人有我无”的状态。例如研发一个新的对powermock友好的覆盖率引擎,超过jacoco,来吸引相关用户,体现价值。

而面向公司内部消灭重复建设,往往是和该场景里已经在用的用户做竞争,用户会自己用jacoco,对jacoco结果数据的分析、整合、展示,确实每个用jacoco的团队都要做的,从分析、整合、展示能力开始研发,降低其他团队的重复建设,体现价值。

研发效率和用户习惯

几乎所有中大型公司都会有人负责研发效率,一般都包括度量和提升两方面,毕竟CTO是要向CEO汇报“这么多研发同学干的怎么样”这个问题的。但研发效率是个很玄学的问题,毕竟研发心情不好要多写几个bug,心情很好则少写几个bug,这种事情很难度量出来,也就很难说明做一件让研发舒服的事情的价值。很容易发生的本末倒置的事情,是围绕度量数据,去自研一些产品,来替代业界著名产品。这些自研产品往往在刷高度量数据上非常有效,但实际是大幅度劣化了研发效率。例如一个用习惯了Github、Gitlab的同学,突然面对一个长的奇奇怪怪bug多多功能还不全的自研产品的时候,往往是效率降低而不是提升的,虽然这种降低往往度量不出来。