单体架构的痛点
-
数据库的连接数成为应用服务器扩容的瓶颈
数据库的连接是比较重要的一类资源,不仅连接过程耗时,而且客户端数量有限制。 -
单体架构增加了研发的成本,抑制了研发效率的提升
由于代码部署在一起,每个人都向同一个代码库提交代码,代码冲突无法避免;功能之间耦合严重,可能更改了很小的逻辑,却导致其他功能不可用,在测试时需要对整体功能回归;模块之间相互依赖,一个小的错误可能对于整体系统稳定性影响很大 -
单体架构对于系统的运维也会有很大的影响
随着系统复杂度的增加,一次构建的过程,包括编译、单元测试、打包和上传到正式环境,花费的时间可能达到十几分钟,并且任何小的修改,都需要构建整个项目,上线变更的过程非常不灵活。
从上图可以看出单体架构的问题可以通过微服务化拆分来解决。
拆分的时机
产品初期应该以单体架构优先。
面对一个新的领域,对业务的理解很难在开始阶段就比较清晰,往往是经过一段时间之后,才能逐步稳定。过早拆分,导致边界拆分不合理或者拆分过细,反而会影响生产力;在资源受限的情况下,单体服务在性能上反而会更有优势。当业务复杂度达到一定程度后,微服务架构消耗的成本才会体现优势。服务的划分应逐步进行,持续演进。
随着商业模式逐渐得到验证,产品获得了市场的认可,为了加快产品的迭代效率,团队开始引进更多的研发人力,此时业务已经达到了一定的复杂度,单体应用已经无法满足业务增长的需求,研发效率开始下降,这是就是需要考虑服务拆分的时机点。
服务拆分的落地还需要提前准备好配套的基础设施,比如注册中心、配置中心、日志系统、持续交付、监控系统、分布式定时任务、CAP 理论、分布式调用链、API 网关等等;
人才的储备和观念的变化也得同时跟上
服务拆分不仅仅是技术的升级,更是开发方式、组织架构、开发观念的转变
拆分的原则
-
单一服务内部功能高内聚低耦合
每个服务只完成自己职责内的任务,对于不是自己职责的功能交给其他服务来完成 -
闭包原则
当我们需要改变一个微服务的时候,所有依赖都在这个微服务组件内,不需要修改其他微服务 -
服务自理、接口隔离原则
尽量消除对其他服务的强依赖,这样可以降低沟通成本,提升服务稳定性。服务通过标准的接口隔离,隐藏内部实现细节。使得服务可以独立开发、测试、部署、运行,以服务为单位持续交付 -
持续演进原则
服务拆分初期,很难确定服务究竟要拆成什么样。服务粒度小会导致数量增多,架构复杂度急剧升高,开发、测试、运维等环节很难快速适应,会导致故障大幅增加,可用性降低,非必要情况,应逐步划分,持续演进,避免服务数量的爆炸性增长 -
服务接口的定义要具备可扩展性
服务拆分之后以独立进程方式部署,服务间通信变成跨进程的网络通信。在这种通信模式下,服务接口的定义要具备可扩展性,否则在服务变更时会造成意想不到的错误,比如接口升级由之前的三参数变为四参数,推荐做饭服务接口的参数类型最好是封装类 -
避免环形依赖与双向依赖
尽量不要有服务之间的环形依赖或双向依赖。存在这种情况说明我们的功能边界没有划分清楚或者通用的功能没有沉淀下来 -
阶段性合并
随着对业务领域理解的逐渐深入或者业务本身逻辑发生了比较大的变化,亦或者之前的拆分没有考虑很清楚,导致拆分后的服务边界变得越来越混乱,这是就需要重新梳理领域边界,不断纠正拆分的合理性
拆分的粒度
服务拆分粒度太细会增加运维复杂度,粒度过大又起不到效果,如何平衡拆分粒度呢?
弓箭原则
平衡拆分粒度可以从两方面进行权衡;一是业务发展的复杂度,二是团队规模的人数。
产品初期阶段,业务逻辑并没有足够复杂到2~3人没法维护的地步,这时我们没有必要将业务继续拆分的更细,但随着业务的发展,业务逻辑变的越来越复杂,可能同时服务多个平台,这时你会发现服务面临各种问题,这个阶段就需要将服务拆分为更细粒度的服务。虽然业务复杂度已经满足了,但如果没有足够的人力,服务最好也不要拆分,拆分会因为人力的不足导致更多的问题,如研发效率大幅下降。
一个服务需要几个开发维护是比较理性的?
三个火枪手原则
- 从系统规模上讲:三个人负责开发一个系统,系统的复杂度刚好达到每个人都能全面理解整个系统,又能够进行分工的粒度;
- 从团队管理上讲:三个人可以形成一个稳定的备份,即使一个人休假或者调配到其他系统,剩余两个人还可以支撑;
- 从技术提升上讲:三个人既能够形成有效的讨论,又能够快速达成一致意见;如果两个人可能出现互相坚持自己的意见,或者经验不足导致设计缺陷;如果一个人没有人进行技术讨论可能陷入思维盲区;如果四人以上容易出现参与的人员没有认真参与,或者只是完成任务而已
三个火枪手原则主要应用于微服务设计和开发阶段
拆分的策略
拆分策略可以按功能和非功能维度考虑,功能维度主要是划分清楚业务的边界,非功能维度主要考虑六点:扩展性、复用性、高性能、高可用、安全性、异构性。
功能维度
纵行拆分(基于业务逻辑拆分)
从业务维度进行拆分。标准是按照业务的关联程度来决定,关联比较密切的业务适合拆分为一个微服务,而功能相对比较独立的业务适合单独拆分为一个微服务
横向拆分
从公共且独立功能维度拆分。标准是按照是否有公共的被多个其他服务调用,且依赖的资源独立不与其他业务 耦合。
按领域模型拆分
按领域模型拆分主要是划分清楚业务边界,主要分四步:
1、找出领域实体和值对象等领域对象
2、找出聚合根,根据实体、值对象和聚合根的依赖关系,建立聚合
3、根据业务及语义边界等因素,定义限界上下文
4、每一个限界上下文可以拆分一个对应的服务,但是也要考虑一些非功能因素
非功能维度
扩展性
区分系统中变与不变的部分,不变的部分一般是成熟的、通用的服务功能,变的部分一般是改动比较多、满足业务迭代扩展性需要的功能,我们可以将不变的部分拆分出来,作为公用的服务,将变的部分独立出来满足个性化扩展需要。
二八原则:经常变动的部分大约只占20%,剩下的80%基本不变或极少变化
复用性
不同的业务里或服务里经常会出现重复的功能,比如每个服务都有鉴权、限流、安全以及日志监控等功能,可以将这些通用的功能拆分出来形成独立的服务,也就是微服务里面的API网关。
可靠性
将可靠性要求高的核心服务和可靠性要求相对低的非核心服务拆分开来,然后重点保护核心服务的高可用。
高性能
将性能要求高或者性能压力大的模块拆分出来,避免性能压力大的服务影响其他服务。常见的拆分方式和具体的性能瓶颈有关,例如电商的抢购,性能压力最大的是排队功能,可以将此独立成一个服务;对于读写差异比较大的服务,也可以基于读写分离来拆分;基于数据一致性拆分,将强一致性的业务尽量放在一个服务中,弱一致性通常拆分为不同的服务
安全性
不同的服务可能对信息安全有不同的要求,因此把需要高度安全的服务拆分出来,进行区分部署,可以更有针对性地满足信息安全的要求,也可以降低对防火墙等安全设备吞吐量、并发性等方面的要求,降低成本,提高效率
异构性
对于开发语言种类有要求的业务场景,可以用不同的语言将其功能独立出来实现一个独立服务
以上拆分方式可以根据实际情况自由排列组合使用。拆分不仅仅是架构上的调整,也意味着要在组织结构上做出响应的适应性优化,以确保拆分后的服务由相对独立的团队负责维护
拆分后的合并
一个系统现在拆分出来的服务粒度也许合适,但随着时间的流失,系统需要不断的适应新的业务发展阶段,我们对系统领域的了解也越来越深,之前拆分的服务粒度可能就不合适了。例如业务的增删导致、过多的进程间通信导致效率低下等因素。
人员和服务数量的不匹配导致的维护成本增加,也是导致服务合并的一个重要原因。
服务数量过多和资源不匹配,则可以考虑合并多个微服务到一个服务包,部署到一台服务器,这样可以节省服务运行时的基础资源消耗,也降低了维护成本。需要注意的是,虽然服务包是运行在一个进程中,但是服务包内的服务依然要满足微服务定义,以便在未来某一天要重新拆开的时候可以很快就分离
网友评论