说到微服务就不得不说拆分了,服务拆分要有一些指导依据。
拆分依据
微服务的理论知识有大量的分享,这里是我对微服务理论基础认识的一些观点:
- 小,且专注于做一件事情,即满足单一职责原则。 关于单一职责可以阅读我的另一篇文章《软件开发中的单一职责》
- 运行在独立的进程中。
- 轻量级的通信机制,RPC或者HTTP或者MQ。
- 松耦合,独立部署。
- 康威定律:设计系统的组织,其产生的设计等同于组织之内、组织之间的沟通结构。
服务拆分依据结合上面的理论基础充分考虑了以下因素:
- 业务和领域模型
- 技术、业务量等其他因素
- 团队
业务应该说是最实在的,也是最好理解而且最容易把握的。虽然领域的有界上下文从理论上能指导拆分,但是万万需要拆分的不是一个全新的系统,而是一个在线上稳定运行了很长时间的,很多人一砖一瓦堆砌起来的,并且仍然在持续添砖加瓦,不管是桥梁,还是高楼,我们的目的是让系统运行的更健壮,而不是拆成七零八碎,所对于这样的老系统,用领域拆分需要结合团队现状,理论结合实际,事半功倍。
运行时隔离也是很重要的拆分依据,会根据一些具有特定功能的API单独拆分出来作为一个微服务,和其他微服务隔离,避免相互影响,避免一个老鼠害了一锅汤。比如一些文件上传一类的API,特征是响应时间长,对IO依赖比较多,其线程池需要特殊配置;比如多线程利用CPU来换取响应时间的等等。
对于康威定理,究竟是团队影响拆分,还是拆分影响团队,那就需要均衡利弊了。如果是因为拆分微服务,而拆分了团队,那势必会影响到团队的稳定性和团队成员的归属感,尤其是一个组建很久的老团队。反之,团队负责多种业务,也没有明显的职责区分,就要考虑是否拆分团队,明确拆分后的团队职责。
对于正在运行的系统,如何拆分和拆分为多大的粒度,事实上是不能有太过理论化理想化,更需要深入项目本身和该项目团队,了解业务,人和代码。不能是拆迁队,也不是修缮,应该是拆成各种形状的合理大小的积木。
孰重孰轻很难说明白,团队不一样项目不一样,实践就不一样,找到适合自己团队的方法。
拆分粒度
拆分粒度不应该过分追求细粒度,要考虑适中不能过大或过小。按照单一职责原则和康威定律,在业务域、团队还有技术上平衡粒度。拆分后的代码应该是易控制,易维护的,业务职责也是明确单一的。
拆分过程实践
在拆分过程不得不考虑的是业务在跑,砖在砌,不能停,而且拆分工作也必须得进行,过程上不能一部到位,必须一步一步走,也由于此拆分后落地上线也要稳妥。所以,过程上,我也采用了比较稳妥的战术,先拆分为maven module,然后在拆分为微服务可部署构件。这样的好处是,拆分过程不影响业务迭代,而且可以随意组合成单体应用和微服务app,并且还可以事先测试和验证拆分,最大限度的降低微服务化实施的风险。
下面是拆分代码过程实践经验:
1). 设计module骨架
module骨架的设计是基础,影响最终拆分结果,拆分成功的向导。按照技术,业务,部署打包,测试这几个维度来规划设计,下面是一个参考。
最终骨架模型:
root web app
webapp //war module,打包为单体war,整体部署
micro-services //微服务pom module
user-service
customer-service
order-service
other-service
api-gateway
biz //业务相关的module
entitys //所有实体类
biz-base //一些无法拆分的代码上有依赖的服务
biz-user //用户业务
biz-customer //客户业务
biz-order //订单业务
...
commons
async-framework //一部框架
utils //工具类
2). 拆分技术commons
作为第一步,先对整个工程按业务和功能进行了maven多module的拆分。拆分过程没有技巧可言,一步一步走,一步一个脚印。首先是分离出技术上的commons,感觉这应该是最好拆分的了,把相关的类重构到一个包里,在分离出一个module即可。实际过程并非如此,因为是老项目,这类commons也并没有想象的容易,经过很多人的添砖加瓦,并没有完全按照单一职责原则来砌,根本就是把业务特征的代码也放到了技术类代码中,不过review代码后感觉还好,微遵循的毕竟是少数,那就先重构代码,把业务特性的砖从类里移出去。
3). 拆分entity
很多在业务代码上都会共享entity类,通常没法也没法把entity类分门别类,最简单就是把所有的entity类放到一个module,谁需要谁依赖的原则。entity类也没有太多jar依赖和业务依赖,也不会形成污染源。
4). 公共业务
同commons和entity方法,不在复述,也被各个业务依赖,这种业务大部分是过渡性的,在未来迭代演进时可以通过其他方式抽象集成。
5). 拆分业务代码
拆分业务是最痛苦的事情了,这个要看原来的代码整洁度和互相依赖程度,一般采取2中方法:
- 新建业务module,加入基础module的pom依赖,再从源module复制和该业务module相关的代码(包括单元测试代码)过来,解决编译错误和单元测试错误,完成本业务拆分。
- 从源module复制一个新业务module,保持代码一致,先删除和本义务无关的代码(包括单元测试代码),再删除无关的pom依赖,解决编译错误和单元测试错误,完成本业务拆分。
选择哪种方法,可以根据代码整洁度和互相依赖程度,如果代码很整洁且相互依赖较弱,可以采取前者,否则就采取后者。
6). 拆分微服务
有了以上的拆分基础,可以在合适的业务迭代将各个微服务module迁移到不同的代码仓库,完成进一步隔离管理。