(研发中心 尹书伟)
刚接到要我到北京参加MPD大会通知的时候,我并不感到有多少激动与期盼,毕竟参加这样的会议多了,没有多少新鲜感。不过,每次去北京都有不同的体验。早先去的时候全国都没有高铁,大约得十六七个小时的样子。那时候经验也少,自然觉得学到的东西很多,什么都新鲜。那时候没有智能手机,一切要提前规划好,尤其是下了飞机或火车之后怎么走要提前弄明白,甚至需要在纸上记下来。后来有了高铁,有了智能手机,有了专车,随着互联网的快速发展,一切方便了很多。由于这些便利,我也不再需要提前记下怎么走了,到了以后再用地图导航就好了。然而这次去北京,刚到出站口,发现手机已被停机,这意味着我自己彻底与信息时代隔离,甚至想给自己的手机充值都不可能,因为没有信号没流量。晚上十点,身处一个完全陌生的地方,我不得不借助于原始的信息获取手段——问路。经历过这样的窘境,才更加意识到互联网飞速发展所带来的便利。上次去北京,体验到的是专车的便利,而这次让我体验了一下遍地五颜六色的共享单车的便利。
在到达会场之前,我已经在课程选择上拿好了主意,最感兴趣的课程有两个:大型系统、遗留系统的微服务改造实践和DevOps企业化部署之路。然而到达会场之后却发现课程有所调整,这两个课程都被换掉了。不能不说这是非常令人遗憾的。从讲师阵容上来看,应该还算说得过去,大多为互联网应用的一些牛人。我基本就在架构设计专属教室留了下来。
首先上场的是饿了么研发中心总经理史海峰,题目看上去很潮的样子:《当我们谈论架构的时候我们在谈论什么?》。一共分两个半场,都是套路,上半场上来提出什么是架构之类的问题,学员先发表自己的见解,然后史海峰给出他自己的看法,下半场也大致如此,只不过话题在什么是架构师这样的话题上。这样的课程,我自认为没啥干货,只不过是概念性的普及而已。消息队列、读写分离、最终一致、幂等、避免分布式事务、数据库主键与业务无关等,都只是泛泛而谈,我想大家也都对这样的理念不陌生。在谈到技术债的时候,很多人现身说法,每个人都认为自己在为前人还债,有个人甚至一还就是八年,最后不堪重负走人了。谈到技术债,深有感触,谁会愿意替人还债呢?然而不是每个项目都能够从头开始设计,即便是从头开始设计开发,只要在工期压力,领导压力下,欠债和偷懒是难免的。作为架构师应该施加影响,确保自己的设计得到广泛支持并得到不折不扣的执行,避免欠债。有时候故意欠债也是一种无奈的权衡,但必须记得及时还债,免得债务越积越多,最后只能全部赖账了。技术债要管理,首要一条自然是立即停止欠债。技术债只是个形象的比方,真实项目中什么是技术债受限于人们对软件开发认识的深度。很多人认识不到自己所欠的技术债,也许还自我感觉很美。有的时候意识到了,但是总是想着一下子把债务还清,一下子变成无债一身轻。必须意识到,债务是长年累月累积的,要想偿还必须付出巨大的利息,需摆正心态,做好长期还债计划,剥丝抽茧慢慢来。
微服务相关的内容换成了京东开放平台架构师王栋的《从京东微服务架构演进看互联网高可用架构设计》,临时救场临时准备,却比上一场的哲学式的空谈更能提起我的兴趣。当然,架构的演进最终到了微服务架构这一模式,自然少不了要强调一下适合的架构才是好架构。适合有多重含义,不仅仅指适合项目,更要适合团队,只要团队能驾驭得了,那就OK。
单体架构的最大劣势就是随着规模的增大,维护成本呈指数级增长。这一点我公司是有很多教训的。团队的持续交付周期变长,正确添加新功能变得非常困难,沟通、管理和协调成本必然显著增加。新人培养周期长,通常也是线上事故的制造者。可伸缩性差,难以扩展。
微服务架构使得巨大的单体应用被分解为很多服务,并且技术开发团队也得以完全分离,独立部署使得团队之间不再需要协调部署,这使得持续部署成为可能。而且每个服务根据自身特点得以独立扩展,独立决定运行所需软硬件资源。由于每个服务所支持的业务变得很小,新人所需了解的业务知识也就很少,培养周期大大缩短。由于隔离去耦,新人造成的麻烦被限定在可控范围。
关于如何拆分服务,王栋给出了一些原则,当然这些原则都是耳熟能详的,并不是他自己的发明。基本原则是低耦合,高内聚。一个服务只负责一件事情,可以独立部署。这个原则,其实在其它层面也成立,比如一个类应该只负责一个主要职责,一个方法应该只做一件事。横向按照系统功能划分,比如:接入子系统、消息推送子系统、客户端监控管理子系统等等。 纵向根据业务模块划分,清楚各个服务的职责边界是什么,比如:购物车、订单管理、单品页、实时库存等等。要坚持最小知识原则,一个组件或者是对象不应该知道其他组件或者对象的内部实现细节。不允许循环依赖,不允许从低层向高层依赖。根据业务发展情况不同,公司所处阶段不同,服务拆分的粒度也不相同。粒度越小,维护成本越大。服务多了以后又会产生新的混乱,因此服务一定要治理。
像很多人一样,我也曾经苦苦的寻找软件领域的救世良方,以为那些大牛手里有不为人知的秘笈,有了它一切都被完美解决。但我早已领悟到,很多大师也告诉过我,没有银弹。如果说有的话,还是那些人人皆知的原则,哪怕做到一条,比如说真正做到高内聚低耦合,那就不用担心自己的修改会对外部其他人造成影响。但是就这一条,如果你是以数据为中心编程,那就不可能低耦合,因为库表和SQL访问没有访问级别保护,所有的应用深度耦合到了一起。王栋也多次强调了这一点,一定不能直接访问其它团队的库表,一定是通过服务来进行访问,定义好接口。当然也不能有所谓的公共库表,比如档案,你会说大家都要用,所以都可以随便访问。但这会破坏高内聚低耦合原则,所以这也必须进行服务封装,通过服务来访问。其实封装和隐藏就能够极大地提升系统的稳定性和可维护性,怎么强调都不过分。实施起来的关键点是,让成员形成意识,像防贼一样防止他人侵入我的实现细节当中。如果人人都防范起来,那么每个人都安全了,耦合就会降低,系统就会稳定。
现在我们已经知道了足够多的知识,只是没有人会静下心去真正实践这些真知灼见。我们要知道的是一切并没有那么神秘,并没有那么难,你只要去做,不过如此而已。这也是《从bag of words 到alpha GO》题目的作者李睿的忠告。李睿基本上给出的都是干货了,从简单的算法到神经网络娓娓道来。最后,告诫我们,不知道感觉很神秘,知道了也就那么回事,然而,复杂的算法是要付出代价的,应该选择能够解决问题的最简单办法来解决问题。
最后,分享一下王栋提供的两个案例,这两个案例各有特点。反复强调的是数据库不是用来走量的,仅在必要时走数据库。另外,也可以看到,一提到数据量大,我们往往就想到大数据技术,这也是一种误区,关键还是要看场景,对王栋看来不到10亿那都是很少的数据。还有一点要注意,在第二个案例用了三层缓存,这也是有代价的,不能盲目模仿,如果我们没有那么苛刻的响应时间要求,一层缓存效果就很好了。
在第一个案例中主要设计是加入了缓存,数据库分表和读写分离。缓存是第一位的,替数据库抗住了量。在有数据更新时,不得不直通数据库,更新完数据库后,则同步更新缓存,异步更新Solr(目前京东开始准备用ES替换Solr)。出现问题降级,直走缓存。
在第二个案例中,由于数据量不大,因此就不用关系型数据库了,直接持久化到Redis中。虽然数据少,但是为了满足高性能高可用特性,不惜金钱,采用双机房双活方式,而且还要分片。加了三级缓存,第一级就是普通的分布式缓存,第二级在应用服务器所在节点增加缓存,避免掉了网络开销,第三级增加JVM缓存,避免了跨进程开销。这样的架构部署完了以后,任何请求的相应时间都在1-3毫秒之间,绝不会超过5毫秒。