架构师训练营第十周学习总结
本周面向系统模块拆解进行相关原理和实践的学习,主要包括微服务体系架构原理、领域驱动设计、组件设计原则。
微服务体系架构原理
作为一个微服务体系的架构,需要实现几个基本功能:服务的注册和发现、服务的负载均衡和代理调用、服务的异常处理和服务演化的控制。
服务的注册和发现
作为微服务框架最基本的功能之一,是需要有一套完备的机制来做服务提供者和服务调用者的管理,这里依赖于我们的注册中心来实现。
注册中心相当于一个联络清单,所以声明自己对外提供的微服务都要到这个清单里进行登记(注册),而客户端则从注册中心拿到这份清单,就可以发现服务。服务提供者可以是一组冗余的集群,所以注册中心会用相关的命名规则方式来表示服务,通过服务编号或名称来定位具体是需要调用什么服务。利用注册中心这样的机制可以很好实现水平伸缩,需要扩大时启动新的服务器往注册中心登记,注册中心会通知客户端最新的服务清单;需要缩小时,注册中心通过心跳机制感知到某些服务器下线,会通知客户端取到最新的服务清单。
服务的负载均衡和代理调用
当一组服务是集群部署的时候,微服务框架需要有负载均衡的能力,并能够代理客户端的调用请求,客户端无需自己了解后端集群的情况,由微服务框架来处理,这里依赖于我们的微服务网关。
调用方应当首先调用统一的微服务网关,由网关充当一个中间调度的角色,负载均衡就是在这里完成。网关是流量的入口,本身也可以集群化。单个网关对后端的微服务提供者通过一定的策略进行负载均衡,保证后端的负荷平衡,这里就需要考虑我们的应用服务是有状态还是无状态的,无状态则多次请求无需路由到同一个后端服务器,如果是有状态则需要考虑状态信息是否保存在单个服务器上,这样就要保证多次路由到同一个服务器,否则这部分状态信息也要共享到一个统一的服务器。
而这一切的负载均衡选择策略对调用者本身是透明的,调用者无需关心,这里就需要通过代理的方式实现,减少微服务网关的架构对我们本身代码的侵入性,我们的代码依然是在调用一个接口,接口的具体实现由微服务框架来代理,无论是本地调用还是远程 RPC 调用。
服务的异常处理
为了应对高可用,微服务框架要有处理内外异常的能力,当网络抖动延迟、后端某些服务器故障或升级时,甚至当流量高峰时如何保护核心业务受到最小的影响,微服务框架本身需要有一套机制来应对这些场景,这里就需要有一套异常处理机制。这里就依赖微服务客户端、微服务网关和注册中心。
客户端出现调用异常时,会触发重试机制,若干次后会路由到其他节点并通知注册中心进行异常节点的健康检查,如果判断是异常则会把这个异常节点剔除出注册清单里,如果是整个服务集群出现异常或故障,更有可能触发服务熔断,客户端将不等待调用返回直接快速失败(fail-fast)。
如果是流量出现异常的高峰,微服务网关会通过流量算法(计数器、各类桶限流)对向后分发的请求进行控制,甚至对非核心业务进行一定的限流,保证核心业务还可以承载当前的大流量负荷。
服务演化控制
微服务本身适合敏捷开发,各种小迭代频繁发布,甚至线上有多个版本的服务。这时候需要微服务支持我们对于版本的控制,这里依赖我们的微服务网关。
微服务网关可以对请求数据进行解析,识别版本标识进行一定规则的路由,同时支持一定的灰度发布机制,把少量的流量导入到新上线的服务版本做验证,验证通过后再进行大规模的版本升级,减少因为新版本的不稳定原因导致大面积服务不可用。
领域驱动设计
领域驱动设计是目前比较热门的一种开发实践,与我们通常使用的贫血模型有很大的不同,领域驱动设计强调我们按照领域对象进行编程,而过去的贫血模型更像是面向 UI 和数据库设计编程,一切的流程都是以数据存储看齐。
这导致当我们的业务发展到一定程度出现开发上的很多问题:
1.代码过于臃肿,Service 层的代码超过数千行;
2.模块与模块之间的边界不清晰,因为面向数据库表的关联开发,很多业务都堆在若干个核心 Service 类,因为这里有非常多的上下文调用的便利代码,开发就自然往这里堆砌代码;
3.开发效率低下,由于代码过于臃肿,每当有旧功能需求变更时,不知如何下手,面对数千行的方法,修改举步维艰;
以上这些都还只是单体应用的普遍问题,如果要演化成微服务架构,面对这一坨代码如何进行合理的模块拆分也是非常头疼的问题。
实践领域驱动设计,需要回顾我们的业务,需要有架构师和领域专家一起分析业务的核心问题,领域、子域、限界上下文、实体、值对象、领域事件等都是什么,用一套 DDD 的思维来指导我们进行架构和模块设计,保证我们后续在业务发展过程伴随的架构发展能相对顺利演化。
组件设计原则
组件设计关系到我们的代码可重用和稳定性问题,保证一个组件内是相关高内聚的功能模块,不要让组件依赖它不需要的代码,因为依赖越多那么相应出现变更修改的可能性也就越大,也就越不稳定。另一个参考就是我们按照一个发布的最小粒度来设计组件,让组件可以单独部署和升级版本,减少整体大量发布的情况,减少运维的压力。
识别组件的边界,为我们后续实现业务中台有很大帮助。
目前也在尝试使用 DDD 来改造工作中的存量代码,过程体会到最大的难点还是识别领域对象的功能边界,还需要多实践。多使用不同的方案来验证自己的设计是否匹配我们对业务的理解,这里面还是需要花费不少的时间的,所以项目的初期并不建议过度使用 DDD 来进行开发,在初具规模并有市场前景的情况下,适时地采用 DDD 进行代码和架构的重构是一个较为合理的选择。
版权声明: 本文为 InfoQ 作者【Gosling】的原创文章。
原文链接:【http://xie.infoq.cn/article/9b90364b28b1fa39affa4ac0a】。文章转载请联系作者。
评论