Skip to content

SpringCloud_组件基本使用_基础概念

  • SpringCloud
    • 服务注册
      • nacos
      • eureka
    • 负载均衡
      • Ribbon 负载均衡策略
      • 自定义负载均衡
    • 熔断、降级
    • 监控 skywalking
  • 业务相关
    • 限流
      • 漏桶算法
      • 令牌桶算法
    • 分布式事务
      • 分布式理论 CAP、BASE
      • 分布式事务解决方案
      • seata
    • 分布式服务接口幂等
    • 分布式任务调度 xxl-job
  • 消息中间件

一、基础概念

1、SpringCloud是什么

SpringCloud 提供了一整套的分布式系统解决方案,通过 Spring Boot 风格的封装,屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、容易部署的分布式系统开发工具包。

SpringCloud 包含了:服务注册与发现、配置中心、服务网关、智能路由、负载均衡、断路器、监控跟踪、分布式消息队列等。

Spring Cloud 包含很多子项目,国内主要是采用 Netflix 和 Alibaba 两个标准实现,前者由于不再维护,目前一般采用Spring Cloud Alibaba 这一套微服务解决方案。

了解组件

Spring Cloud Netflix 第一代

针对多种 Netflix 组件提供的开发工具包,其中包括 Eureka、Ribbon、Feign、Hystrix、Zuul、Archaius 等。

  • Netflix Eureka:一个基于 Rest 服务的服务治理组件,包括服务注册中心、服务注册与服务发现机制的实现,实现了云端负载均衡和中间层服务器的故障转移。
  • Netflix Ribbon:客户端负载均衡的服务调用组件。
  • Netflix Hystrix:容错管理工具,实现断路器模式,通过控制服务的节点,从而对延迟和故障提供更强大的容错能力。
  • Netflix Feign:基于 Ribbon 和 Hystrix 的声明式服务调用组件。
  • Netflix Zuul:微服务网关,提供动态路由,访问过滤等服务。
  • Netflix Archaius:配置管理 API,包含一系列配置管理 API,提供动态类型化属性、线程安全配置操作、轮询框架、回调机制等功能。

Spring Cloud Alibaba 第二代

阿里开源组件

  • Nacos:阿里巴巴开源产品,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
  • Sentinel:面向分布式服务架构的轻量级流量控制产品,把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
  • RocketMQ:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。
  • Dubbo:Apache Dubbo™ 是一款高性能 Java RPC 框架,用于实现服务通信。
  • Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。

阿里商业化组件

  • Alibaba Cloud ACM:一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。
  • Alibaba Cloud OSS:阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
  • Alibaba Cloud SchedulerX:阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
  • Alibaba Cloud SMS:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

关于Cloud各种组件的停更升级替换

image.png

SpringCloud 版本介绍

Eureka、Ribbon、Feign、Hystrix、Zuul、Archaius

2、SpringCloud 的常用组件

回答这个问题,必须要回答出基本架构相关的一些组件。

image.png

Spring Cloud Netflix 第一代

  • Eureka 注册中心
  • Ribbon 负载均衡
  • Feign 远程调用
  • Hystrix 服务熔断
  • Zuul 网关

Spring Cloud Alibaba 第二代

  • Nacos 注册中心、配置中心
  • Ribbon 负载均衡
  • OpenFeign 远程调用
  • Sentinel 服务保护
  • Gateway 网关

3、微服务

可能会问一些关于微服务相关的概念和知识

4、常见概念

CAP
  • 一致性
  • 可用性
  • 分区容错性

image.png

  • 一致性(Consistency):所有节点上的数据在同一时间是相同的。即在分布式系统中,每次读取操作都能得到最新的写入结果。一致性确保了系统的数据副本之间能够达成一致的状态。
  • 可用性(Availability):每个请求都能在有限的时间内收到一个明确的响应,无论响应是成功的还是失败的。可用性保证了系统能够持续响应客户端的请求。
  • 分区容错性(Partition tolerance):系统能够在任意网络分区发生时继续运行。分区容错性指的是,即使系统内部发生了网络分割,使得某些节点之间无法通信,系统仍然能够对外提供满足一致性或可用性的服务。

实际应用:

在实际的分布式系统设计中,CAP定理提供了一个理论框架来指导如何在一致性、可用性和分区容错性之间做出权衡。不同的系统可能会根据自身的业务需求和特点,选择适合自己的CAP平衡点:

  • CA系统:放弃分区容错性,通常适用于单一数据中心的传统数据库系统。
  • CP系统:放弃可用性,适用于对数据一致性要求极高的系统
  • AP系统:放弃一致性,适用于对可用性要求极高的系统

在分布式环境中,系统节点之间肯定的需要有网络连接的,因此分区(P) 是必然存在的


在现实的分布式系统中,网络分区是不可避免的,因此分区容忍性(P)是必须要满足的。这就意味着我们只能在一致性(C)和可用性(A)之间做出选择。

  • AP系统:在网络分区发生时,选择保证可用性,但牺牲了一致性。这意味着在某些节点上可能读取到的是过时的数据,因为系统为了保证响应的可用性,允许在没有获取到最新数据副本的情况下也能响应客户端的读写请求。
  • CP系统:在网络分区发生时,选择保证一致性,但可能牺牲可用性。这意味着如果数据不能在所有节点间同步,系统可能会拒绝处理读写请求,以保证不会返回错误或过时的数据。
BASE 🚩

BASE理论是对CAP理论的一种补充和延伸,主要面向大规模分布式系统,在牺牲强一致性的前提下,追求系统的可用性和性能。

BASE是三个英文单词的缩写,分别代表:

  • 基本可用(Basically Available):系统允许分布式系统在出现故障的时候,保持核心可用,但是允许部分暂时不可用。这意味着在一些情况下(如网络分区、系统故障),系统仍然能提供基本的服务,即使这些服务可能是降级的。
  • 软状态(Soft State):系统的状态不需要实时一致,允许系统中的数据存在中间状态,并认为这种状态是正常的。与之相对的是传统数据库中数据的“硬状态”,必须保证数据在任何时刻都是一致的。软状态允许系统在不同节点间数据同步存在延迟。
  • 最终一致性(Eventual Consistency):系统保证在没有新的更新操作的情况下,数据最终会达到一致的状态。最终一致性强调的是,所有的更新操作最终将会传播到所有节点上,但并不保证更新操作的实时性和顺序性。

BASE理论提供了一种面向可用性和性能的、对一致性要求相对宽松的设计和实现分布式系统的方法论,强调通过牺牲一致性来获得高可用性和高性能的可能性。

示例:基于Base 理论实现分布式事务

面试题:解释一下 CAP 和 BASE

  • CAP定理(一致性.可用性、分区容错性)
    • 1.分布式系统节点通过网格连接。一定会出现分区问题(P)
    • 2.当分区出现时,系统的一致性(C)和可用性(A)就无法同时满足
  • BASE理论
    • 1,基本可用
    • 2.软状态
    • 3.最终一致
  • 解决分布式事务的思想和模型:
    • 1,最终一致思想:各分支事务分别执行并提交,如果有不一致的情况,再想办法恢复数据(AP)
    • 2.强一致思想:各分支事务执行完业务不要提交。等待彼此结果,而后统一提交或回滚(CP)

这个理解有点问题,待后续回顾一下 to be contined....


二、Nacos

基本介绍

Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。

Nacos支持几乎所有主流类型的服务的“服务发现和服务健康监测”,同时提供动态配置服务,支持配置的动态更新,服务路由和管理等丰富的功能。(主要功能包括服务发现和配置管理)

服务发现 🚩
  1. 服务注册:服务实例在启动时,通过Nacos客户端向Nacos Server注册自己的服务信息(包括服务名、实例IP、端口等)。Nacos Server接收到注册信息后,将其存储在服务注册表中。
  2. 服务同步:在Nacos集群环境下,服务注册信息会在Nacos Server节点间进行同步,确保每个节点的服务注册表信息一致。
  3. 服务健康检查:Nacos Server定期对注册的服务实例进行健康检查,根据心跳机制或者其他健康检查机制来更新服务实例的健康状态。不健康的服务实例会被标记,以防止被调用。
  4. 服务发现:客户端应用通过Nacos客户端向Nacos Server查询目标服务的可用服务实例列表。Nacos Server返回健康的服务实例列表给客户端,客户端根据负载均衡策略选择一个服务实例进行调用。

工作流程将的有点问题,包括服务发现的具体实际操作是怎么样的,细节也没有讲述到。

工作流程 🚩 💣
配置管理
  1. 配置发布:应用或操作人员通过Nacos控制台、API或SDK向Nacos Server发布配置信息。Nacos Server将配置信息存储在配置存储中。
  2. 配置变更通知:当配置信息发生变更时,Nacos Server会通知所有订阅了该配置的客户端应用。
  3. 配置获取:客户端应用启动时,会通过Nacos客户端向Nacos Server查询所需的配置信息。Nacos Server从配置存储中检索配置信息并返回给客户端。
  4. 动态更新:客户端应用在收到配置变更通知后,会重新向Nacos Server请求最新的配置信息,并根据新的配置信息动态调整自己的行为,无需重启应用。
工作原理
  • 数据一致性:Nacos采用Distro(一种AP型的数据一致性协议)和Raft协议来确保数据的一致性。Distro协议用于服务发现的数据同步,而Raft协议用于配置管理和元数据的数据一致性保证。
  • 服务隔离:Nacos支持命名空间、分组等机制,可以实现不同环境或不同应用间的服务和配置隔离。
  • 灵活的负载均衡:Nacos客户端支持多种负载均衡策略,并允许自定义策略。

Nacos 和 Eureka 的区别有哪些?

  • Nacos 和 Eureka 的共同点(注册中心)
    • 1、都支持服务注册和服务拉取
    • 2、都支持服务提供者心跳方式做健康检测
  • Nacos与Eureka的区别(注册中心)
    • 1、Nacos 支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
    • 2、临时实例心桃不正常会被剔除,非临时实例则不会被剔除
    • 3、Nacos 支持服务列表变更的消息推送模式,服务列表更新更及时
    • 4、Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP方式
  • Nacos还支持了配置中心,eureka则只有注册中心,也是选择使用nacos的一个重要原因

配置中心

参考; https://www.yuque.com/snailclimb/mf2z3k/kptwkp

  • 为什么要用配置中心
  • 常用的配置中心有哪些

Apollo和Nacos两者都是国内公司开源的知名项目,项目社区都比较活跃且都还在维护中。

Nacos是阿里开源的,Apollo是携程开源的。

Nacos使用起来比较简单,并且还可以直接用来做服务发现及管理。

Apollo 只能用来做配置管理,使用相对复杂些。

如果你的项目仅仅需要配置中心的话,建议使用 Apollo。如果你的项目需要配置中心的同时还需要服务发现及管理的话,那就更建议使用Nacos。

大概讲一下 Nacos 配置中心是怎么用的,然后带一下 说知道有 Apollo 这个东西,但没有用过。

三、OpenFeign

OpenFeign和Feign本质上指的是同一个技术,不过从历史发展和使用上下文中,它们有细微的区别。

Feign 最初是由Netflix开发的一个开源项目,它内部内置集成了Ribbon(客户端负载均衡)和Hystrix(断路器)等组件。

OpenFeign 是Spring Cloud在Feign的基础上进行了封装和扩展的项目,正式名称为Spring Cloud OpenFeign。它是Spring Cloud组件中的一部分,专门用于微服务架构中的服务间调用。

比起 Feign ,额外提供了一些功能,比如 Spring MVC的注解、自动配置和健康检查等功能。

  • 1、两者都是基于相同的Feign技术,核心理念和基本用法相似,都是通过定义接口和注解来创建HTTP客户端。
  • 2、Feign是Netflix的一个开源项目,而OpenFeign是Spring Cloud项目的一部分,得到了Spring社区的支持和维护。
  • 3、OpenFeign在原有Feign的基础上进行了扩展,更好地集成了Spring Boot和Spring Cloud的特性,提供了更多的配置项和更方便的使用方式。

如果问起负载均衡,一般还是问 Ribbon ,然后 ribbon 是内嵌到了 openfeign,同时还有熔断器的功能。

服务调用流程

讲述一下大致的一个负载均衡的操作步骤:

服务注册 → 服务发现 → 负载均衡 → 服务调用

  • 在Nacos结合OpenFeign的微服务环境下,负载均衡是自动实现的。
    • Nacos作为服务发现和服务注册中心,OpenFeign作为声明式的HTTP客户端调用工具,它们共同工作,提供了一个简洁高效的服务调用流程。
  • 1、服务注册
    • 服务提供者启动:每个微服务在启动时,会将自己的可用实例信息(包括服务名、IP地址、端口等)注册到Nacos服务注册中心。
    • 服务同步:Nacos会将服务信息同步到所有的服务节点,确保每个节点的服务信息是最新的。
  • 2、服务发现
    • 服务消费者启动:服务消费者(客户端应用)启动时,会通过Nacos客户端查询需要调用的服务的实例列表。
    • 获取服务实例:Nacos返回所有健康的服务实例信息给服务消费者。
  • 3、负载均衡
    • 客户端负载均衡:OpenFeign内部集成了Ribbon,Ribbon作为客户端负载均衡器,根据特定的策略(如轮询、随机等)从Nacos获取的服务实例列表中选择一个实例进行调用。
    • 服务调用:选定服务实例后,OpenFeign会构造HTTP请求并发送到目标服务实例。
  • 4、服务调用
    • 服务处理请求:服务提供者接收到请求后,执行相应的业务逻辑,然后返回处理结果给服务消费者。
    • 返回响应:服务消费者接收到响应数据,完成一次服务调用流程。
  • 5、故障处理
    • 断路器:如果服务调用失败(例如,超时或服务实例不可用),OpenFeign集成的Hystrix断路器可以提供备用方案,如返回一个默认值或调用备用服务,避免服务雪崩。

Ribbon默认的负载均衡策略是轮询,但可以根据需要更改策略。

image.png

负载均衡

常见负载均衡策略

Ribbon 常见的负载均衡策略:

  • Round Robin Rule(轮询规则)
    • 这是最简单也是最常用的负载均衡策略。它按顺序循环地选择服务实例,每次请求轮流分配给下一个服务实例。
    • 使用场景:适用于所有服务实例处理能力相当的情况。
  • Random Rule(随机规则)
    • 随机选择一个服务实例。每次请求都是随机从服务列表中选取一个服务实例进行调用。
    • 使用场景:适用于服务实例众多,需要避免轮询带来的均匀性不足问题的场景。
  • Response Time Weighted Rule(响应时间加权规则)
    • 根据服务实例的平均响应时间来计算所有服务实例的权重,响应时间短的服务实例将有更高的权重,被更频繁地选择。
    • 使用场景:适用于服务实例性能差异较大,希望优先调用响应时间短的服务实例的情况。
  • Retry Rule(重试规则)
    • 在一个指定的时间内,如果选定的服务实例不成功,则会重新选择一个服务实例进行尝试,直到获取成功响应或达到最大重试次数。
    • 使用场景:适用于对服务调用的可靠性要求较高的情况,可以增加调用成功的机会。
  • Best Available Rule(最低并发规则)
    • 选择并发请求最小的服务实例。这个策略会考虑到服务实例的并发请求量,优先选择并发量最小的实例。
    • 使用场景:适用于需要平衡服务实例负载,避免某些实例过载的场景。
  • Availability Filtering Rule(可用性过滤规则)
    • 先过滤掉一直连接失败的被标记为circuit tripped的服务实例,然后选择一个并发量最小的实例。
    • 使用场景:适用于需要剔除不稳定服务实例,确保服务调用的高可用性。
  • Zone Avoidance Rule(区域权重规则)
    • 综合区域的性能和服务实例的可用性来选择服务器。尽可能地选择一个较少负载且可用性较高的服务实例。
    • 适用于跨多个区域(如AWS区域)部署的服务,需要考虑区域性能和可用性的场景。

一般问到负载均衡常用的策略也可以从常见的几个策略谈一下:轮询、随机、响应时间权重、区域权重。

具体的负载均衡策略配置是怎么操作的:

在Spring Cloud应用中,具体配置Ribbon的负载均衡策略通常涉及到application.properties或application.yml配置文件的修改,或者通过编码的方式自定义配置。

下面是两种常见的配置方法:

通过配置文件配置

application.ymlapplication.properties中,可以为所有服务或特定服务设置负载均衡策略

application.yml示例

为所有服务设置轮询策略:

ribbon:
  NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule

为特定服务设置随机策略:

user-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

你也可以通过编写Java配置类来自定义Ribbon的负载均衡策略,这种方法更加灵活,允许更复杂的配置。

下面是一个为user-service服务自定义随机负载均衡策略的例子:

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RibbonConfiguration {

    @Bean
    public IRule ribbonRule() {
        // 使用随机负载均衡策略
        return new RandomRule();
    }
}

然后,你需要在application.propertiesapplication.yml中指定这个配置类应用于哪个服务:

application.yml

user-service:
  ribbon:
    NFLoadBalancerClassName: com.example.demo.config.RibbonConfiguration

注意事项

  • 当通过编码方式自定义Ribbon配置时,确保不要将@Configuration配置类放在主应用程序上下文的@ComponentScan中,以避免该配置成为全局配置。推荐的做法是将Ribbon的配置类放在主应用类的包外,或者显式指定@ComponentScan的扫描路径排除这些配置。
  • Spring Cloud中,Feign已默认集成了Ribbon,所以通过Feign调用服务时,也可以利用这些负载均衡策略。

通过这些方法,可以灵活地为应用配置合适的负载均衡策略,优化服务调用的性能和可靠性。

自定义负载均衡策略

在上文中实际已经讲述了自定义负载均衡策略的实现:

  • 1、创建类实现 IRule 接口,指定负载均衡策略(全局)
  • 2、在客户端的配置文件中,配置某一个服务指定的负载均衡策略(局部)

image.png

关于【创建类实现 IRule 接口,指定负载均衡策略】的进一步说明

首先是这种方式和【在配置文件中为所有服务统一设置负载均衡策略】的区别:

  • 1、对于大多数标准使用场景,通过配置文件设置负载均衡策略更容易管理和调整。尤其是在微服务架构中,当服务数量较多时,能够快速统一调整策略。(后者的一个作用是简化配置)
  • 2、通过编码方式实现IRule接口,可以创建一个自定义的负载均衡策略,这种方法提供了极高的灵活性和控制力。你可以根据具体的业务需求编写负载均衡逻辑,实现一些标准策略(如轮询、随机)之外的特殊需求。(前者可以定制化一些策略,不过复杂性会高一些)

针对【创建类实现 IRule 接口,指定负载均衡策略】是全局生效的进一步说明

看了一种解释,是说:并不是都是全局,一般是全局,但是也可以针对特定服务。

创建类实现IRule接口来指定负载均衡策略,然后在Spring配置中将这个自定义的规则应用到Ribbon客户端上,这种做法可以是全局的,也可以是针对特定服务的,具体取决于如何配置这个自定义规则。

全局配置

如果你创建了一个实现IRule接口的类来定义一个自定义的负载均衡策略,并且在Spring的主配置类(@SpringBootApplication所在的类)中通过@Bean注解注册了这个规则,那么它会被应用为全局的负载均衡策略。

这意味着所有使用Ribbon客户端进行服务调用的地方都会使用这个自定义的负载均衡规则。

@Configuration
public class RibbonConfiguration {
    @Bean
    public IRule ribbonRule() {
        return new MyCustomRule();
    }
}

特定服务配置

如果你想要将自定义的负载均衡策略应用到特定的服务上,而不是全局应用,你可以通过创建一个Ribbon的配置类并使用@RibbonClient注解来指定这个配置类应用到哪个服务上。这个配置类不应该被主应用程序上下文的@ComponentScan所扫描,以避免它成为全局配置。通常,这意味着将这个配置类放在主应用程序类所在包的外面,或者在@ComponentScan注解中显式排除它。

@Configuration
public class UserRibbonConfiguration {
    @Bean
    public IRule ribbonRule() {
        return new MyCustomRuleForUserService();
    }
}

@RibbonClient(name = "user-service", configuration = UserRibbonConfiguration.class)
public class ServiceConfig {
}

在这个例子中,MyCustomRuleForUserService只会应用到user-service服务上,而不会影响其他服务。

区别

  • 全局配置:通过在Spring主配置中注册自定义IRule实现,它会被应用到所有通过Ribbon调用的服务上。这种方式简单,但缺乏针对性。
  • 特定服务配置:通过为特定服务创建独立的Ribbon配置类并使用@RibbonClient注解来指定,可以实现对不同服务使用不同的负载均衡策略。这种方式提供了更高的灵活性和针对性。

服务调用

演示一下使用 openfegin 进行服务调用的操作:

to be contined...

这里演示一下 指定Fallback 相关示例(服务降级)


服务雪崩

服务雪崩是分布式系统中一个常见的问题,指的是当某个(或某些)微服务不可用或响应时间过长时,会导致调用该服务的其他服务也跟着变慢或不可用,这种情况会像雪崩一样向整个系统蔓延,最终可能导致整个系统崩溃。

  • 服务雪崩:一个服务失败,号致整条线路的服务都失败的情形
  • 解决方案
    • 服务降级:服务自我保护的一种方式,或者保护下游服务的一种方式,用于确保服务不会受请求突增影响变得不可用,确保服务不会崩溃,一般在实标开发中与 feign 接口整合,编写降级逻辑
    • 服务熔断:默认关闭。需要手动打开,如果检测到10秒内请求的失败率超过50%,就触发熔断机别。之后每隔 5秒重新尝试请求微服务,如果微服务不能响应,继续走熔断机制,如果微服务可达,则关闭熔断机制,恢复正常请求

四、限流操作

image.png

限流的常见实现方式:

  • 1、Tomcat: 可以设置最大连接数
    • 不过这种方式适合单体项目,分布式项目不太适合
  • 2、Nginx,漏桶算法
  • 3、网关,令牌桶算法
  • 4、自定义拦截器

先掌握了解一下第二种和第三种方式

面试题:你们项目中有没有做过限流,怎么做的。

  • 1、先来介绍业务,什么情况下去做限流,需要说明QPS具体多少
    • 我们当时有一个活动,到了假期就会抢购优惠券,QPS 最高可以达到2000,平时10-50之间,为了应对突发流量,需要做限流
    • 常规限流,为了防止恶意攻击,保护系统正常运行,我们当时系统能够承受最大的QPS是多少(压测结果)
  • 2、nginx限流
    • 控制速率(突发流量),使用的漏桶算法来实现过滤,让请求以固定的速率处理请求,可以应对突发流量
    • 控制并发数,限制单个ip 的链接数和并发链接的总数
  • 3、网关限流
    • 在spring cloud gateway中支持局部过滤器RequestRateLimiter来做限流,使用的是令牌桶算法,可以根据或路径进行限流,可以设置每秒填充平均速率,和令牌桶总容量。

限流常见的算法有哪些呢

可以讲一下令牌桶算法和漏桶算法。

讲一些扩展内容

1、一般通过什么做压测的

就讲使用 JMeter 即可

具体需要看实际使用 to be contined....

Nginx 限流

  • 控制访问请求速率
  • 控制并发连接数(某个用户建立的连接数)

Nginx提供了内置的模块来支持限流,如limit_req_modulelimit_conn_module,它们可以帮助你根据请求速率或并发连接数来限制流量。

在实践中,通常会结合使用这两种方法来更全面地控制流量,确保服务既不会因为瞬时请求过多而崩溃,也不会因为过多并发连接而耗尽资源。

使用Nginx进行限流只是应对流量过载的一种手段,还需要结合应用层的限流、服务降级、熔断等策略来综合保障系统的稳定性和可用性。

控制速率 limit_req_module

可以理解Nginx 进行控制请求速率限制是类似漏桶算法的一种实现(应用了漏桶算法的思想);

  1. 桶容量:算法中的“桶”有一个固定的容量,这个容量可以理解为系统能够承受的最大流量或请求速率的上限。
  2. 入水速率:数据(或请求)以任意速率流入桶中。如果流入速率不定,那么这些数据会暂时存储在桶中,直到被处理。
  3. 出水速率:数据以固定的速率从桶中“漏出”(即被处理)。出水速率是预先设定的,与实际流入速率无关。
  4. 溢出处理:如果流入的数据量超过了桶的容量,即桶已满,那么超出的数据会被丢弃(或者触发其他的限流措施),这可以防止系统过载

通过这种方式的限流,请求流量会以固定的速率从桶中“漏出”(即被处理);如果流入的数据量超过了桶的容量,即桶已满,那么超出的数据会被丢弃。

image.png

使用limit_req_module进行请求速率限制

limit_req_module模块允许你限制处理请求的速率,防止请求过多导致服务不可用。这个模块通常用于限制每个客户端的请求速率。

  1. 定义限流规则:首先,需要在Nginx配置中定义一个限流规则(limit_req_zone),指定限流的键(如客户端IP)、速率和存储区域大小。
http {
    limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;
    ...
}

这里定义了一个名为mylimit的区域,大小为10MB,限制每个IP地址每秒最多只能处理1个请求。

应用限流规则:然后,在需要限流的location或server上应用这个规则(limit_req)。

server {
    location / {
        limit_req zone=mylimit burst=5;
        ...
    }
}

这里在/位置应用了mylimit规则,并设置了burst参数为5,意味着允许在短时间内超过限制的请求被缓存起来,直到达到5个请求的限制。

控制并发连接数

image.png

使用limit_conn_module进行并发连接数限制

limit_conn_module模块允许你限制并发连接数,这适用于限制每个客户端可以同时建立的连接数。

  1. 定义连接数限制:同样,首先定义一个限制区域(limit_conn_zone)。
http {
    limit_conn_zone $binary_remote_addr zone=connlimit:10m;
    ...
}

这里定义了一个名为connlimit的区域,大小为10MB,用于存储并发连接数的状态。

  1. 应用连接数限制:在需要限制的location或server上应用这个规则(limit_conn)。
server {
    location / {
        limit_conn connlimit 10;
        ...
    }
}

这里在/位置应用了connlimit规则,限制每个IP最多可以同时建立10个连接。


网关限流 🚩

网关限流一般是使用 Spring Cloud Gateway 来实现网关层面的流量控制。

image.png

围绕网关限流可用先回答一下项目是如何做的:比如说是:利用RequestRateLimiter过滤器,结合Redis来存储和管理限流数据。

然后说一下配置文件中的参数中是应用了令牌桶算法的思想

还可以结合 Gateway + Sentinel 实现流控(带过)

令牌桶算法

令牌桶算法(Token Bucket Algorithm)是一种流量控制算法,广泛应用于网络流量整形(Traffic Shaping)和速率限制(Rate Limiting)中。该算法的核心思想是以恒定的速率向桶中添加令牌(token),请求消耗令牌才能被处理,如果桶中没有令牌,则请求被限制。

基本原理

  1. 令牌桶:有一个固定容量的桶,系统以固定速率往桶里添加令牌。如果桶满了,则新添加的令牌会被丢弃。
  2. 请求处理:每个传入的请求需要从桶中取出一定数量的令牌。如果桶中的令牌足够,请求被处理,同时从桶中移除对应数量的令牌;如果桶中令牌不足,请求则根据策略(如排队、立即拒绝等)处理。
  3. 恒定速率:令牌以恒定的速率r被添加到桶中。这个速率r决定了请求被允许的最大平均速率。
  4. 突发流量:桶的容量允许在短时间内处理突发流量。如果桶中有足够的令牌,超过平均速率r的请求可以立即被处理,直到桶中的令牌被耗尽。

在Spring Cloud Gateway中使用Redis RateLimiter实现限流时,其背后的原理就借鉴了令牌桶算法。

通过定义replenishRateburstCapacity参数,分别对应令牌添加的速率和桶的容量,从而实现对网关流量的细粒度控制。当请求到达网关时,会尝试从Redis中的令牌桶获取令牌,如果获取成功,则请求被允许通过;如果令牌不足,则请求被限流。

  • 令牌添加速率
  • 桶容量

具体使用示例:

Gateway 网关限流

  • 1、在Spring Cloud Gateway中实现网关限流,我们通常会利用RequestRateLimiter过滤器,结合Redis来存储和管理限流数据。
  • 2、还有一种是 Gateway + Sentinel 结合实现流量控制

这里说一下第一种,第二种感觉引入组件造成系统复杂度提升,并没有采用(从这个角度回答)

第一种方式

以下是一个基于Spring Cloud Gateway与Redis实现网关限流的示例。

  • 1、添加Spring Cloud Gateway和Spring Boot Redis的依赖
  • 2、配置 Redis
  • 3、定义限流规则

在Spring Cloud Gateway中通过配置文件定义路由和限流规则。在这个示例中,我们为/api/myService/**路径配置了限流规则。

spring:
  cloud:
    gateway:
      routes:
        - id: myservice_route
          uri: lb://MY-SERVICE # 假设MY-SERVICE是你的微服务名称
          predicates:
            - Path=/api/myService/**
          filters:
            - name: RequestRateLimiter
              args:
                # 每秒钟允许通过的请求数
                redis-rate-limiter.replenishRate: 10
                # 用户在一秒钟内可以突发的请求数量
                redis-rate-limiter.burstCapacity: 20
                # 使用SpEL表达式来确定限流的key
                key-resolver: "#{@ipKeyResolver}"
  1. 实现KeyResolver

key-resolver用于确定限流的维度,例如可以按IP地址限流。下面是按IP地址限流的KeyResolver实现。

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Configuration
public class RateLimiterConfiguration {

    @Bean
    KeyResolver ipKeyResolver() {
        return new KeyResolver() {
            @Override
            public Mono<String> resolve(ServerWebExchange exchange) {
                // 从请求中获取IP地址,作为限流的key
                return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
            }
        };
    }
}
  1. 启动并测试

启动Spring Cloud Gateway应用,并向/api/myService/**路径发送请求来测试限流效果。

如果请求速率超过了定义的replenishRateburstCapacity,超出限流条件的请求将会收到429 Too Many Requests响应。

注意事项

  • 实际部署时,Redis的配置应根据你的环境进行调整,包括考虑Redis集群等高可用配置。
  • 限流参数replenishRateburstCapacity应根据实际业务场景合理设置,避免过度限流影响用户体验。
  • key-resolver的实现可以根据实际需要选择不同的限流维度,除了IP地址外,还可以按用户ID、API路径等维度进行限流。

五、服务监控

为什么需要监控

一般目的是为了:

  • 问题定位
  • 性能分析
  • 服务关系
  • 服务告警

监控工具

  • Springboot-admin
  • prometheus+Grafana
  • 链路工具
    • zipkin
    • skywalking

大概看一下这个用法:prometheus+Grafana

如果实际问到,就回答自己回答有这个监控方式,以及如何使用的。

Prometheus和Grafana是一对强大的监控和可视化工具组合,广泛用于监控微服务架构中的应用和基础设施的健康状况、性能指标等。这种组合的用法主要涉及到三个步骤:数据收集(Prometheus),数据存储(Prometheus),以及数据可视化(Grafana)。

步骤1: 数据收集

Prometheus通过拉模式(pull)或推模式(push,需要使用Pushgateway)从应用程序和基础设施中收集指标数据。通常,应用程序和服务会暴露一个HTTP端点(通常是/metrics),Prometheus定时从这些端点抓取(scrape)数据。

为了让Prometheus能够收集数据,你需要在Prometheus的配置文件(通常是prometheus.yml)中定义抓取目标和频率。例如:

scrape_configs:
  - job_name: 'spring-application'
    scrape_interval: 15s
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['localhost:8080']

数据采集这里要求 SpringBoot 应用对外开发 /metrics 接口,实际操作通常是通过集成Spring Boot Actuator和Micrometer来实现的。 🚩

Spring Boot Actuator提供了一系列内置的端点,包括/metrics,用于监控和管理应用。Micrometer则作为Spring Boot的维度化指标库,充当了应用和监控系统之间的桥梁。

  • 引入依赖:确保在Spring Boot项目的pom.xml文件中引入了Spring Boot Actuator和Micrometer Prometheus的依赖。
  • 配置应用:确保application.propertiesapplication.yml配置文件中启用了/metrics端点。Spring Boot 2.x默认启用了大部分Actuator端点,包括/metrics
  • 暴露/metrics端点
  • 也可以自定义指标

步骤2: 数据存储

Prometheus会将收集到的数据存储在本地的时间序列数据库中。Prometheus的存储引擎专为时间序列数据优化,支持高效的数据存储、查询和管理。

步骤3: 数据可视化

Grafana是一个开源的数据可视化工具,支持从多种数据源(包括Prometheus)查询和展示数据。要使用Grafana展示Prometheus的数据,你需要:

  1. 安装并启动Grafana:下载并安装Grafana,然后启动Grafana服务。
  2. 配置数据源:登录Grafana的Web界面,添加一个新的数据源,选择Prometheus作为类型,并配置Prometheus服务器的URL。
  3. 创建仪表板:在Grafana中创建新的仪表板,可以添加多种类型的面板(如图表、表格、单值指标等)来展示Prometheus收集的指标数据。
  4. 查询和展示数据:在仪表板的面板中,使用PromQL(Prometheus查询语言)编写查询,从Prometheus中检索数据,并根据需求配置面板的显示方式。

六、生产问题 🚩

分布式事务

参考这篇文章整合一下相关内容: https://www.yuque.com/snailclimb/mf2z3k/ng9vmg

  • 简历上写的微服务,只要是发生了多个服务之间的写操作,都需要进行分布式事务控制
  • 描述项目中采用的那种方案(seata|MQ)
    • 1.seata的XA模式,CP,需要互相等待各个分支事务提交,可以保证强一致性,性能差 银行业务
    • 2.seata的AT模式,AP,底层使用undo log实现,性能好 互联网业务
      1. seata的TCC模式,AP,性能较好,不过需要人工编码实现 银行业务
  • 4.MQ模式实现分布式事务,在A服务写数据的时候,需要在同一个事务内发送消息到另外一个事务,异步,性能最好 互联网业务

微服务项目

  • Seata 框架(XA、AT、TCC)
  • MQ
Seata 📕

Seata是一个开源的分布式事务解决方案,旨在提供高性能和简单易用的分布式事务服务。

在Seata中,主要提供了三种事务模式:XA、AT和TCC,以满足不同场景下的分布式事务需求。


Seata 架构

Seata事务管理中有三个重要的角色:

  • TC(Transaction Coordinator)-事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚。
  • TM(Transaction Manager)-事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务。
  • RM(Resource Manager)-资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

image.png

Seata 中有三种模式,分别是 XA、AT 和 TCC

  • XA 模式
  • AT 模式
  • TCC 模式

XA 模式

XA模式基于XA协议实现,是一种两阶段提交的分布式事务解决方案。XA模式涉及到事务管理器(TM)、资源管理器(RM)和事务协调者(TC)三个角色。

  • 第一阶段(准备阶段):事务管理器指示所有参与当前事务的资源管理器准备提交事务。
  • 第二阶段(提交/回滚阶段):如果所有资源管理器都准备就绪,事务管理器会指示它们提交事务;如果任何资源管理器不能准备就绪,事务管理器会指示所有资源管理器回滚事务。

XA模式保证了跨数据库和系统的事务一致性,但实现复杂,性能开销较大。

image.png

AT 模式

AT模式是Seata自主创新的一种事务模式,全称为Automatic Transaction,即自动化事务模式。它通过对数据进行前后镜像记录来实现分布式事务。

  • 提交事务:在事务提交时,如果执行成功,则直接提交事务;如果执行失败,利用镜像记录进行回滚。
  • 回滚事务:在回滚时,利用之前记录的前镜像和后镜像进行数据的恢复。

AT模式不依赖XA协议,具有较好的性能,易于理解和使用,是Seata中使用最广泛的模式。

image.png

TCC 模式

TCC模式即Try-Confirm-Cancel模式,是一种补偿性的事务模式。它将业务操作拆分为三个步骤:尝试(Try)、确认(Confirm)和取消(Cancel)。

  • Try阶段:检查并预留业务资源。
  • Confirm阶段:如果所有服务的Try阶段都成功,那么执行业务操作。
  • Cancel阶段:如果任何服务的Try阶段失败,那么执行撤销操作,释放在Try阶段预留的资源。

TCC模式提供了较为灵活的事务控制能力,适用于对一致性要求高的业务场景。但是,开发成本较高,需要用户手动实现Try、Confirm和Cancel三个操作。

image.png

总结;

  • XA模式:基于XA协议的两阶段提交,保证事务的ACID特性,实现复杂,性能开销大。
  • AT模式:通过数据镜像记录实现分布式事务,简单易用,性能较好,是Seata的默认事务模式。
  • TCC模式:通过显式的Try-Confirm-Cancel操作来控制事务,灵活性高,适合对一致性要求高的场景,但实现成本较高。

暂时还不太理解,到时候就说一下关于分布式事务

其中一种解决方案是使用 Seata 进行一个分布式事务的管理,默认采用的是 AT 模式,事务提交的时候会保留一个数据快照,如果整个事务有执行失败的话,则执行回滚操作。(大致 Get 一下,详细的后面再了解一下)

关于其他的 AC、XA、TCC 是知道有这个,但具体不太清楚。然后角色记一下:TM 事务管理器,RM 资源管理器,TC 事务协调者。


MQ 🚩 💣

需要看一下这个啥,分布式事务MQ 怎么试下难点

通过消息队列(MQ)实现分布式事务是一种常见的解决方案,这种方法通常被称为最终一致性方案或异步确保型方案。它利用消息队列的可靠性来保证分布式事务的一致性,主要应用于对实时性要求不是非常高,但对系统可用性和一致性要求较高的场景。

通过MQ实现分布式事务的基本思路是将事务的执行分成两个阶段:

  1. 本地事务执行:首先在发送方执行本地事务,并将要执行的跨服务操作封装成消息发送到消息队列中。此时,消息处于不可被消费状态。
  2. 消息确认与消费:当本地事务成功提交后,消息变为可消费状态,接收方从消息队列中拉取消息并执行相应的操作。如果操作成功,完成整个分布式事务;如果操作失败,可以通过重试机制再次尝试,或者执行补偿操作以保证数据一致性。

实现MQ分布式事务通常有两种方式:

  1. 半消息机制:这是一种较为复杂的实现方式,发送方首先发送一条“半消息”(即预备消息)到MQ,这条消息暂时不会被消费端消费。发送方执行本地事务,本地事务成功后再发送另一条确认消息给MQ,MQ收到确认消息后,将半消息标记为可消费,消费者此时可以正常消费这条消息。

  2. 事务消息:某些MQ支持事务消息的特性,例如Apache RocketMQ。事务消息允许发送方在发送消息的同时执行本地事务,如果本地事务执行成功,则提交消息使其可被消费;如果本地事务执行失败,则回滚消息,使其不会被消费。

接口幂等性

分布式服务的接口幂等性如何设计?

幂等:多次调用方法或者接口不会改变业务状态,可以保证重复调用的结果和单次调用的结果一致。

需要幂等场景:

  • 1、用户重复点击(网络波动)、
  • 2、MQ 消息重复
  • 3、应用使用失败或者超时重试机制

在 Restful API 的角度,看常见类型请求的幂等性分析:

image.png

token + redis
分布式锁

setnx + redission

image.png


面试题回答:

image.png

七、任务调度

分布式任务调度 xxl-job

image.png

路由策略

image.png

失败重试

xxl-job 任务执行失败怎么解决?

设置故障转移 + 失败重试,查看日志分析 → 邮件告警

image.png

分片广播

如果有大数据量的任务同时都需要执行,怎么解决?

image.png

面试题回答;

image.png

八、日志管理

这部分先不管,大概说一下,实际项目中的一种解决方案是怎么做的就行

to be contined....

后续打算优化可用看一下这篇文章: https://www.yuque.com/snailclimb/mf2z3k/ma1byh


参考

引用