Skip to content

面试煎熬成蛋_SpringCloud

基础概念

什么是 SpringCloud

SpringCloud 作为 Spring 家族重要的一环,它整合了多种微服务解决方案,为微服务架构提供了全面的支持,使得开发者可以更加专注于业务逻辑而非基础设施。

SpringCloud 通过Spring Boot风格进行封装,使得开发者可以轻松地构建分布式系统的各种组件。

SpringCloud VS SpringBoot

  • Spring Boot ,专注于快速,方便的开发单个微服务个体。
  • Spring Cloud ,关注全局的服务治理框架。

说一下常用组件 🚩

Spring Cloud Netflix 第一代

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

Spring Cloud Alibaba 第二代

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

image.png

讲一下 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 和 Eureka 的区别有哪些?

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

讲一下 Nacos 的工作流程

  • 服务发现
    • 服务注册,服务发现,健康检查,服务注销
  • 配置管理

image.png

为什么要用配置中心

进行动态配置和统一管理

常用的配置中心有哪些

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

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

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

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

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

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

Nacos 是去中心的吗,如果服务挂了,是否可以继续进行访问操作

参考: https://wenku.csdn.net/answer/c5a9f976aa15481caf8ac84f2e036e46

Nacos 一般会有两种模式,一种是集群模式,还有一种是持久化模式

  • 集群模式下,如果一个节点挂了,其他节点仍然是能提供后续服务
  • 持久化模式下,nacos 服务如果挂了,服务之间仍然可以通过本地保存的注册信息进行通信。

nacos的挂掉对服务之间的相互访问有影响,但可以通过 nacos 的集群模式和持久化模式来避免这种影响。

OpenFeign

OpenFeign 的原理你了解吗 🚩

OpenFeign 通过动态代理的方式封装了一系统操作,比如:建立连接、构造请求、发起靕求、获取响应、解析响应等

大概讲一下实现原理

  • 在默认配置下,Feign 客户端会与 Ribbon 和 Hystrix 集成,以提供负载均衡和断路器功能

  • 首先,如果你对某个接口定义了 @FeignClient 注解,Feign就会针对这个接口创建一个动态代理

  • 你要是调用那个接口,本质就是会调用 Feign创建的动态代理,这是核心中的核心

  • Feign的动态代理会根据你在接口上的@RequestMapping等注解,来动态构造出你要请求的服务的地址

  • 最后针对这个地址,发起请求、解析响应

image.png

一般代码使用

  • 1、启动类上添加 @EnableFeignClients 注解
  • 2、使用 @FeignClient 注解定义接口,并指定需要调用的服务名称
  • 3、Controller 层正常提供对外的 RestController 接口

远程调用的时候你遇到过什么问题,怎么解决的 🚩

遇到过一些问题

  • 网络超时,接口调用时间过长
    • 配置超时时间:在远程调用时,设置合理的超时时间,避免长时间的阻塞。
      • 在 Spring Cloud Feign 中可以配置超时时间:
    • 使用熔断器:采用熔断机制(如 Hystrix 或 Resilience4j),在多次调用失败后断开连接,防止系统过载。
      • fallback 方法
  • 服务不可用
    • 服务降级:通过熔断器或其他方式提供降级策略,在服务不可用时返回默认值或缓存数据。
    • 重试机制:配置重试策略,在调用失败后重试几次,以应对临时性的问题。
  • 负载不均衡
  • 数据一致性
    • 确保这种调用,最后能够保证最终一致性
  • 安全性问题
    • 使用 OAuth2 或 JWT 对远程调用进行身份验证和授权
    • 使用 HTTPS 或其他加密协议保护数据传输安全
  • 定位问题
    • 远程调用失败时,调试和监控困难,难以快速定位问题。
      • 在远程调用前后记录详细的日志信息,便于排查问题。
      • 使用分布式跟踪系统(如 Zipkin 或 Jaeger)跟踪远程调用链路,分析性能瓶颈

讲一下 OpenFeign 的 Fallback 原理

远程调用整个架构怎么设计的,少的话几个,多的话几百个、上千个接口如何管理?

  • 服务注册与发现
  • 负载均衡
  • API 管理平台

扩展

  • 缓存
  • 限流与熔断
  • 异步通信
  • 监控日志

Ribbon

讲一下 Ribbon 的作用

  • 均匀的把请求分发到各个机器上

实现工作流程

  • 1、服务列表维护:Ribbon 从注册中心(如 Eureka、Nacos)获取服务实例列表,并定期更新。它维护一份本地缓存,包含所有可用服务实例的信息。
  • 2、负载均衡策略:Ribbon 提供多种负载均衡策略(如轮询、随机、加权等),可以通过配置选择合适的策略。默认策略是轮询。
  • 3、服务调用:在客户端发起请求时,Ribbon 会根据选定的负载均衡策略,从服务实例列表中选择一个实例,并将请求转发到该实例。

Ribbon 常见的负载均衡策略有哪些

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注解来指定,可以实现对不同服务使用不同的负载均衡策略。这种方式提供了更高的灵活性和针对性。

负载均衡策略

获取 URL路径

Ribbon 的底层原理 🚩

Ribbon 的底层原理包括获取服务实例列表、应用负载均衡策略选择服务实例、提供重试机制以及与服务发现组件的集成。

通过这些机制,Ribbon 可以在客户端实现高效的负载均衡,使得分布式系统中的服务调用更加稳定和高效。

Hystrix

讲一下 hystrix 的作用

  • Hystrix隔离、熔断和降级

一般如何使用的

使用示例

在 Spring Boot 应用的主类上添加 @EnableFeignClients@EnableHystrix 注解:

@SpringBootApplication
@EnableFeignClients
@EnableHystrix
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

使用 @FeignClient 注解定义 Feign 客户端接口,并指定降级处理类

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@FeignClient(name = "service-provider", fallback = HelloClientFallback.class)
public interface HelloClient {
    @GetMapping("/hello")
    String sayHello();
}

创建一个实现 HelloClient 接口的降级处理类:

import org.springframework.stereotype.Component;

@Component
public class HelloClientFallback implements HelloClient {
    @Override
    public String sayHello() {
        return "Fallback response";
    }
}

在控制器中使用 Feign 客户端:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @Autowired
    private HelloClient helloClient;

    @GetMapping("/hello")
    public String hello() {
        return helloClient.sayHello();
    }
}

服务雪崩是什么

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

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

服务雪崩

  • 如果某个服务不可用,依赖该服务的其他服务也无法正常工作,从而导致整个依赖链中的服务都不可用
  • 可能造成现象的原有
    • 1、资源耗尽,请求量太大了,服务处理不过来(没有进行限流)
    • 2、服务挂了
    • 3、配置问题

Gateway

讲一下 网关的作用

  • 网关:如果前端、移动端要调用后端系统,统一从网关进入,由网关转发请求给对应的服务

Eureka 那一套的流程,思路可以借鉴一下

image.png

Spring Cloud Gateway 是一个基于 Spring WebFlux 框架的API 网关,它提供了一种简单且有效的方式来路由请求、处理认证与授权、进行负载均衡、监控和限流等操作.

核心组件

  • Route 路由
    • Route 是 Gateway 中的基本构建块。每个 Route 都定义了一个匹配的条件(Predicate)和一个处理请求的逻辑(Filter)
    • Route 包含:
      • ID:唯一标识路由的字符串。
      • URI:目标服务的地址。
      • Predicates:一组条件,用于匹配请求。例如,根据请求路径、方法、头部等进行匹配。
      • Filters:一组过滤器,用于对请求或响应进行处理
  • Predicate 谓词
    • Predicate 用于匹配进入的 HTTP 请求。如果请求匹配,则该请求会被路由到指定的 URI。
    • 常见的谓词有:路径匹配、头部匹配、方法匹配等。
  • Filter 过滤器
    • Filter 用于对请求和响应进行处理。可以在请求被路由之前或之后执行各种操作,例如修改请求头、限流、日志记录等。
    • 过滤器分为两类:
      • Global Filters:全局过滤器,作用于所有路由。
      • Route-Specific Filters:特定路由的过滤器,只作用于指定的路由。

工作流程

image.png

示例

application.yml 中配置路由示例:

spring:
  cloud:
    gateway:
      routes:
        - id: example_route
          uri: http://example.com
          predicates:
            - Path=/example/**
          filters:
            - AddRequestHeader=Example, HeaderValue

在这个配置中:

  • 定义了一个 ID 为 example_route 的路由。
  • 当请求路径匹配 /example/** 时,请求会被转发到 http://example.com
  • 在请求被转发之前,会添加一个请求头 Example: HeaderValue

核心功能

image.png

  • 日志记录
    • 一般会定义全局过滤器 GlobalFilter(继承),然后重写 filter 方法,在方法中记录请求和响应日志
  • 动态路由
    • 一般结合 nacos 使用
  • 负载均衡
    • 结合 Openfeign 使用,常见的一种用法

动态路由

application.yml 中配置 Nacos。

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true

在 Nacos 控制台中,创建或修改路由配置,格式与 application.yml 类似。

负载均衡

Spring Cloud Gateway 默认使用 Spring Cloud LoadBalancer 进行负载均衡。以下是一些常见的配置和使用示例

application.yml 中配置负载均衡。

spring:
  cloud:
    gateway:
      routes:
        - id: loadbalancer_route
          uri: lb://service-provider
          predicates:
            - Path=/loadbalancer/**
          filters:
            - AddRequestHeader=LoadBalancer, HeaderValue

这里 lb://service-provider 表示将请求负载均衡到 service-provider 服务实例上

Gateway 原理大概讲一下

  1. 请求匹配

当客户端发出 HTTP 请求时,Gateway 会根据配置的 Route 和 Predicate 对请求进行匹配。匹配过程如下:

  1. Predicate Matching:Gateway 会遍历所有的路由,根据 Predicate 对请求进行匹配,找到符合条件的路由。

  2. Filter Execution:对于匹配的路由,执行其配置的 Filter 以及全局 Filter。

  3. Request Forwarding:处理完过滤器后,将请求转发到 Route 定义的目标 URI(目标服务)。

  4. 请求处理流程

  5. 客户端请求:客户端发送一个 HTTP 请求到 Gateway。

  6. 请求匹配:Gateway 根据定义的 Route 和 Predicate 对请求进行匹配。

  7. 过滤器链:如果请求匹配某个 Route,Gateway 会执行该 Route 上定义的 Filter 以及全局 Filter。

  8. 请求转发:经过过滤器链处理后,请求会被转发到 Route 定义的 URI(目标服务)。

  9. 响应处理:目标服务返回响应,Gateway 会再经过过滤器链处理响应,然后返回给客户端。

Gateway 的版本区分

版本差异

  • 2.0.x 版本:引入了基于WebFlux的反应式编程模型,不再使用传统的Servlet API。提供了基本的路由、过滤功能。
  • 2.1.x 版本:增加了对WebSocket的支持,改进了路由断言和过滤器的功能。
  • 2.2.x 版本:引入了更多的路由断言和过滤器,增强了安全性和可管理性。
  • 3.0.x 版本:更新了底层的Spring Boot和Spring Framework的版本,支持最新的功能,例如更好的错误处理和性能优化。

一般说我们常见的是2.2 版本就行

Sentinel

基本原理

Sentinel 的限流主要是通过在方法或接口前后增加拦截逻辑来实现的。当请求达到某个接口或方法时,Sentinel 会检查当前的请求流量是否超过了预设的限流规则,如果超过则进行限流处理(如拒绝请求、排队等)

主要的限流方式

  1. QPS(Queries Per Second)限流:基于每秒请求次数的限流。
  2. 线程数限流:基于并发线程数的限流。
  3. 流控排队模式:基于排队等待时间的限流

Sentinel 控制台提供了可视化的限流规则配置界面,可以动态调整限流规则。

  • 1、控制台
  • 2、代码配置
  • 3、配置类

Zipkin

  • 查询服务链路,并排查问题
  • 监听服务问题,并及时反馈

Xxl-job

你们项目中使用了什么分布式任务调度

image.png

xxl-job 路由策略有哪些?

  • 轮询、故障转移、分片广播

image.png

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

重试 + 告警

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

image.png

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

分片广播策略

image.png

面试题回答;

image.png

分布式事务

讲一下分布式事务的方案有哪些,你们项目中怎么处理的

参考这篇文章整合一下相关内容: 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。事务消息允许发送方在发送消息的同时执行本地事务,如果本地事务执行成功,则提交消息使其可被消费;如果本地事务执行失败,则回滚消息,使其不会被消费。

讲一下分布式事务 🚩

分布式事务是一个复杂但重要的问题。分布式事务指的是跨多个独立的服务或数据库实例的事务操作。

这些操作需要在不同的服务中保持数据一致性和可靠性。

常见的解决方案

  • 二阶段提交 2PC
    • 准备阶段
      • 协调者向所有参与者发送准备请求,询问是否可以提交事务。
      • 每个参与者执行事务操作,但不提交,并将操作结果告知协调者。
    • 提交阶段
      • 如果所有参与者都同意提交,协调者向所有参与者发送提交请求,参与者提交事务。
      • 如果有任一参与者不同意提交或超时未响应,协调者向所有参与者发送回滚请求,参与者回滚事务。
  • 三阶段提交 3PC
    • 准备阶段
      • 协调者询问参与者是否可以提交事务。
    • 预准备阶段
      • 如果所有参与者都响应可以提交,协调者进入预提交阶段,参与者执行事务操作,但不提交。
    • 提交阶段
      • 如果所有参与者都预提交成功,协调者发送提交请求,参与者提交事务。如果有参与者预提交失败,协调者发送回滚请求,参与者回滚事务。
  • 本地事务和事件驱动(基于消息的最终一致性)
    • 本地事务
      • 每个服务只管理自己的本地事务,确保操作要么完全成功,要么完全失败。
    • 事件驱动
      • 服务之间通过事件总线(如 Kafka、RabbitMQ)发送和接收事件。一个服务完成本地事务后,发送事件通知其他服务。
      • 接收服务处理事件并执行相应的本地事务操作。
  • TCC(Try-Confirm/Cancel)模式
    • Try
      • 尝试执行业务操作,预留资源,但不提交。
    • Confirm
      • 确认操作,正式提交事务,只有在 Try 阶段成功时执行。
    • Cancel
      • 取消操作,释放预留资源,只有在 Try 阶段失败或超时时执行

2PC 的缺点是协调者是一个单点故障,并且网络分区或参与者的崩溃会导致事务挂起。

三阶段提交协议(3PC)是对 2PC 的改进,通过引入预提交阶段降低了协调者单点故障带来的影响

3PC 进一步降低了事务挂起的风险,但实现较为复杂。

Seata

Seata 的三个角色

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

其中,TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端

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操作来控制事务,灵活性高,适合对一致性要求高的场景,但实现成本较高。

半消息 RocketMQ 二阶段提交

通过消息队列(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


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

  • 幂等:多次调用方法或者接口不会改变业务状态,可以保证重复调用的结果和单次调用的结果一致
  • 如果是新增数据,可以使用数探库的难一素引
  • 如果是新增或修改数据
    • 分布式锁,性能较低
    • 使用token + redis来实现,性能较好
      • 第一次请求,生成一个唯一的 token 存入 redis ,返回给前端
      • 第二次请求,业务处理,携带之前的 token,到 redis 进行验证,如果存在,可以执行业务,删除 token;如果不存在,则直接返回,不处理业务

讲一下分布式锁 🚩

Redisson 的可重入锁结构

Redisson 的可重入结构是基于 Hash 结构的,具体的结构为

  • key:锁的名词
  • value:持有锁的人,线程ID : 重入次数

讲一下 Redisson 的 看门狗机制

  • 1、在分布式系统下,对于共享资源的访问可以使用分布式锁,这里一般会考虑引入组件 redis ,然后使用 redis 中的 setnx 命令,这个命令的作用是 如果不存在则进行添加,如果存在则添加失败 setnx name value

  • 2、一般这种情况下就可以实现一个简易的分布式锁,但是呢,有可能出现 redis 宕机的情况,这种情况下,为了防止这个 键值 一直存在 redis 中(锁),一般会进行添加一个有效时间,从而避免锁一直未释放的操作 setnx name value ex100;同时,这里如果是在过期时间内进行释放锁,会进行 删除操作,这里一般建议使用 Lua 命令进行操作(防止误删到其他的锁)

  • 3、上面还可能出现一种情况,就是在过期时间后自动释放了锁,业务流程却未执行完,为了避免这种情况,建议引入 redission

  • 当使用 Redisson 的锁机制(如 RLock)锁定某个资源时,如果没有显式地设置锁的租期(lease time),Redisson 会启用“看门狗”机制。这个机制的核心是自动续租,确保在持有锁的服务实例正常运行时,锁不会因为超时而被自动释放

    • 默认租期设置:默认情况下,锁的租期设置为 30 秒。这意味着,如果在这段时间内没有操作完成,锁就会自动释放,防止死锁。
    • 自动续租:“看门狗”机制会在锁快到期时(默认情况下是租期的 1/3,即10秒后),自动发送一个命令给 Redis,将锁的租期重新延长到 30 秒。这个过程会一直持续,直到锁被显式释放。
    • 故障转移:如果持有锁的服务实例因为某些原因宕机或无法与 Redis 通信,锁将不会被续租,最终会在默认的租期结束后自动释放。这样,其他服务实例可以获取锁,继续处理任务,保证了系统的高可用性和一致性。
  • waitTime:定义了尝试获取锁的最大等待时间。在等待时间内,如果锁释放了,当前线程可以成功获取锁;否则,获取锁失败。

  • leaseTime:定义了锁的持有时间。当持有时间到期后,锁会自动释放,防止死锁。如果设置为 -1,看门狗机制会启动,定期延长锁的有效期,直到显式释放锁。

image.png

限流与监控

你们项目中有没有做过限流,怎么做的

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中检索数据,并根据需求配置面板的显示方式。

日志

你们项目中日志管理怎么做的

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

to be contined....

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