@gaoxiaoyunwei2017
2019-06-24T15:10:33.000000Z
字数 4625
阅读 594
白凡
分享:孙立伟
编辑:白凡
讲师介绍:大家好,我叫孙立伟,昨天刚从北京到的。我先介绍一下我自己,我个人从业大概二十几年,基本上以Java为主,之前在电信行业工作过一段时间,电信行业是以C++和Java一半一半的背景。大概5年前转到互联网行业,之前在去哪儿网,现在在饿了么。饿了么被阿里收购,现在是阿里集团的一个子公司。
刚好这场会赶在双11前,我个人也是头一次参与双11,如果以后有机会可以和大家分享一下。
我这次分享的主题是可拆分微服务的单体应用,这个架构缘起可能从现实角度出发是被业务驱动出来的,一会讲的一个案例是我参与的业务系统中的案例,我在饿了么的头几年时,业务系统的发展特别的快,频繁会有不断创新的项目出来,但可能过一两个月没了或者是一直维持在一个地方而没有进展,这种情况非常多。
当时在北京,饿了么研发中心刚建立起来,对于人才的需求是非常迫切的,但是客观原因导致我们也没有很足够的资深的人去参与这个业务的开发,基于这个背景,经过了大概1-2年的业务系统迭代以后,我们觉得应该要有一种姿势解决应对快速发展、快速迭代的场景。当然讲这个业务系统的架构和DevOps 国际峰会现在的DevOps可持续交付之间的关系,我个人觉得还是比较紧密的,因为我们在做架构的时候,实际上也是以可持续交付的理念为背景思考我们的软件系统应该怎么去做能够可持续交付,能够应付快速迭代。
微服务谈了好几年了,基于叙述完整性,我简单说一下,微服务,我的理解应该是在一个业务系统中划分出多个业务的子域,每个子域是独立的,但现实达不到这种状态。微服务是相对松耦合的,都需要独立部署和运行。子动脉之间的通信是轻量级的,从微服务定位讲不限制你用任何协议,你用RPC、OPC都可以,关键在于轻量,对比起来,什么是不轻量的?目前我能想到的一个例子是很早以前被淘汰的一个技术,相对重要的一点可能是叫SAP的协议。既然是一个业务发生子动脉,最终分布式状态一般是采取集中式,而不是强一致性。
在饿了么也差不多按照APP ID的维度,都会被分配唯一APP ID,可以把它认为一个微服务,他们在互相协作的时候,你的运维层面、基础架构层面需要一系列支撑,才能让你的微服务运行的更好,在服务治理方面大家都熟悉Dubbo,这些问题都是问题子集群的发现和接口的垄断的问题。这么多节点和集群在跑,这么多业务在跑,怎么监控是需要服务式的监控系统。从业界来看,开源领域有一个OPEN CHOSEN的标准。
饿了么对外需要API网关,可以有两个网关,一个是保证连接的冗余,包括基本的SSI的卸载。严格意义上可能叫负载均衡,在负载均衡之后一般还需要一个网关,可能贴业务更近一点。每个端类型不同,你可能需要把你业务系统内部的不同的API通过组织针对每个端做一个API向外做的动作,需要这种网关的支撑。
还有自动化测试、部署系统也是很关键的,我们现在做的是自研的。第五点的集中日志管理也是非常重要的,那么多节点,业务系统的日志没有集中管理,根本没办法排查问题,所以要实现微服务的话,我的观点是需要很多的基础架构的支撑,而且还需要很多的精力去对你的业务进行合理力度的划分。
这是应对创新类项目,创新类项目开发周期和时间是满足不了业务的发展需要,所以我们经常面临的现实情况就是一个月能不能上线,一个月不光是开发时间,还有测试时间,我们之前做的智能取餐柜系统还需要第三方厂商联调的问题,微服务带来的好处很明显,业务系统刚开始的时候,不管是现实的人力资源也好,还是你一开始的基础架构支撑也好,可能各个不同的公司,腾讯这种公司可能很成熟,很多中小企业可能就直接上阿里云、腾讯云了,对于这块微服务的支撑,其实相对也没有办法,因为这是一般性的支撑,针对你自己的业务系统来做运营的话还是需要费很大功夫。
我们觉得微服务这个东西,Martin Fowler是DevOps的资深专家,当你的系统复杂的时候,当你复杂的程度还不足够复杂的时候,你千万不要考虑微服务,基于刚才叙述的微服务带来的架构的复杂性和你维护的成本性。
第二个观点是连你单体应用都构建不好的话,你凭什么觉得微服务能够解决你的问题,任何业务系统的迭代,生命周期一定是从单体应用开始的,不可能一上来就是微服务,当你公司架构成熟以后,实际上你可以用API可以干很多事情。对于现实开发团队来讲的话,说一些具体的体验的话,说白了就是你的代码放在几个户,分几个API部署,这是具体层面的事情。在具体层面来讲,一开始当你体应用的代码维护在一个数据库,只要DevOps和自动化程度足够,实际上可以做到自动维护的,我的一个数据库可以部署多个ID,当然带来的好处从开发层面讲,一个数据库维护起来显然比多个数据库成本要低很多,各个模块很容易处理。在一个数据库的时候,你的成本相对就会比较低。
当你的应用从单体应用开始的时候,我们当时在思考一个问题,就是说很自然思考我的单体应用长大以后怎么拆的问题,我怎么去拆它?所以我们想到的解决方案就是说这是很容易想到的东西,你的应用是单体还是微服务,作为独立应用领域,你的系统是否足够模块化,这个是任何软件系统的追求,我想从软件工程角度讲或者软件开发学科讲的话,很多软件工程或者软件开发术语都从现有平时都是从建筑行业来的,在传统行业里面早已实现模块化,造一辆汽车的零件都是各个厂商产的,我们在软件系统开发的时候,实际上本质上要关注你的软件系统够不够模块化,模块和微服务是类似的,模块足够了,说简单一点,你分布式系统追求六个字,高内聚低耦合,这是在开发过程中经常要思考的问题,你的业务足不足够内聚,你对别的依赖是不是足够的低耦合。所以,我们觉得当你单体应用足够模块化,我们就能自动化维护。
新零售的TMS是新的项目,这个架构经过验证以后大概的思路得到了比较好的验证,这个想法和规划后续还有很多发展。新零售TMS我们在开发的时候有六个子系统,但是我们用一句话搞定了,Shard多活是基于你某些业务字段从业务层面已经分片了,包括实现异地多机房多活的架构。实际上我们这个应用一开始就是这样子,就两个4C8G体系,两个机房就上线了。
我们实现这个架构的几个关键的技术要点,就是我们在利用了很多现在的一些新的开源库的能力或者新的构建工具的能力,
一个是Spring Boot的自动化配置机制,然后就是Gradle的依赖管理和强大可编程脚本能力,我们把系统之间的依赖通过Gradle有比较好的控制。
第三就是你的微服务,在你要向对外暴露各种HTV服务的时候需要有一个网关的,这个网关,我们内部也是我们团队研发的全自动的API网关,可以自动给你部署服务,相当于自动随着你的节点部署。
这也当然依赖自动运维构建系统足够自动化,可编程,如果你的发布老是点来点去,实现起来会更困难一点。
在整个的业务系统的概念上,这个是一个模块开发的约定,我们大概就是这么想的,也很简单,也很容易理解,稍微有点不同的是可能接口的API有两个,一个是对外,一个是对内,你开发模块的时候,你需要遵循这种规则,包括模块之间开发的约定也稍微比较多一点,比如说你模块的依赖,你只能依赖其他模块的API,不能依赖它的实现包,包括你在模块开发的时候你要遵循Fail-fast原则,当然实现模块化的话,你需要对每个模块都有唯一的标识,实际上贯穿于所有的层里面,你的数据层等等都要需要标识。
模块化这个东西其实开源有很多解决方案,OSGI这个框架这两年没有人提了,它是模块化的东西,是用了开发工具。模块化的解决方案是很严谨的,你如果能控制得住,模块化的可能性质很高的。
现实世界中,像Java9出的模型也可以,我们当时想这块怎么约束,用什么技术搞,现在变成了我现在讲的这样子,因为这两个东西都不适合现实团队,现实团队意思就是说能不能有足够水平的人控制住这些框架,所以我们相对是比较山寨一点土法模块化,自己搞了一个小的模块化的约束。
Fail-fast其实跟这个关系不是特别大,但这也是我们内部一直遵循的工程实践原则,不管任何系统,不要容忍任何错误,不要犯对你业务不可处理的业务,一旦拆开了,你的异常的甚至你现在的熔断都是依赖各种异常机制起作用的,这个我不展开谈了。
这个不要try-catch,确保进程不退出或崩溃,在开发的时候依赖现有机制让你的错误更明显的暴露出来,只有把错误暴露出来才知道怎么去做错误的保护,这是一个正向的逻辑关系,在现实开发中,我们遇到很多业务系统开发团队的同学为了让程序看起来正常,大家做了不少容忍的事情,这实际上对业务系统的开发很有伤害,到时候拆也好重构也好这都是技术的坑,所以我们一直强调这个事情。
到最后我们的架构就这样子了,每个业务模块有API和WEB模块,下面的数据库一开始的话会分表,按表明的空间做这个事情,你的业务系统,你这几个表不要跨表访问。
给大家大概看一下这是我们基于Gradle插件写的东西,基于刚才的理念,架构这个东西其实我的理解就是一种约束,要么靠人约束,要么靠系统约束,能够用系统约束就约束住了,我们的Gradle插件就是约束这种风格。
Gradle插件,把project依赖都模块化的管理起来,然后做自动化的东西。每个系统开发的时候,当它引入其他业务模块依赖的时候是用我们emodule标注做的,不要引入第三方依赖性。通过这种方式地大家可以看到这是tms的模块图,看起来就是这样子,一个业务模块,一个-API子模块,每一个人负责一到多个子模块的开发,一开始的时候,数据库拆不干净,所以一开始数据库也是拆不开的,你要有一个架构约束它,你外来是可以拆开的,你要两个准备,一方面准备随时没有了,一方面随时准备把它壮大,在这个地方模块的样子就是这样子。
相对实现技术我个人觉得比较简单,里面有很多细节没办法展开谈,但是我们的做法在实现可持续交付的系统里面收到的效果是很好的,就好像我们刚举的例子,按照正常开发的话,时间需要翻倍才能上线,我们一开始准备好以后,现在新零售的系统慢慢在壮大。要差异的话,这个地方我没有办法暴露出更多的细节出来,大家可能基于可理解原因有一些公司内部安全问题,但是在子模块拆分里面,我们现在的这个东西是一个大的GIT库,但是可以通过脚本的方式。Tms-main只是一个Spring Boot的入口,这会做第三方服务的基本配置,这是集通用的,你的CI文件可以选Main、oms,一个git库可以实现这个,如果当前git有4个ci文件的话,每个API可以对应每个文件,可以是运行随时跑了。
实际上我们这个团队,主要还是API everything,我们希望从API开始向上的东西全部自动化,自动化的目的不是让我们的运维不管它,而是为了更好的管它。我们说的饿了么网关服务,我们内部的业务网关,我是全自动的,你不需要自己布,我们通过内部发布系统紧密结合,知道你什么时候构建什么时候部署,我可以自动为你对应外部容器,这个时候我们发现很多业务系统就懒得去管它了,全自动有的时候会给人带来一些误解,包括DevOps,就是什么都不用管了,这个我觉得是错误的,只是为你省掉很多人力的东西,让你更专注于关注你的业务系统,更多的是你要看你的监控、集群状态、告警,而不是什么都不管。
我大概今天分享的就是这么点经验,我不知道大家有什么问题要提。