近年来,微服务与DevOps等概念不断热炒。两者实际上是紧密相联,又相辅相成,Docker、Mesos、Kubernates等技术方案的快速崛起,为微服务提供了更坚实土壤,使其得以更顺利地实施落地。 类似spirng-boot等技术的发展与大为传播,更是直接促进了微服务成熟化发展。本文将从与对传统单体架构服务与微服务的比较,介绍微服务,并在最后对DevOps与微服务联系做简单介绍。
单体架构(Monolithic Architecture )
假设你要开发一个Web后台服务,大部份开发者一开始肯定向于将,所有功能实现放在一个服务进程内。例如JavaWeb的开发者,往往会产出一个War包,这就是代码编译后全部结果,只要丢到tomcat或者jetty容器就可以运行。这就是一典型的单体架构。当然单体架构不是简单的代码堆叠,单体架构对于开发者而言,同样要求较好的模块划分与清晰的分层设计。
单体架构的好处明显:
物理架构简单。往往都是单机搞定。
IDE友好,一个project搞定。
易于布署。以JavaWeb为例,将War发布到tomcat或者jetty容器运行便可。
易于测试。不需要跨服务间联调。
易于扩展。只需拷贝应用复本,多实例运行,并引入ngnix/ LVS之类load balance方案,做好负载均衡便可以了。
然而随时间推移与业务需求的不断加重,如果继续延用单体架构开发,你的应用可能就会变得臃肿不堪,逻辑复杂,难以疏理。你的服务由原先的简单清晰,变成了一头难以驯服的怪兽。这时单体架构的就会带你陷入一场灾难:
代码难以理解,导致修改困难。各种代码逻辑盘根错节,你渐渐发现任何敏捷开发的尝试,都会失败。特别是那些不注重设计与架构分层的,急于应付业务的开发者,加入各种定制逻辑,其实很可能是在为自己挖坑埋地雷,让自己在后继开发过程中瞻前顾后,寸步难移,加大项目风险。往往一次修改都可能会临一次不小的重构。
服务启动时间长。 随着功能的增多与业务加重,越来越多数据加载、预处理工作在服务启动的时间进行。这就会导致服务的启动时长变长,甚至一次启动要花费十几分钟。同样在本地做测试时,每次测试会带动繁重的加载处理操作,严重影响开发效率。
服务的稳定性被拉低。因为功能模块逐渐增加、模块问关联逻辑愈加复杂,而任何模块本身bug与它们交互时带来的bug及出错情况(例如内泄漏)都可能会把你整个服务搞挂。
多人协同开发困难,不便于团队分工。单体架构服务往往更适合一个人去开发。正常情况下,单体架构服务在IDE中以一个project的形式存在,多人开发一个project时,每个人难以把整体掌握全局逻,存在风格冲突、引入技术重叠字、修改冲突等一系列问题。
硬件资源浪费。 往往在一个复杂的应用中,每个功能模块对物理资源的需求是不同的,某个模块可能是CPU密集型的,某个模块是I/O密集型的,某个模块将会造成越大的磁盘占用等等。将么多模块写在一个服务中,为应对水桶效应,必然会为该服务提供全面满足资源需求的服务器。
引入新的技术框架、语言成为奢望。因为换用新的框架,会面临极大的迁移成本,迁稳过程中故障概率也较高。而不做迁移的话,应用采用的技术栈越来越陈旧,难以跟上外界变化。
微服务 (Microservices Architecture)
为解决上述单体架构下的各种问题,微服务架构应运而生。与其构建一个臃肿庞大、难以驯服的怪兽,还不如及早将服务拆分。微服务的核心思想便是服务拆分与解耦,降低复杂性。微服务强调将功能合理拆解,尽可能保证每个服务的功能单一,按照单一责任原则(Single Responsibility Principle)明确角色。 将各个服务做轻,从而做到灵活、可复用,亦可根据各个服务自身资源需求,单独布署,单独作横向扩展。
微服务的好处在于:
每个微服务更小更轻。每个微服务的功能角色更清晰,代码更易于阅读与理解。就算人员变动,代码交接,也不会有太大的困难。当然由于服务变轻,各个服务的启动时间变得更短,单模块测试与开发的效率得到有效提升。
每个微服务独立布署,可以根据资源特点与实际需求,各自优化或做横向扩展。不同的服务的特点不同,为cpu密集的服务分配更多的cpu资源,为I/O密集型的服务分配更多的I/O资源,为负载压力较大的QQ买卖服务生成更多的实例. 如果结合使用Docker、mesos等技术,可以将实例的资源占用从服务器级别整机降到可动态分配的容器级别。
微服务的架构更适合团队分工。按我们以往经验,对于成熟的开发人员而言,一个服务归于一个特定的开发人员,是比较合理的,避免了太多的沟通成本与开发修改冲突。微服务架构对单体服务完成拆分后,自然可以根据每个团队成员不同的技能方向分配服务开发。甚至让技术架构与技术团队的组织架构相互贴近吻合,确保技术架构的稳定的同时,也提供团队之间的良好合作方式。
允许在不同服务上尝试使用不同的技术栈。由于每个服务较轻,所以对单个服务的技术框架的迁移成本并不大,也可以快速地尝试试验新的技术方案。如此,保证整体服务的技术水准与时俱进,不断提升。
架构分层、具备服务级别的复用能力。经过合理的接口设计与功能划分,很多基础模块不再专用的,而是成为一种可复用的通用服务。而对架构的分层,还可以做到不同级别的服务复用。
微服务不是万能的,更不是适用于所有场景的。在对微服务的尝试过程中,会发现某些场景下滥用或者错误设计下,也可能带你陷入灾难。
首先,微服务必然会带为你带来一些附加工作量与挑战。与单体架构服务all in one 思路不同,随着服务的拆分,服务开始形成多角色并各自独立布署,这时自然需要提供可靠服务间通信机制。而为了让服务能够完成自治,具备服务注册与发现功能的中间件开始引入。同时在微服务架构下如何保证不同服务间数据一致,也是我们在开发的常会遇到的挑战。也就是说,微服务架构在为你的系统提供更强大的扩展潜力的同时,依赖的外部组件也越来越多,系统复杂度增大。
调试、bug定位链路拉长。随着服务的拆分细化、服务分层层次增加,一次完整的请求,会涉及贯穿多个服务。开发者为了定位某个bug,经常会打开多个服务,同时查看后台日志;而为了重现某个现个bug,需要在测试环境下同时把相关服务环境准备好。总的来说,在微服务下,如果你想追踪某个完整的请求,你要花费的时间往往远大于单体架构服务的,这跟你的服务拆分细度与分层层数有关。
维护成本加大。 这也是我们在微服务实践中深切体会到的。一般而言,团队中的每个开发人员负责2~3服务(不包含没有变更需求的稳定服务)的开发与维护,已经相对饱合了。但如果拆分的过细,逐渐会发现,开发者更多的时间不是用于新功能的开发,而是服务的维护。大部份公司的对应用的部署,至少是区分线上测试和线上生产环境。而每一次的变动,就可会涉及服务的线下布署、测试、验证、上线操作。当服务数量较少的时候还好,但当服务数量多达近十个时,你会发现开发的大多部时间都是在服务维护工作上,抽不出身来搞其他开发。特别在大公司,需要配合pe,对应用做各种安全升级,环境升级等操作,虽然是一次性,但服务数量一多,可能一周之内一个开发一大部份时间都耗在上面了。这就是一种的忙得要死,周报却不知道咋写的处境。
微服务相对单体服务有更高的系统架构能力要求。这里就需要对每个角色有清晰的定义,从而可以合理地设计服务间库接口,在接口的层面确定每个服务的能力范围,如果接口太过通用,那你的服务的功能角色会变得不可控,如果接口设得过死板,服务会显得不够灵活。数据结构与二方库的设计,也在一定程度上决定了,哪些数据是对哪些服务可见。服务分层设计,是对服务的复用能力的保障。
上线风险。服务一多,上线就会变得麻烦。到正式上线之时,你很可能需要制定一个合理的上线方案。往往上线一个新功能,修改了多个服务,为保证上线期间的服务可用,需要按照指定顺序依次上线。 公共二方库的改写也要特别慎重,犹其是对二方库中多个服务共享的数据结构的修改,很可能一次修改,某个服务上线后,结果发现其他服务不可用了。
DevOps与微服务
根据上述结论,你会发现微服务架构并不完美,其代价也不小,特别是运维成本。正是DevOps相关技术的发展,使得这种代价变得越来越小。DevOps的核心思想是开发测试与运维部署一体化,作为一种可选平台,使得测试、布暑、运维等环节的工作自动化。对于微服务,可以设想有一种PaaS云服务,可以为应用开发者提种一种易用的方式用来微服务的布署与管理,包含服务的持续集成、服务升级、资源动态分配等功能。
从实现角度,举一个我了解的例子。此处的DevOps方案可看作三层架构。首先以容器技术为底层,这里有以Docker为代表的容器技术,做资源定义与隔离;第二层可采用Mesos,将硬件资源池化,作为统一的资源调度层; 第三层,是利用Kubernetes对微服务集群的管理。Kubernetes是一套开源容器编排系统,可以运行在mesos之上,它提供了一系列基本的功能,如应用的自动化部署,维护和扩展等。而Kubernetes与Mesos集成后,也可以实现Worker节点的自动扩展,所有Worker节点都是自动创建的,不需要用户手动干预。
总结
微服务现在已经十分火热。但经上述比较分析可知,在业务逻辑简单,资源有限的情况下并不一定适合微服务架构架,至少不应将服务拆分过细。而在应用微服务之后,必将引入更多的系统复杂性与维护工作,DevOps相关技术的引入可以减少不少这样的工作量与护成本。