“微服务”的概念兴起于四五年前,近几年尤其火热,各大厂都在进行微服务化改造和微服务建设。最近一年来我们也参与了微服务化的改造大军,这里写下一些做微服务系统设计和开发时的切身感受。
01 微服务架构
说起微服务,不得不提那篇经典的文章,来自Martin Flower的《Microservices》,建议多读几遍。Martin Flower是敏捷开发方法创始人之一,《重构》《企业应用架构模式》作者,ThoughtWorks公司的首席科学家。微服务虽然不是在这篇文章中首次提出,但是它将发展多年的微服务架构进行了重要性总结,推动了微服务的流行。
Martin在文章中从多个方面详细阐述了微服务的概念,首先作者对比单体应用的架构,一个单体应用处理请求的所有逻辑都运行在一个单独的进程中,这种情况下,你可以在笔记本上开发、测试、部署都很简单,还可以通过负载均衡进行横向扩展,最后交付给运维团队。单体应用非常成功,但是越来越多的人感觉不妥,尤其是在云中部署的时候,任何微小的变更都要整体重新构建和部署,扩展的时候也需要整体扩展,不能进行部分扩展。这时候微服务架构风格就出现了,它提出将应用程序构建为一套服务,每个服务都可以独立部署和扩展,运行在独立的进程中,服务之间通过RPC调用进行通信。微服务的应用致力于松耦合和高内聚:采用单独的业务逻辑封装,接受请求、处理业务逻辑、返回响应,而且喜欢简单的REST风格,而不喜欢复杂的协议,最终实现敏捷开发。
微服务不是什么框架,也不是什么系统,只是一种架构风格。我们所使用的DSF(华为)、DUBBO(阿里)、Spring Clould(pivotal)等框架,在介绍中都称是分布式服务框架。这些分布式服务框架都是微服务架构必不可少的基础能力,微服务一定是分布式的。分布式服务的概念比较模糊,只是解决了网站的高并发问题,很多细节问题是微服务帮其明确的。微服务更加强调敏捷和健壮,强调服务的粒度,一个服务只需完成一个单一的、独立的功能,多个微服务组合完成相对复杂的业务系统,以满足需求。而且微服务注重借助于各种中间件进行业务解耦和提高性能,以及提高服务的容错性。
从上面的介绍中我们可以看出,微服务架构有很多优点。例如,服务的拆分将复杂问题简单化;每个服务由专门的团队开发,开发者可以自由选择实现技术,提供API服务;每个微服务都可独立部署,加快了部署速度;每个服务可独立扩展以满足需求。微服务不是免费的午餐,微服务架构也有缺点。微服务概念强调了服务的大小,但是服务变小不是最终目的,微服务的目的是有效地拆分应用,实现敏捷开发和部署;微服务应用都是分布式系统,需要通过RPC进程间通信完成服务调用,这样大大增加了系统的复杂性,因此必须写代码处理由于网络或者服务不可用等导致的调用失败问题,虽然一般框架都支持相关配置,但是在这种情况下微服务显得相对复杂些;在微服务架构的应用中,建议不同的服务使用不同的数据库,这种情况下,一个交易一般会调用多个服务,同时需要修改多个数据库,由于CAP理论和其它的一些因素,并不能实时地保证数据的一致性,因此不得不使用最终一致性的方法,从而对开发者提出了更高的要求和挑战;测试一个微服务架构的应用也是很复杂的任务,首先需要启动和它相关的所有服务(至少需要这些服务的stubs)。最后还是要强调一下,不要低估了微服务架构带来的复杂性,下面介绍一下我们如何应对这样的复杂性。
微服务架构给我们带来方便的同时,也引入一定的系统复杂性,最近几年随着基础设施自动化技术的发展,比如云平台、容器技术,再加上自动化测试与持续集成,减少了构建、发布、运维微服务的复杂性。因此我们的微服务团队应该依赖基础设施自动化技术构建软件,下图说明这种构建的流程:
在构建微服务软件时使用的分布式服务框架也拥有很多特性,这些特性提供了很多复杂问题的解决方案,比如异步调用、超时失败策略、故障隔离、健康检查、流量控制以及自定义路由等,这些特性大大减轻了开发者处理逻辑的负担,还有一些高性能、高可用的保障都增加了我们构建微服务的信心。而且经过多年的微服务实践者的摸索,也总结出了很多辅助运维系统,比如统一日志收集(ELK)、服务管理、服务监控和服务治理等,大大减轻了运维的工作,而且能让我们对复杂的微服务系统做到心中有数。
总之,微服务架构入坑容易,坑了呆得舒服难。各种基础设施和配套系统必须跟得上,还得有一个具有微服务特性的团队,才能真正驾驭得了微服务。
两个值得深入的话题:
1.微服务与持续集成
2.微服务与测试
02 微服务团队
上一节中说到更适合构建微服务应用的微服务团队,什么时微服务团队?说一个比较现实的问题,当我们做服务拆分的时候,通常管理都会集中在技术层面,涉及到UI团队、服务端业务逻辑团队、数据库团队、测试团队和运维团队。当采用这种标准对团队进行划分时,即使时小小的变更都将导致跨团队项目协作,从而消耗时间和预算审批。这就是 Conway's Law :
设计一个系统的任何组织(广义上)都会产生这样一种设计,其结构是组织交流结构的复制。
——Melvyn Conway, 1967
Melvyn Conway 的意思是设计一个系统的团队结构将决定了一个软件系统的结构,将人员划分为 UI 团队,中间件团队,DBA 团队,那么相应地,软件系统也就会自然地被划分为 UI 界面,中间件系统,数据库。而一个高效的微服务团队会针对这种情况进行改善,微服务团队需要是一个全栈的团队,跨职能的团队,应该包含整个项目周期的所有技能,前后端开发、UI设计、测试、构建部署、上线与运维都是必须技能。
微服务团队除了熟练的业务逻辑开发之外,还需要有DevOps能力、服务的快速构建、良好的团队文化:
- 首先DevOps能力是保证持续交付和应对复杂运维问题的动力之源,运维人员不懂开发人员的服务设计和调用流程,开发人员不懂产品环境和整体配置结构,开发和运维的融合才能更好的应对微服务架构。正如下面这句话所说的,“你构建,你运维”。
You build it, you own it. – Amazon CTO
因此,需要打造DevOps文化,将运维作为需求提前注入到开发流程中。
其次保持服务的持续演进,使服务能够快速、低成本地被拆分和合并,以快速响应业务的变化。
同时要保持团队和架构的对齐,微服务看似是技术层面的变革,但它对团队结构和组织文化有很强的要求和影响,识别和构建匹配架构的团队是解决问题的加速器。微服务团队的另一个关键点是持续改进、持续学习和反馈,只有保持这样一个文化氛围,微服务架构才能持续发展下去,保持新鲜的生命力,进而实现我们的初衷。
专业的微服务团队,为微服务保驾护航。
03 项目的微服务设计实践
我们在各种基础设施不健全的情况下,匆忙进入了微服务改造的深渊。原来的单体应用按照功能拆分成了下面的结构:
拆分之后,模块突然变多了。将内部业务逻辑和原子功能拆分出来,实现了部分功能的复用,并且对外的接口按类型区分到不同的模块中去了,方便了使用,但是增加了管理的难度。
从上面我们对微服务架构的了解,一个微服务系统会有大量的服务组成,服务之间的层次关系依然是存在的,但是调用顺序上的要求会有所降低,只要满足严格的上层调用下层即可,在某些简单的业务功能上允许跨层调用。
上图是三种架构(集中式、分布式、微服务架构)的形象化展示。可以看到微服务的一个状态,乍看起来有些混乱,如果还按照以前的管理方式维护项目的话,工作量会成指数级增长,人力所无法胜任的工作。
这时候就需要依赖一些分布式服务框架,并建立一些辅助运维系统:
- 首先服务之间远程调用,服务的注册和发现机制必须有好的分布式服务框架(类似dubbo)支持,方便做服务之间的调用配置管理。
- 部署的服务进程增加以后,对应的日志文件也会增多,这时再使用ssh到服务器上查看日志,这个工作量也是可想而知的。我们对业务日志做了规范化约束,然后使用ELK技术栈搭建了日志集中化管理系统。
- 为了能够对线上的所有服务有个全局的把控,我们根据注册中心的数据搭建了服务的管理系统,展示各个维度的统计数据,例如,每个主机上多少个应用,每个应用提供了多少个服务,同时又消费了多少个服务等等。还有一些针对服务的约束规则的告警信息,例如某个应用消费的服务,却没有服务提供者等。还有一个整体的应用之间的依赖关系图。
- 为了优化系统结构和排查问题,搭建了服务的监控系统,这个是依赖分布式服务框架打印的预统计日志的,通过这些日志分析得到一个整体的服务监控功能,例如每个服务的响应时间、错误率等。还有调用链跟踪功能,排查问题时使用它来跟踪请求信息。
- 服务治理功能用来及时地对线上项目做一些调整,例如限流、降级、负载均衡、超时、路由、隔离容错等。
有了以上这些系统辅助,我们的微服务化改造之路平坦了许多。
在进行微服务化改造的时候,接口的设计上遇到了一个问题,服务之间的调用是通过私有协议进行的RPC调用,这种调用中如何描述服务响应成功之外的其他异常情况?
1.采用Exception类,定义各种异常类来描述异常情况。
2.采用错误码+错误描述。
其中方案1采用的Exception类,是我们平时java中定义进程内部接口最常见的方法,这种方法的优点是,能够使调用接口的一方的代码更简洁,通过try...catch来处理,如果不需要处理的话,在方法上声明直接向上抛出即可。缺点是跨语言开发解析难度大增,而且日志中异常堆栈很多。方案2采用错误码的形式恰恰相反,缺点就是每次调用都要判读是否调用成功,增加代码中的if语句。
针对这个问题,希望各位实践分布式服务或者微服务的大神给出你们的实践方案,疯狂讨论起来。
04 结束语
上述三节中讲述了我们在构建微服务的经历和一些感想,给其他准备实施微服务和正在实施微服务的团队以参考,写这篇文章的另一个目的就是希望能起到抛砖引玉的作用,希望能和其他团队进一步交流。