转载:http://youzhixueyuan.com/the-principles-and-steps-of-distributed-architecture-system-resolution.html
我以淘宝技术架构演进为例,淘宝从一个大系统工程向分布式架构演变过程,你就能很清楚的知道为什么要需要进行应用拆分。
1 人员的角度。
维护一个代名工程Denali的百万级代码怪兽(虽然物理部署是分离的),从发布到上线,从人员的角度,百号人同时在一个工程上开发,一旦线上出问题,所有代码都需要回滚,从人员的角度,也基本忍受到了极致。
2 业务的角度
淘宝包含太多业务:用户、商品、交易、支付…等等,所有的代码早期都在denali一个工程里,代码已经严重影响到业务的效率,每个业务有各自的需求,需要给自己应用部署,各自开发需求。
3 从架构的角度
从数据库端oracle数据库集中式架构的瓶颈问题,连接池数量限制(oracle数据库大约提供5000个连接),数据库的CPU已经到达了极限90%。数据库端也需要考虑垂直拆分了。
4.急需走向一个大型的分布式时代,率先需要应用拆分。
1 )首先工程代码垂直拆分
把整个工程代码按照业务为单元进行垂直拆分。
淘宝按照业务为单位拆分成了类似这样的系统:UM(UserManger)、SM(ShopManager)..等等几十个工程代码。
2 )应用服务拆分
按照业务为单位,把所有调用相关的接口以业务为单元进行拆分。
比如,一个店铺系统,需要访问一个用户的头像的接口,用户头像的接口是独立部署在用户中心(UIC)这边的集群服务器上的。随着拆分的进行,淘宝的业务接口中心就变成了:UIC(用户中心服务)、SIC(店铺中心服务)等等以业务为单元部署的集群。
最终就演变成下图,按照业务为单位拆分和部署服务,用户中心、商品中心等:
总之,系统拆分是单体程序向分布式系统演变的关键一步,也是很重要的一步,拆分的好坏直接关系到未来系统的扩展性、可维护性和可伸缩性等,拆分工作不难理解,但是如何正确拆分、有什么样的方法和原则能帮助我们拆分得到一个我们理想中的系统:高可用、可扩展、可维护、可伸缩的分布式系统。
以下主要再从拆分需求、拆分原则和拆分步骤谈起:
1、组织结构变化:从最初的一个团队逐渐成长并拆分为几个团队,团队按照业务线不同进行划分,为了减少各个业务系统和代码间的关联和耦合,几个团队不再可能共同向一个代码库中提交代码,必须对原有系统进行拆分,以减少团队间的干扰。
2、安全:这里所指的安全不是系统级别的安全,而是指代码或成果的安全,尤其是对于很多具有核心算法的系统,为了代码不被泄露,需要对相关系统进行模块化拆分,隔离核心功能,保护知识产权。
3、替换性:有些产品为了提供差异化的服务,需要产品具有可定制功能,根据用户的选择自由组合为一个完整的系统,比如一些模块,免费用户使用的功能与收费用户使用的功能肯定是不一样的,这就需要这些模块具有替换性,判断是免费用户还是收费用户使用不同的模块组装,这也需要对系统进行模块化拆分。
4、交付速度:单体程序最大的问题在于系统错综复杂,牵一发而动全身,也许一个小的改动就造成很多功能没办法正常工作,极大的降低了软件的交付速度,因为每次改动都需要大量的回归测试确保每个模块都能正确工作,因为我们不清楚改动会影响到什么,所以需要做大量重复工作,增加了测试成本。这时候就需要对系统进行拆分,理清各个功能间的关系并解耦。
5、技术需求:
1)单体程序由于技术栈固定,尤其的是比较庞大的系统,不能很方便的进行技术升级,或者说对引入新技术或框架等处于封闭状态;每种语言都有自己的特点,单体程序没有办法享受到其它语言带来的便利;对应到团队中,团队技术相对比较单一。
2)相比于基于业务的垂直拆分,基于技术的横向拆分也很重要,使用数据访问层可以很好的隐藏对数据库的直接访问、减少数据库连接数、增加数据使用效率等;横向拆分可以极大的提高各个层级模块的重用性。
6、业务需求:由于业务上的某些特殊要求,比如对某个功能或模块的高可用性、高性能、可伸缩性等的要求,虽然也可以将单体整体部署到分布式环境中实现高可用、高性能等,但是从系统维护的角度来考虑,每次改动都要重新部署所有节点,显然会增加很多潜在的风险和不确定定性因素,所以有时候不得不选择将那些有特殊要求的功能从系统中抽取出来,独立部署和扩展。
下面是拆分代码过程实践经验:
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即可。
3). 拆分entity
很多在业务代码上都会共享entity类,通常没法也没法把entity类分门别类,最简单就是把所有的entity类放到一个module,谁需要谁依赖的原则。entity类也没有太多jar依赖和业务依赖,也不会形成污染源。
4)公共业务
同commons和entity方法,不在复述,也被各个业务依赖,这种业务大部分是过渡性的,在未来迭代演进时可以通过其他方式抽象集成。
5)拆分业务代码
拆分业务是最痛苦的事情了,这个要看原来的代码整洁度和互相依赖程度,一般采取2中方法:
选择哪种方法,可以根据代码整洁度和互相依赖程度,如果代码很整洁且相互依赖较弱,可以采取前者,否则就采取后者。
6)拆分微服务
有了以上的拆分基础,可以在合适的业务迭代将各个微服务module迁移到不同的代码仓库,完成进一步隔离管理。
业界开源微服务架构框架提供了微服务的关键思路,例如 Dubbo 和 Spring Cloud(请关注后续文章,我会详解区别和优劣势)
Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合)。
Spring Cloud是一个微服务框架,相比Dubbo等RPC框架, Spring Cloud提供的全套的分布式系统解决方案。
微服务架构是互联网技术发展的必然结果,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。
1.明确拆分原则和拆分需求。
2.梳理出业务模块和之间的依赖关联关系。
3.按照业务为单位,拆分实体、以及应用工程单独部署。
4.按照业务为单位拆分应用服务,避免环形依赖和双向依赖。
5.抽离出公用的接口、实体,以及服务单独部署为公用服务。