微服务的几种设计模式
微服务的几种设计模式
单体服务和微服务的对比,微服务包含的一些设计模式以及他们的概念和作用
1.背景
业务应用程序存在的问题
- 业务不受管制的增长
- 缺乏架构
- 代码过长,像意大利面条
- 隐藏的各种问题
加上软件行业大爆发,移动互联网兴起,使得单体架构无法很好适应现代大规模互联网
2.单体服务的局限性
- 可扩展性不足
- 开发速度慢,在单体服务中添加新功能改动很大,代码耦合程度高
- 由于代码过于耦合,代码量增加后导致新人学习周期长,开发周期长,发布周期长
- 当程序规模扩大后,单体架构的内部接口边界开始分崩离析
3.微服务的优缺点
定义:微服务架构是将软件系统分解为可独立部署的自治单元,这些单元通过轻量级、与语言无关的方式进行通信,并共同实现业务目标
优点:
- 新技术的兴起,例如云计算、容器化(Docker、kubernetes)、DevOps、新兴语言(Golang、Rust)、敏捷软件开发、NoSQL等
- 方便扩展
- 开发速度快,每个人或团队负责一个微服务,共同开发
- 发布周期短
- 模块化,单体服务的边界是内部接口,微服务之间的边界是外部接口,所以具有高内聚、低耦合的特性
- 每个微服务可以用不同语言编写,然后不同服务之间用同一的方式通信,如gRPC
缺点:
- 设计复杂,微服务通常适用于较大型的系统,且解决方案众多,所以用对合适的方案很重要
- 分布式系统复杂性,微服务是一个分布式系统,可能出现以下问题:整体系统延迟较高,网络故障或单个节点故障会导致整个系统崩溃,操作复杂性较高
- 操作复杂,不同服务之间的日志记录、监控、追踪比单体服务更加复杂,所以基建工作所花费时间比单体服务更长
- 安全性,同时保障多个服务之间的安全性具有一定挑战
- 数据共享的数据一致性的保障困难
何时使用微服务
- 网络规模的应用程序开发
- 多个团队处理应用程序时的企业应用程序开发
- 长期收益优于短期收益
- 团队拥有能够设计微服务架构的软件架构师或高级工程师
4.微服务的设计模式
1.独享数据库
单体服务的时候一般是用一个大型中央数据库,但是到了微服务则不能所有服务都使用一个数据库,特别是在大规模系统中,这将导致微服务在数据库层严重耦合
更好的方法是为每个微服务提供自己的数据存储,这样数据库层的服务之间就没有强耦合,不同微服务可以共享同一个物理数据库,但它们应该使用单独的 Schema。
优点:
- 数据由服务完全所有
- 服务的开发团队之间耦合度降低
缺点:
- 在服务之间共享数据变得具有挑战性
- 提供应用程序范围的 ACID 事务保证变得更加困难
适用:
- 在大型企业应用程序中
- 当团队需要完全把控微服务以实现开发规模扩展和速度提升
不适用:
- 小规模应用中
2.事件溯源(Event sourcing)
在传统数据库中,直接存储的是业务实体的当前“状态”,而在事件源中任何的“状态”更新事件、创建事件或其他重要事件,即快照,都会被存储起来,而不是直接存储数据实体本身,事件溯源是借鉴数据库事件日志的一种数据持久方式,在事件日志中记录导致状态变化的一系列领域事件,通过持久化记录改变状态的事件,重新播放获得状态改变的历史,事件回放可以返回系统到任何状态,使用事件溯源开发业务逻辑
优点:
- 为高可伸缩系统提供原子性操作
- 自动记录实体变更历史,包括时序回溯功能
- 松耦合和事件驱动的微服务
- 事件存储包括完整的审计跟踪,可以在需要严格监管的场景中派上用场。
- 可以轻松地重建生产数据库。
- 有多个为读优化的数据存储
缺点:
- 从事件存储中读取实体成为新的挑战,通常需要额外的数据存储
- 系统整体复杂性增加了,通常需要领域驱动设计
3.命令和查询职责分离(CQRS)
CQRS 建议将应用程序层分为两个方面,即命令端(Command)和查询端(Query)
查询端负责优化读取数据,从持久化获取数据,然后将它们映射到展现层表单,这些表单通常被标识为数据传输对象(DTO)
命令端关注优化写入数据,命令执行各种用例,修改实体状态并将其持久化
4.API网关(API Gateway)
客户端和微服务连通时,要面对如下挑战:
- 一个客户端需要向多个微服务发送请求,则要多次往返服务器
- 微服务中存在多种通信协议(gRpc、thrift、REST、AMQP 等),客户端很难所以协议都采取
- 每一个微服务都有例如身份验证、授权、日志等功能
为了应对上述挑战,引入了一层网关层,位于客户端和服务器之间,主要有如下特性:
- API网关提供了一个反向代理,将请求(第7层路由,通常是HTTP请求)重定向或路由到内部微服务
- 聚合请求,将多个客户端对多个微服务的请求聚合到一个Gateway中,为客户端提供了与微服务系统进行通信提供了单一的入口,减少客户端和微服务之间的调用次数
- 集中管理的横切关注点,整合边缘重复功能
- 认证和授权,身份识别与访问管理(IAM)
- 服务发现
- 缓存响应结果
- 重试策略、熔断器、服务质量(QoS)
- 限速和节流
- 负载均衡
- log 日志、链路追踪、关联,集中式日志管理(服务之间的 transaction ID、错误日志等)
- Header、query 字符串 以及 claims 转义
- IP 白名单
缺点:
- 单点故障
- 额外的网络调用带来的延迟增加
- 如果不进行扩展,它们很容易成为整个企业应用的瓶颈
- 额外的维护和开发费用
5.面向前端的后端(BFF)
微服务架构中,前后端应用是分离和独立的服务,它们通过 API 或 GraphQL 连接,前端除了Web端还有移动端(ios,android……),因为移动客户端和 Web 客户端有不同的屏幕尺寸、显示屏、性能、能耗和网络带宽,它们的 API 需求不同。
BFF是 API 网关模式的一种变体,它提供了基于客户端的多个网关,而不是提供给客户端一个单一的入口点,目的是根据客户端的需求提供量身定制的 API,从而消除了为所有客户端制作通用 API 造成的大量的浪费,BFF 只是一种逻辑分层,而非一种技术。
如下图,每次访问该页面都需要发送 3 个请求,同时为了保障 Android,iOS,以及 Web 端的不同需求,需要为不同的平台写不同的 API 接口,而每当值发生一些变化时,需要 Android,iOS,Web 做出修改。与此同时,当我们需要对一个字符串进行处理,如限定 140 个字符的时候,我们需要在每一个客户端(Android,iOS,Web)分别实现一遍,这样的代价显然相当大。
加入 BFF 层,原本每次访问发送 3 请求页面,变成一个请求,有了 BFF 这一层时,就不需要考虑系统后端的迁移,后端发生的变化都可以在 BFF 层做一些响应的修改。
优点:
- 分离 BFF 之间的关注点,使得可以优化不同的UI。
- 提供更高的安全性。
- 减少 UI 和下游微服务之间频繁的通信。
缺点:
- BFF 之间代码重复。
- 大量的 BFF 用于其他用户界面(例如,智能电视,Web,移动端,PC 桌面版)。
- 需要仔细的设计和实现,BFF 不应该包含任何业务逻辑,而应只包含特定客户端逻辑和行为。
适用:
- 如果应用程序有多个含不同 API 需求的 UI。
- 出于安全需要,UI 和下游微服务之间需要额外的层。
- 如果在 UI 开发中使用微前端。
- 项目中时常存在一些需要缓存的临时数据,将该缓存操作放在 BFF 层,将与第三方的交互放在BFF层。
- 服务中的权限控制,将所有服务中的权限控制集中在 BFF 层,使下层服务更加纯粹和独立。
6.断路器
在微服务架构中,微服务通过同步调用其他服务来满足业务需求。服务调用会由于瞬时故障(网络连接缓慢、超时或暂时不可用) 导致失败,这种情况重试可以解决问题。然而,如果出现了严重问题(微服务完全失败),那么微服务将长时间不可用,这时重试没有意义且浪费宝贵的资源(线程被阻塞,CPU 周期被浪费)。
在这种情况,可以使用断路器模式挽救,通过统计最近发生的故障数量,并使用它来决定是继续请求还是简单的直接返回异常,断路器可以有以下三种状态:
- 关闭:断路器将请求路由到微服务,并统计给定时段内的故障数量,如果超过阈值,它就会触发并进入打开状态。
- 打开:来自微服务的请求会快速失败并返回异常。在超时后,断路器进入半开启状态。
- 半开:只有有限数量的微服务请求被允许通过并进行调用。如果这些请求成功,断路器将进入闭合状态。如果任何请求失败,断路器则会进入开启状态。
优点:
- 提高微服务架构的容错性和弹性。
- 阻止引发其他微服务的级联故障。
缺点:
- 需要复杂的异常处理。
- 日志和监控。
- 应该支持人工复位。
7.外部化配置
每个业务应用都有许多用于各种基础设施的配置参数(例如,数据库、网络、连接的服务地址、凭据、证书路径),如果在微服务中把这些配置都内部化,当有数百个微服务的时候,改动配置将变得麻烦,还可能带来安全风险,更好的方法是将所有配置外部化,使得构建过程与运行环境分离,生产的配置文件只在运行时或通过环境变量使用,从而最小化了安全风险。
优点:
- 生产配置不属于代码库,因而最小化了安全漏洞。
- 修改配置参数不需要重新构建应用程序。
- 点赞
- 收藏
- 关注作者
评论(0)