为什么需要应用拆分
我以淘宝技术架构演进为例,淘宝从一个大系统工程向分布式架构演变过程,你就能很清楚的知道为什么要需要进行应用拆分。
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.拆分原则
- 单一职责
- 服务粒度适中
- 考虑团队结构
- 以业务模型切入
- 演进式拆分
- 避免环形依赖和双向依赖
2.分布式应用拆分实战
下面是拆分代码过程实践经验:
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中方法:
- 新建业务module,加入基础module的pom依赖,再从源module复制和该业务module相关的代码(包括单元测试代码)过来,解决编译错误和单元测试错误,完成本业务拆分。
- 从源module复制一个新业务module,保持代码一致,先删除和本义务无关的代码(包括单元测试代码),再删除无关的pom依赖,解决编译错误和单元测试错误,完成本业务拆分。
选择哪种方法,可以根据代码整洁度和互相依赖程度,如果代码很整洁且相互依赖较弱,可以采取前者,否则就采取后者。
6)拆分微服务
有了以上的拆分基础,可以在合适的业务迭代将各个微服务module迁移到不同的代码仓库,完成进一步隔离管理。
微服务架构框架
业界开源微服务架构框架提供了微服务的关键思路,详细请查看微服务Dubbo和SpringCloud架构设计、优劣势比较
Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合)。
Spring Cloud是一个微服务框架,相比Dubbo等RPC框架, Spring Cloud提供的全套的分布式系统解决方案。
微服务架构是互联网技术发展的必然结果,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。
分布式架构之应用拆分总结:
1.明确拆分原则和拆分需求。
2.梳理出业务模块和之间的依赖关联关系。
3.按照业务为单位,拆分实体、以及应用工程单独部署。
4.按照业务为单位拆分应用服务,避免环形依赖和双向依赖。
5.抽离出公用的接口、实体,以及服务单独部署为公用服务。
服务器集群
1.集群概念
集群就是一组相互独立的计算机,通过高速的网络组成一个计算机系统。服务器集群就是指将很多服务器集中起来一起进行同一种服务,在客户端看来就像是只有一个服务器。
2.集群的特点和优势
1)高性能
比如上千台服务器同时计算运行,远大于单机的运行效率。
2)性价比优势
利用通用廉价的服务器代替小型机,例如:阿里轰轰烈烈的去IOE(去掉IBM的小型机、Oracle数据库、EMC存储设备,代之以自己在开源软件基础上开发的系统)
3)可伸缩性
马上即将开始的阿里双11,除了提前预估服务器外,在极端的情况下,可以实现动态的服务器扩容。
3.集群的分类
一般分为如下三类:
- 负载均衡集群(Load balancing clusters)简称LBC
- 高可用性集群(High-availability clusters)简称HAC
- 高性能计算集群(High-perfomance clusters)简称HPC
大家平时接触最多的就是负载均衡集群,常用的有 Nginx 把请求分发给后端的不同web服务器,还有就是数据库集群,负载均衡就是,为了保证服务器的高可用,高并发。
负载均衡
在服务器集群中,需要有一台服务器充当调度者的角色,用户的所有请求都会首先由它接收,调度者再根据每台服务器的负载情况将请求分配给某一台后端服务器去处理。那么在这个过程中,调度者如何合理分配任务,保证所有后端服务器都将性能充分发挥,从而保持服务器集群的整体性能最优,这就是负载均衡。
负载均衡更加详细深入的视频讲解请查看:
分布式
分布式是指将不同的业务分布在不同的地方,而集群指的是将几台服务器集中在一起,实现同一业务。分布式中的每一个节点,都可以做集群,而集群并不一定就是分布式的。
分布式一致性
分布式系统中,解决了负载均衡的问题后,另外一个问题就是数据的一致性了。
在分布式集群中,很难保障数据的一致性。在以往的单节点服务中,通常使用锁来实现,当发生并发冲突时 通过对锁的持有获得对象的操作权,从而保证数据在同一时刻只允许被一个请求操作。但是在集群中,若同样采用锁的机制,那么需要一台节点用来管理分配锁,当其他节点进行请求前,首先去获取锁从而获得执行权。但是这样会产生单节点问题,即若管理锁的节点down掉,那么整个集群将无法工作。同时,由于锁的机制会使整个集群变成串行化单节点的形式,失去了集群的意义。
所以,在集群中需要一种高容错的分布式一致性算法,因此提出了Paxos算法。
Paxos是一种基于消息传递且具有高度容错性的一致性算法,在分布式当中应用的十分广泛。
后来为了简化Paxos算法,升级到了现在主要应用的Raft等算法。