前言
最近,参加了很多内部或外部的技术架构的分享,个人学习过程中也时常偶然偶尔在各种博客文章、公众号文章等其他情况下常常看到有关架构设计的资源资料。
其中的设计思想有很多是可以参考和借鉴的,本文针对个人粗浅的理解,进行个人总结和记录,仅供参考。比较关键的设计思想如Trade Off(权衡)、架构的健壮性考虑因素包含且不限于三高(高性能、高可靠、高并发)等、DDD领域设计(如领域划分、核心域、通用域、子域等,领域事件、防腐层设计)、分层设计思想(MVC架构、适配层、类似DDD防腐层的Transformation Data数据转换层、业务层与存储层)等的层级间解耦、利用sideCar /agent等组件间解耦、利用设计模式编码对象间抽象与组合解耦思想、分而治之等。
领域驱动设计(Domain-Driven Design,DDD)
基本思路
- 基本过程:抽象业务、分析流程、识别边界、建立模型、映射到服务和代码
- 避免过度耦合(解耦)、存在贫血领域对象等情况->建立充血模型
- 划分界限上下文,厘清上下文之间的映射关系,比如合作关系、共享内核、客户方-供应方开发、防腐层、开放主机服务等等。
- 细化上下文对象,区分实体、值对象、聚合根、领域服务、领域事件(MQ通知、RPC回调等)
微服务设计原则
原则1: 按照业务能力(business capabilities)来规划或拆微服务。
原则2: 按照领域驱动设计(Domain-Driven Design,DDD)来规划或拆解微服务。
原则3:微服务的设计应该遵循「单一职责」原则
所谓单一职责原则,就是对一个服务而言,它的功能要“单一”,只做与它业务领域内相关的业务。在微服务的设计过程中要按职责进行设计,彼此保持正交,互不干涉。
什么样的单一领域对象的单一职责微服务才是有价值的?
就是不断有业务变化,能够维持业务持久性,有业务生命力的领域对象。但是能够实现独立演进、个性化的弹性伸缩。
原则4: 微服务的设计应该遵循「高内聚」原则
前因:过度追求「单一职责」,或者拆分微服务过细,往往会带来不良后果。微服务的设计并不是越细越好,过度拆分会导致调用性能变差、数据一致性难以保障、系统可用性降低等问题。
因此,「高内聚」原则要求:
- 完全独立。微服务粒度的下界是它至少应满足独立,能够独立发布、独立部署、独立运行与独立测试
- 足够内聚。强相关的功能与数据在同一个服务中处理
- 足够完备。一个服务包含至少一项业务实体与对应的完整操作
原则5:微服务的设计应该遵循「低耦合」原则
- 避免数据过度暴露、业务数据"不扩散"
- 避免数据库、表多微服务间共享
- 最小化同步调用,如有必要,引入事件驱动进行异步调用
原则6:服务无状态化
先解释下什么是「状态」?
如果一个数据需要被多个服务共享,才能完成一项流程业务动作行为,那么这个数据被称为有状态。依赖这个「状态」数据的服务被称为有状态服务,反之称为无状态服务。「无状态」原则并不是说在微服务架构里就不允许存在状态,而是要把有状态的业务服务改变为无状态的计算类服务,那么状态数据也就相应的迁移到对应的“有状态数据服务”中。
如上图举例的场景说明:例如我们以前在本地内存中建立的数据缓存、Session缓存,到现在的微服务架构中就应该把这些数据迁移到分布式缓存中存储,让业务服务变成一个无状态的计算节点。迁移后,就可以做到按需动态伸缩,微服务应用在运行时动态增删节点,就不再需要考虑缓存数据如何同步的问题。
只有服务无状态,才能实现快速弹性扩缩容,应对流量峰谷。
原则7:服务高可用性
- 接入高可用中间件(如sentinal),实现限流、熔断、降级,增强可用性
- 部署多节点、多实例、多副本等提高HA能力
原则8:服务可观测性
除了默认系统监控告警(Zabbix/ Prometheus中间件等)外,微服务需要梳理并定义必要的「业务监控指标」,并可通过Dashborad等面板(Grafana)可视化
原则9:服务配置可统一管理能力
微服务相关配置需要统一接入配置中心进行管理、控制。如Consul、Apollo等
原则10:避免「分布式大单体」
- 只做单向调用,避免循环调用
- 多个服务循环依赖调用形成集中式“分布式大单体”,违背微服务的原则
原则11:异步解耦
按需接入消息队列MQ(RabbitMQ、Kafka、Pulsar等),实现「异步解耦」、「流量削峰」
- 串行同步调用异步化,提高响应能力和响应速度
- 应对突发流量,实现流量削峰与流量控制
- 解耦核心业务逻辑不必要的依赖
- 业务设计中的最终一致性
原则12:引入BFF层,降低客户端与后端微服务之间的耦合
尽量设计BFF(Backend For Frontend)层,把前端的特殊需求交给BFF层,使后端服务逻辑具有高内聚、高复用性的精简核心逻辑。了解下GraphQL API模式QL实现层。
BFF落地架构层次如下图:
原则13:服务发布遵循安全发布三板斧
保证「可灰度」、「可监控」、「可回滚」
原则14:正视「架构腐化」,遵循「持续演进」原则
「架构腐化」的常见场景:
- 多人维护一个微服务,出现「频繁代码冲突」
- 当你发现在领域建模时错误地将聚合d放到了微服务C里,或者随着业务发展聚合d更适合放在微服务D里。由于领域模型的不合适,可能会导致微服务之间出现频繁调用,进而导致微服务之间出现「紧耦合关系」
实际开发过程中,“代码腐化”、“架构腐化”、“设计腐化”等随着业务不断的膨胀,个人觉得无法避免,只能尽量做到规范。
原则15:参考「AKF扩展立方」模型,服务除了「水平扩容」外,还可以考虑「功能拆分」或者 「数据分区」【很重要也很深的一个理念!!】
- X轴:服务和数据的水平扩容。
- Y轴:功能/业务拆分
- Z轴:沿客户边界的服务和数据分区
「水平扩容」比较容易理解,直白点说就是加机器。根据AKF模型,除了加机器外,我们还可以考虑「功能拆分」或者 「数据分区」。
「功能拆分」相对复杂,一般包括几种模式:
- 微服务拆分。根据具体业务模型、领域模型拆分更细粒度的微服务。
- 业务隔离拆分。利用消息队列,将在线业务(OLTP)和耗费大量资源的计算任务拆分隔离。
- 核心与非核心隔离。对于一个微服务,可以将SKA客户与普通客户进行隔离,SKA客户使用独立的集群资源,提高稳定性。
「数据分区」往往指的是数据库层面。需要引入数据库中间件,像 sharding-jdbc、mycat 等,在数据层面需要配置相应的分片逻辑。正确的拆分对提高系统的容量有很大的帮助,失败的拆分可能会造成热点集中,得不偿失。常用的分区逻辑包括 按照时间分区、按照分配键某业务主键id取模分区等。
原则16:对于「废弃服务」,需要做好「下线」工作,包括服务下线、存储释放等。【废话原则】
Trade Off
”权衡“,这个思想是最近我比较觉得有意思且个人很赞同的一种设计理念。有关架构设计流传一句”名言“:没有最完美的架构,只有最合适且适合的架构。
这个权衡包含很多因素,如当前的业务场景、可行性技术方案的落地成本(机器集群预算成本、开发人员人力成本、后期运维维护成本等等等),简而言之,有些决策是需要Boss层面考虑的,是投入与产出的trade off。以最低的成本做到当前最优的架构方案才是“王道”。
高可靠
这个段落除了简单的搭建服务集群,增加副本等常规设计思路。将从一些特别的”点“做到高可靠的能力。
一致性数据可靠
如在服务设计实现业务场景中考虑针对关键“业务节点”、“里程碑事件”做好数据的可靠性校验。
设计思想如:业务逻辑触发前、触发过程、结束后等关键阶段做好相应处理。”前期存储快照或通知、中期check、后期对账“。做好快照数据存储、事件通知或"double check"数据快照对账等等。考虑ODPS Flink/Spark 近实时/离线 流数据分析,比对。中央数据中心的统一数据准实时性、MQ事件通知&重试、后台Job Task check&alarm等、分布式事务的解决方案如TCC/2PC/3PC/消息记录表最终一致性柔性事务等。
高并发
高并发过程中常常有”加锁“的设计思想,用于保障并发过程的某些”顺序“特性场景。而分布式加锁场景常见的如利用其他中间件实现:如Redis的lua脚本/setNx/watch Dog/Redission等等,zookeeper的顺序临时节点加watch机制,mysql等数据库的悲观锁 如select ... from table where status=' ' for update等。
除了类似“悲观锁”的实现(对并发性能影响较大,又类似上面说的trade off权衡的概念),还可以利用”乐观锁“ 如CAS(compare and swap)思路。防止ABA问题 可以再增加一列快照版本号保证。 mysql 库实现乐观锁举例如update .... where status=' ' and snapshot_version=' ';
架构设计及落地的执行流程阶段
收集当前技术栈相关使用情况
收集、梳理当前的技术栈使用情况,如涉及的业务线,量级,机器使用情况等等。方便后续技术选型或升级的"代价和成本(又一个trade off)"
分析当前技术栈的痛点及新技术架构需求、需要实现的大体功能
如对时间差的容忍程度,是否接受类似"时间窗口”带来的对业务的冲击,是否满足现业务行为。
是否对开发、测试、业务使用等真正带来效率提升。对技术栈的整合、替换,需要考虑"普适性",其他团队的改造成本等。
调研 - 技术选型对比&开会确定技术方向
关注点评估:
1、调研相关技术的生态环境。如技术社区活跃程度、未来的持续发展前景等
2、学习、开发成本问题。新技术的”上手“效能、后期的二次开发能力、团队的接受性(如是否跨语言,跨技术栈)
3、横向与纵向对比技术框架或架构方向的优劣势。如三高(AP or CP)的实现与支持、长远考虑后续可能的“迁移”成本、是否支持跨机房、多数据中心存储、多集群多租户隔离等、数据迁移的平滑程度
4、评估性能。CPU、Mem、网络IO、磁盘占用等对机器性能的最低支持。高并发Multi Request、吞吐量、响应耗时RT、容错能力等指标 etc...
5、开会讨论各个技术方向的优缺点,确认最终的技术方向。如选择分布式事务框架,是2pc/3pc/还是tcc、消息事务记录表等。
Demo - 实现试用 性能评估&压测
灰度-小范围试用 全面升级&使用
架构模块拆分&细粒度功能拆分
拆分的粒度根据实际情况决定。拆分思想 粒度主要是方便保证可以按优先级实现的阶段,分阶段推进,如切分成三阶段,第一阶段(重要且紧急)满足使用,第二阶段(重要不紧急)优化细节,第三阶段(不重要且不紧急)扫尾处理,分模块分粒度落地
划定里程碑事件
模块拆分完成后,根据模块,根据阶段划分项目的里程碑事件及完成时间,有利于项目进度管理