微服务化的道与术


微服务的目标是提高响应能力,降低复杂度,让一切去中心化是微服务的最高宗旨。

背景

随着研发团队的项目工程的增加、代码量的膨胀、团队人员的增长,传统的单体架构的弊端越来越凸显,严重影响了业务的快速创新和敏捷交付。随行付在2015年底为了解决传统单体架构面临的挑战,先后经历了单体架构、系统模块拆分以及到现在的微服务化。

微服务架构并非银弹,它的实施过程中会面临很多的陷阱和挑战、几乎影响到整个软件开发的生命周期,稍有不慎,会导致整个微服务改造的效果大打折扣,甚至失败。

本公众号接下来的一段时间会协同随行付架构部、随行付技术委员会等技术团队,结合随行付的一些实际经验,论证微服务架构实施的道与术。

微服务原因

为什么要做微服务化?可以从以下三个方面看为什么搞微服务。

  • 分而治之:减低复杂性
  • 分而用之:提高可重用性
  • 分而做之:提高开发效率

可以用变化的成本来衡量架构的好坏。而架构的目的是管理复杂性、易变性和不确定性,以确保系统在进化过程中,架构的变化不会对应用产生不必要的负面影响。保证业务和研发效率的敏捷、保证易变应用频繁响应市场需求而中台部分的影响尽可能的小。

10几年前,一提到架构,大多数人都会认为,架构=性能+高可用。随着现在的技术不断更新(Docker、人工智能、区块链等技术)、各类开源软件异军突起、DevOps理念深得人心、敏捷开发成为主流开发模式等等,性能和高可用已经不再是那么棘手的问题。应用如何可持续发展?产品如何能快速上线/快速迭代?可维护性、可扩展性、成本,开始慢慢成为很多架构师的关注点。而微服务架构的根本目的就是降低复杂性。

微服务前提

业务拆分

采纳微服务架构首当其冲的问题就是根本没有一个确定的、良好定义的算法可以完成服务的拆分工作。像软件开发本身,服务的拆分和定义更像是一门艺术。更糟糕的是,如果你对系统的服务拆分出现了偏差,你很有可能会构建出一个「分布式的单体应用」,一个包含了紧耦合服务的必须部署在一起的系统。这将会把单体架构和微服务架构两者的弊端集于一身。

自动测试

微服务一个明显的表象就是随着服务的增多,如果继续沿用传统的测试模式就会遇到瓶颈,为了保证高效的迭代,尽量做到测试自动化。通过自动化让测试也可以「一处编写,处处运行」、让回归测试日常化。

自动运维

当互联网发展到今天,业务要保持对市场变化的一个高效响应,自动化运维就是提升交付速度的一个重要环节。微服务拆分之后,每个服务都可以独立部署,不再是固定的时间段去升级,人肉运维已经成了阻碍进步的绊脚石了。

多维度监控

硬件环境、服务状态、系统健康度、服务调用情况、异常的实时告警以及生产事故的事先预警等等。监控在实施微服务过程中至关重要。

道篇

微服务架构原则

技术栈统一

一定要使用成熟技术,充分考虑新技术带来的性价比。所谓成熟不仅仅是指有良好的社区活跃度、稳定性、节约开发成本、提高开发效率。更多的是指在随行付的技术普适度,说白了就是有多少人会,出了问题有多少人能hold住。

数据最终一致性

权衡好哪些服务是最终一致、哪些必须是强一致。而强一致的保障机制是什么?框架层和应用层相互互补来共同保障数据一致性。

服务无状态

对于无状态服务,首先说一下什么是状态。如果一个数据需要被多个服务共享,才能完成一笔交易,那么这个数据被称为状态。进而依赖这个「状态」数据的服务被称为有状态服务,反之称为无状态服务。那么这个无状态服务原则并不是说在微服务架构里就不允许存在状态,表达的真实意思是要把有状态的业务服务改变为无状态的计算类服务,那么状态数据也就相应的迁移到对应的「有状态数据服务」中。

场景说明:例如我们以前在本地内存中建立的数据缓存、Session缓存,到现在的微服务架构中就应该把这些数据迁移到分布式缓存中存储,让业务服务变成一个无状态的节点。迁移后,就可以做到按需动态伸缩,微服务应用在运行时动态增删节点,就不再需要考虑缓存数据如何同步的问题。

无状态通信,我们推荐使用Restful通信风格,因为他有很多好处:

  • 无状态协议HTTP,具备先天优势,扩展能力很强。
  • JSON报文序列化,轻量简单,人与机器均可读,学习成本低,搜索引擎友好。
  • 语言无关,各大热门语言都提供成熟的Restful API框架,相对其他的一些RPC框架生态更完善。

AKF拆分原则

AKF扩展立方体,《架构即未来》一书中提出的可扩展模型。理论上按照这三个扩展模式,可以将一个单体系统,进行无限扩展。

X 轴 :是水平复制。比如讲单体系统多运行几个实例,做个集群加负载均衡的模式。

Z 轴 :是数据分区,比如按照用户请求的地区进行数据分区,北京、上海、四川等多建几个集群。

Y 轴 :是微服务的拆分模式,就是基于不同的业务进行拆分。

举例:比如打车APP,一个集群撑不住时,分了多个集群,后来用户激增还是不够用。分析后发现打车APP上主要用户是乘客和车主,就将打车应用拆成了三个乘客服务、车主服务、支付服务。三个服务的业务特点各不相同,独立维护,各自都可以再次按需扩展。

服务拆分原则

单一职责

根据业务能力拆分。了解过面向对象的人应该都听说过「单一职责原则」,即每一个类都拥有一个职责。比如一个搬砖类,它既可以盖房子,也可以用来拍人,这时搬砖就拥有了两个职责,所以我们应该把类拆分为两个。微服务也是一个道理,我们应该做到一个服务是用来做一件事情的。

松耦合、高内聚

紧密关联的事物应该放在一起,每个服务是针对一个单一职责的业务能力的封装,专注做好一件事情。(每次只有一个更改它的理由)。

DDD

领域驱动设计。我们怎么按领域来划分服务呢?对,我们可以按业务进行拆分,比如财务、订单、仓库等等。这样我们的团队也可以拆分开来分别负责不同的服务,这样不仅项目清晰了,就连公司内部的组织结构权责分配也变得清晰了。

刚才从横向来看我们可以按业务领域拆分,纵向的呢?我们可以按层次进行拆分,比如最底层的数据层、基础设施层以及与业务无关的推送、邮件、短信等等。我们还可以按安全性、性能等需要特别关照的以及通用的功能进行抽离沉淀。

掌握了领域驱动设计不仅遵守了高内聚、松耦合还符合了单一职责原则,那还不赶紧拆起来?

演进式拆分

避免过早拆分。过早的划分服务,可能发展一段时间之后,服务的边界会和之前有所不同,导致很多跨服务的修改,代价越来越高,最终又变成了单块系统。所以一开始我们应该按照较粗的粒度进行划分,而是否拆分为更小的服务,应该由团队组织结构决定,如果团队对拆分后服务的维护成本更大了,那就应该放弃拆分更小的粒度。

服务开发原则

微服务的开发会面临依赖滞后的问题。在单体架构时,大家需要什么,往往喜欢自己写什么,这其实是没有太严重的依赖问题。但是到了微服务时代,微服务是一个团队或者一个小组提供的,这个时候一定没有办法在某一个时刻同时把所有的服务都提供出来,「需求实现滞后」是必然存在的。

例如:A要做一个商户黑名单校验,依赖服务提供者B。而B有其他更重要的任务,导致该服务的开发优先级排的比较低,无法满足A的交付时间点。A会面临要么等待,要么自己实现一个服务。

一个好的实践策略就是接口先行,基于契约,语言中立。服务提供者和消费者解耦,并行开发,提升产能。无论有多少个服务,首先需要把接口识别和定义出来,然后双方基于接口进行契约驱动开发,利用Mock服务提供者和消费者,互相解耦,并行开发,实现依赖解耦。

采用契约驱动开发,如果需求不稳定或者经常变化,就会面临一个接口契约频繁变更的问题。对于服务提供者,不能因为担心接口变更而迟迟不对外提供接口,对于消费者要拥抱变更,而不是抱怨和抵触。要解决这个问题,一种比较好的实践就是管理 + 技术双管齐下:

  • 代码通过Sonar扫描(随行付Sonar中定制208条规则)

  • 有80%以上的单元测试复杂度

  • 允许接口变更,但是对变更的频度要做严格管控

  • 提供全在线的API文档服务(例如Swagger UI),将离线的API文档转成全在线、互动式的API文档服务

  • API变更的主动通知机制,要让所有消费该API的消费者能够及时感知到API的变更

  • 契约驱动测试,用于对兼容性做回归测试

术篇

随行付微服务平台是以DevOps为理念,基于Spring Boot、Spring Cloud、React、React Native、容器技术、人工智能等,面向微服务应用的PaaS平台。实现基础设施云化、应用架构现代化和开发流程敏捷化。

平台核心及功能介绍

Spring Cloud生态时序图

如图上所示,我们围绕Spring Boot、Spring Cloud做了很多功能的扩展,让Spring生态在随行付能生长出更多的解决实际问题的「果实」,同样并没有盲目的造轮子。因文章篇幅,技术篇更多内容会在系列文章中着重介绍,会从各个中间件入手介绍随行付金融科技技术生态。