微服务架构学习笔记 | 青训营

35 阅读18分钟

微服务架构介绍

系统架构演变历史

image-20230821165631517.png

微服务架构

微服务架构是一种软件设计模式,它将一个大型应用程序拆分成一系列小而独立的服务。每个服务都可以独立开发、部署和扩展,且彼此之间通过网络进行通信。使用微服务有以下几个主要的优势:

  1. 灵活性与可扩展性:由于微服务是独立的,你可以根据需求对每个服务进行独立的开发、测试、部署和扩展。这样可以提高整体系统的灵活性,并且当流量增加时,你只需要对需要扩展的服务进行扩容,而不需要整体扩展整个应用。
  2. 技术多样性:微服务允许你使用不同的技术栈来实现不同的服务。这意味着你可以根据每个服务的需求选择最适合的编程语言、框架和工具。例如,你的某个服务可能使用Java开发,而另一个服务可能使用Python或Go开发。
  3. 独立部署与维护:微服务的独立性使得你可以独立地对每个服务进行部署和维护。这样一来,当你需要对某个服务进行升级或修复bug时,不会影响到整个应用的运行。此外,由于每个服务都是独立的,团队成员可以专注于自己负责的服务,提高开发效率。
  4. 高可靠性与容错性:微服务的架构使得整个系统更加容错和健壮。当某个服务发生故障时,其他服务仍然可以正常运行,整个系统不会完全瘫痪。此外,由于每个服务都有自己的数据库,数据的隔离度更高,可以减少单点故障的风险。

概览

image-20230821173917672.png

核心要素

  • 服务治理:服务治理是指管理和控制微服务架构中的各个服务的过程。它包括服务注册与发现、负载均衡、故障恢复和服务间通信等方面。具体来说,服务治理需要解决以下问题:
    • 服务注册与发现:微服务需要能够注册自己,并让其他服务发现并调用它们。通常使用服务注册中心(如Consul、Etcd或ZooKeeper)来实现服务的注册和发现。
    • 负载均衡:在多个实例提供相同服务的情况下,需要确保请求能够平均分配到各个实例上,以提高系统的性能和可伸缩性。
    • 故障恢复:当某个服务发生故障或不可用时,需要有相应的机制来快速检测和修复问题,以确保系统的可用性。
    • 服务间通信:微服务之间的通信需要一个有效的机制,例如使用HTTP/REST、消息队列等方式进行异步通信。
  • 可观测性:可观测性是指对微服务架构中的各个服务进行监控、追踪和日志记录,以便及时发现和解决问题,并对系统运行情况有全面的了解。以下是可观测性的主要方面:
    • 监控:通过监控指标(如请求响应时间、服务调用次数等),可以实时了解系统的健康状况,及早发现潜在的性能问题。
    • 追踪:通过对请求的跟踪,可以了解整个请求在微服务之间的流转情况,帮助排查和分析复杂的系统问题。
    • 日志记录:对微服务的日志进行集中记录和管理,可以更轻松地排查问题和进行故障分析。
  • 安全:安全对于任何系统都是至关重要的,尤其是在微服务架构中。以下是几个关键的安全方面:
    • 认证和授权:确保只有经过身份验证的用户或服务才能访问特定的微服务,并根据预定义的权限进行授权。
    • 数据保护:保护微服务之间传输的数据的机密性和完整性,通常使用加密和安全协议来实现。
    • DDos防护:为了防止分布式拒绝服务攻击(DDoS),需要采取相应的防护机制,例如限制请求频率、使用CDN等。
    • 漏洞管理:对微服务进行定期的漏洞扫描和安全评估,及时修复潜在的安全漏洞。

微服务架构原理及特征

基本概念

  • 服务(service)
    • 一组具有相同逻辑运行实体
  • 实例(instance)
    • 一个服务中,每个运行实体即为一个实例。
  • 实例与进程的关系
    • 实例与进程之间没有必然对应关系,可以一个实例可以对应一个或多个进程(反之不常见)。
  • 集群(cluster)
    • 通常指服务内部的逻辑划分,包含多个实例。
  • 常见的实例承载形式
    • 进程、VM、k8s pod……
  • 有状态/无状态服务
    • 服务的实例是否存储了可持久化的数据(例如磁盘文件)。

服务间通信

对于单体服务,不同模块通信只是简单的函数调用。

对于微服务,服务间通信意味着网络传输。

image-20230821175823690.png

服务注册及发现

调用服务地址

在代码层面,如何指定调用一个目标服务的地址(ip:port) ?

  1. hardcode?
client := grpc.NewClient(10.23.45.67:8080")

不能写死!

  1. DNS?

    • 本地 DNS存在缓存,导致延时。
    • 负载均衡问题。
    • 不支持服务实例的探活检查。
    • 域名无法配置端口。
  • 解决思路:

    新增一个统一的服务注册中心,用于存储服务名到服务实例的映射。简单来说就是哈希表(Map)。

image-20230821180714159.png

服务注册中心充当了服务名到服务实例的映射的角色,使得其他微服务可以根据服务名来获取对应服务的地址(ip:port)。下面是一种常见的实现方式:

  1. 服务注册:每个微服务在启动时,将自己的服务信息(包括服务名、IP地址和端口号等)注册到服务注册中心。服务注册中心会将这些信息保存起来,以便其他微服务进行查询和调用。
  2. 服务发现:当一个微服务需要调用另一个微服务时,它可以向服务注册中心发送查询请求,根据服务名获取对应服务的地址列表。服务注册中心会返回一个或多个可用的服务实例的地址。
  3. 负载均衡:如果服务注册中心返回多个服务实例的地址,调用方可以使用负载均衡算法选择其中一个实例进行调用,以实现请求的均衡分配和高可用性。
  4. 服务调用:调用方使用获取到的服务实例地址,通过网络协议(如HTTP、gRPC等)与对应的服务实例进行通信。调用方可以直接使用服务实例的地址进行请求,或者使用客户端库(如Ribbon、Feign等)简化调用过程。

需要注意的是,服务注册中心应该具备高可用性和容错能力,以确保整个系统的稳定性。

通过使用统一的服务注册中心,在代码层面可以通过服务名来指定调用目标服务的地址,而无需硬编码具体的IP和端口信息。

服务实例上线及下线过程

下线实例:

  1. 从服务注册中心注销实例:首先,你需要从服务注册中心将要下线的服务B实例注销。这样可以确保其他微服务不再使用该实例进行调用。

image-20230821181556968.png

  1. 逐渐停止流量:在注销实例之前,建议逐渐停止将流量发送到该实例,以确保已经发送的请求被处理完毕。这可以通过负载均衡器或路由器进行配置,逐渐降低将请求发送到该实例的比例,直至完全停止。在停止流量之后,你可以监控服务B实例的状态,确保没有新的请求发送给该实例,并等待正在处理的请求处理完成。

image-20230821181532442.png

  1. 关闭实例:当确认服务B实例没有未处理的请求时,你可以安全地关闭该实例。这可以通过终止相关进程、停止相关容器等方式进行操作。

image-20230821181628994.png

上线实例

  1. 启动新的服务实例:首先,在一个新的主机或容器中启动一个新的服务实例。使用health check确保新的实例具有正确的配置和依赖项,并可以独立运行。

image-20230821181941212.png

  1. 注册新的服务实例:将新的服务实例注册到服务注册中心。你需要提供新实例的服务名、IP地址和端口号等信息,以便其他微服务可以发现并调用它。如果系统中使用了负载均衡器或路由器,你可能需要更新其配置,以便将流量发送到新的服务实例。这样可以确保请求可以分发到新实例上。

image-20230821182052071.png

  1. 逐渐增加流量:一旦新的服务实例注册成功并且负载均衡器已经配置完成,你可以逐渐增加将流量发送给新实例的比例。这可通过逐渐提高负载均衡算法中新实例的权重或调整路由规则等方式来实现。

image-20230821182114878.png

  1. 监控和验证:注意观察新实例的运行状态,并确保它能够正常处理请求。同时,监控整个系统的运行情况,确保其他微服务也能够正常与新实例通信,并且没有出现任何错误或异常。

流量特征

  • 统一网关入口

    在微服务架构中,可以使用一个统一的网关作为流量的入口。该网关可以处理所有外部请求,并根据需要将流量路由到相应的微服务实例。通过统一网关入口,可以实现请求的集中管理和控制,提供统一的访问方式和安全认证机制。

  • 内网通信多数采用RPC

    微服务架构中的各个微服务之间需要进行通信和协作。为了高效地进行内网通信,多数情况下会采用RPC(Remote Procedure Call)机制。RPC允许微服务之间直接调用彼此的服务接口,以完成跨服务的数据传输和功能调用。通过使用RPC,可以方便地实现微服务之间的数据交互,提高系统的性能和效率。

  • 网状调用链路

    在微服务架构中,各个微服务之间的调用通常形成网状的调用链路。这是因为一个微服务可能会依赖于多个其他微服务的功能和数据。当一个微服务接收到请求时,它可能需要调用其他微服务来获取所需的数据或完成特定的业务逻辑。这种网状的调用链路可以形成复杂的服务依赖关系,需要合理管理和监控,以确保整个系统的稳定性和可靠性。

核心服务治理

服务发布

服务发布(deployment),即指让一个服务升级运行新的代码的过程。

难点

  1. 服务不可用:在服务发布过程中,需要让旧的服务实例先停下,这时会影响到用户的正常访问和业务的进行。如果是抖音的话就上微博热搜了。
  2. 服务抖动:服务抖动指的是在服务发布后,服务的性能或可用性出现不稳定的情况。例如,短时间内服务的响应时间波动较大,或者服务出现频繁的错误响应。
  3. 服务回滚:服务发布后,如果发现了严重的问题或者与其他服务产生了不兼容的冲突,需要进行服务回滚操作。服务回滚是将服务恢复到之前的可用版本的过程。

蓝绿部署

  1. 准备两个集群:将现有服务划分为蓝色集群和绿色集群,它们分别对应当前稳定版本和新版本。两个集群应具备相同的基础环境和配置。

image-20230821230532528.png

  1. 逐步切换流量:将一部分流量从绿色集群逐渐转发到蓝色集群,可以使用负载均衡器或代理服务器来实现这一过程。逐步增加蓝色集群的流量份额,同时监控新版本的应用性能和稳定性。

image-20230821230544383.png

  1. 替换绿色集群:当绿色集群成功接收了大部分或全部流量后,可以将绿色集群中的服务升级为新版本。这意味着绿色集群将提供新版本的服务,而蓝色集群仍然维持现有的稳定版本。

image-20230821230631248.png

  1. 逐步切换回绿色集群:此时,流量仍然会先到达蓝色集群,但我们开始逐渐将流量从蓝色集群切换回绿色集群,直到所有流量都回到绿色集群为止。在此过程中,继续对服务性能和稳定性进行监控。

image-20230821230709656.png 5. 升级蓝色集群:当所有流量都回到蓝色集群后,可以将蓝色集群中的服务升级为新版本。此时,蓝色集群提供的服务已经是最新的版本。

image-20230821230810853.png

优缺点

简单,稳定

但需要两倍资源

灰度发布(金丝雀发布)

金丝雀发布的关键在于逐步扩大测试范围和持续监控应用的性能和用户反馈。这种发布方式使得问题可以尽早发现并修复,最大程度地降低对用户的影响。

image-20230821231125815.png

  1. 选择目标群体:从现有用户群体中选择一个小比例的目标用户群体来尝试新版本。这个目标群体可能是特定的区域、特定的用户段或特定的功能使用者等。
  2. 部署新版本:将新版本应用部署到生产环境中,但只将其提供给目标群体的用户,而其他用户仍然使用旧版本。
  3. 监控和收集反馈:监控新版本应用在目标群体中的性能和行为。同时,积极收集用户的反馈和意见,以了解新版本的问题和改进点。
  4. 评估结果:根据监控数据和用户反馈,评估新版本应用的表现。如果发现了问题或错误,可以进行调整和修复,然后重新进行金丝雀发布。
  5. 逐步扩大范围:如果新版本应用在目标群体中表现正常并获得积极的反馈,可以逐步将新版本应用提供给更多的用户,扩大测试范围。
  6. 完全发布:当新版本应用在不同用户群体中均表现良好,并且经过充分测试和验证后,可以将其完全发布,提供给所有用户使用。

优缺点

优点:

  1. 逐步降低风险:金丝雀发布可以逐步引入新版本,只在一小部分用户或流量中进行测试,从而降低了整体系统的风险。
  2. 尽早发现问题:通过将新版本应用提供给有限的目标群体,可以更早地发现潜在的问题或错误,便于及时调整和修复。
  3. 快速验证:金丝雀发布允许开发团队在生产环境中验证新功能或改进的效果,从而更快地了解用户反馈和应用的实际表现。
  4. 灵活性:金丝雀发布可以根据需要进行灵活的调整和控制,选择目标群体、增加部署比例等,以满足不同的业务需求和发布策略。

缺点:

  1. 管理复杂性:金丝雀发布涉及到多个版本的并行运行和流量分配,需要合理的管理和监控机制,以确保稳定性和正确性。
  2. 增加工作量:金丝雀发布需要额外的工作量来设置目标群体、监控运行状况、收集用户反馈等,这可能增加团队的负担。
  3. 潜在的延迟:由于金丝雀发布只将新版本提供给一小部分用户或流量,其他用户可能需要更长时间才能获得新功能或改进。

流量治理

在微服务架构下,我们可以基于地区、集群、实例、请求等维度,对端到端流量的路由路径进行精确控制。

image-20230821231830025.png

优势:

  1. 性能优化:通过精确控制流量路由路径,可以根据不同地区、集群或实例的负载和性能情况,将流量导向最佳的资源节点。这样可以避免过载节点的压力,提高整体系统的性能和响应速度。
  2. 故障隔离:通过将流量路由到多个地区、集群或实例,可以减少单点故障对整个系统的影响。当某个地区、集群或实例出现故障时,可以将流量自动切换到其他正常运行的节点,保持服务的可用性和稳定性。
  3. 灰度发布:精确控制流量路径还可以用于实现灰度发布(灰度部署)。可以将新版本的应用只部署在特定的地区、集群或实例中,只将部分流量导向新版本,以便进行有限范围的测试和验证。这样可以降低对用户的影响和风险。
  4. 用户体验优化:通过根据用户的请求特征进行流量路由,可以实现更细粒度的个性化服务。例如,根据用户地理位置将流量导向就近的服务器,减少网络延迟;根据用户设备类型导向相应的服务节点,提供更适配的用户体验等。
  5. 资源利用:通过对流量路由的精确控制,可以根据不同地区、集群或实例的资源情况进行动态调度和负载均衡,合理利用资源,提高系统的资源利用率。

负载均衡

负载均衡(Load Balance)负责分配请求在每个下游实例上的分布。

image-20230821232717929.png

常见的LB策略

  • Round Robin(轮询):按顺序将请求分配给后端服务器,每个服务器依次接收请求数。适用于大多数场景,可以平均分配负载,但无法考虑服务器的实际负载情况。
  • Ring Hash(环形哈希):将请求的关键信息通过哈希函数计算,并根据结果在一个固定的服务器环上选择一个后端服务器来处理请求。适用于需要保持会话一致性的场景,同一用户的请求总是被路由到同一台服务器。
  • Random(随机):随机选择一个后端服务器来处理每个请求。适用于简单负载均衡需求,但不能确保负载均匀分布。
  • Least Request(最小连接数):选择当前连接数最少的后端服务器来处理请求,以确保负载均衡。适用于有状态的场景,可以优先将请求发送到空闲或负载较低的服务器上。

稳定性治理

线上服务总是会出问题的,这与程序的正确性无关。不可控因素包括:

  • 网络攻击
  • 流量突增
  • 机房断电
  • 光纤被挖
  • 机器故障
  • 网络故障
  • 机房空调故障

典型的稳定性治理功能

限流

image-20230821233300169.png

限流是控制系统并发请求量的一种策略。通过设置最大请求数或单位时间内的请求速率,可以防止系统被过多的请求压倒,避免资源耗尽和系统崩溃。限流可以通过算法(如令牌桶、漏桶)或者基于队列长度进行实现。

熔断

image-20230821233316951.png

熔断是一种故障保护机制,当系统中出现故障或异常情况时,可以主动断开对某个服务的请求,避免问题扩散到整个系统。熔断器会监控某个服务的错误率、超时等指标,当达到一定阈值时,开启熔断状态,将新的请求快速失败,以减轻故障的影响。在一段时间后,熔断器可以尝试半开状态,允许少量请求通过,以检测服务是否恢复正常。

过载保护

image-20230821233328890.png

过载保护是防止系统过载的一种机制。当系统负载达到预设的上限时,过载保护可以采取一些行动,如拒绝新的请求、延迟处理请求或者丢弃部分请求,以保护系统免受过载影响。过载保护可以根据系统资源利用率、平均响应时间等指标进行触发和调整。

降级

image-20230821233340532.png

降级是在系统资源不足或异常情况下,主动关闭一些非核心或次要功能,保证系统的核心功能可用性。通过降低某些功能的服务质量或关闭部分功能,可以释放资源并减少对核心功能的依赖,以保证关键业务的正常运行。