Bean 是否线程安全

Bean 默认单例, 可以通过 @Scope("prototype") 设置为多例

如果 Spring bean 没有可变状态 (无状态), 这种情况是线程安全的, 否则不是.

@RestController
public class TestController{
    private int cnt; // 定义了可变成员, TestController线程不安全

    @Resource
    private TestService testService; // TestService中如果没有定义可变变量则是线程安全的
}

事务失效场景

  • 异常捕获处理: 自行捕获异常并处理, 没有 throw, 事务只有接收到抛出的异常通知后才会进行处理
  • 抛出检查异常: 默认不会回滚检查异常(比如: FileNotFoundException), 可以配置注解中rollbackFor属性, @Transactional(rollbackFor=Exception.class)
  • 非 public 方法导致注解失效
  • 同一方法内调用: 同一方法内调用不会走代理, 事务注解都是要走代理才能起效. 从类外调用方法, 实质是调用的是代理包装好的对象, 事务生效, 从一个类内部调用则是this.xxx(), 直接调用, 事务不生效.

Bean 生命周期

  1. 通过BeanDefinition获取 bean 的定义信息
  2. 调用构造函数实例化 bean
  3. 依赖注入
  4. 处理 Aware 接口
  5. 前置的后置处理器BeanPostProcessor
  6. 初始化
  7. 后置的后置处理器BeanPostProcessor
  8. 销毁

循环依赖

三级缓存

初始完的 bean -> 生命周期未走完的 bean -> 对象工厂

两级缓存只能解决普通对象的循环依赖, 三级缓存才能解决代理对象的循环依赖, 三级缓存解决方法如下:

假设 A 和 B 对象发生了循环依赖, 其中 A 为代理对象

  1. 实例化 A, 生成一个 A 的对象工厂放入到三级缓存中
  2. 需要注入 B, 此时 B 不存在, 实例化 B, 对象 B 生成一个对象工厂放入三级缓存中
  3. B 中需要注入 A, 从三级缓存中获取 A 对象工厂, 创建对象并放入到二级缓存中, 并且将创建的 A 对象注入到 B 中
  4. B 对象创建成功, 放入到一级缓存单例池中
  5. 将 B 注入到 A 中, A 创建成功, 从二级缓存中删除并放入到一级缓存中

如果只有二级缓存则只能创建普通的对象放入到二级缓存中, 无法创建增强的代理对象放入其中.

三级缓存只能解决初始化赋值阶段的循环依赖, 无法解决构造函数阶段的循环依赖, 可以增加注解 @Lazy 解决. @Lazy通过生成“假”代理对象的方式, 阻止参与循环依赖的 bean 解决依赖. 直接阻止了项目启动时有可能发生的循环依赖错误, 而在随后真正使用注入的 bean 时再获取 Spring 容器中完整的代理 bean.

public A(@Lazy B b){
    this.b = b;
}

SpringMVC 执行流程

视图阶段 (JSP)

前后端分离阶段 (接口开发, 异步请求)

只返回 JSON 数据, 简化了流程

Spring Boot 自动配置

  1. 在 Spring Boot 项目中的引导类上有一个注解 @SpringBootApplication,这个注解是对三个注解进行了封装,分别是:

    • @SpringBootConfiguration
    • @EnableAutoConfiguration
    • @ComponentScan
  2. 其中@EnableAutoConfiguration是实现自动化配置的核心注解。该注解通过@Import注解导入对应的配置选择器。关键的是内部就是读取了该项目和该项目引用的 Jar 包的的 classpath 路径下 META-INF/spring.factories 文件中的所配置的类的全类名。

  3. 在这些配置类中所定义的 Bean 会根据条件注解所指定的条件来决定是否需要将其导入到 Spring 容器中。

  4. 条件判断会有像@ConditionalOnClass这样的注解,判断是否有对应的 class 文件,如果有则加载该类,把这个配置类的所有的 Bean 放入 spring 容器中使用。

Spring* 常见注解

Spring 注解

注解 说明
@Component @Controller @Service @Repository 实例化 Bean
@Autowired 根据类型依赖注入
@Qualifier 集合@Autowired用于根据名称依赖注入
@Scope 表注 Bean 作用范围
@Configuration 指定类未配置类,创建容器时会从该类上加载注解
@ComponentScan 指定初始化容器时扫描的包
@Bean 使用在方法上,标注将该方法返回值存储到容器中
@Import 使用@Import导入的类会被加载到 IOC 容器中
@Aspect @Before @After @Around @Pointcut AOP

SpringMVC 注解

注解 说明
@RequestMapping 映射请求路径,在类上为类中所有方法的父路径,在方法上则为方法的路径
@RequestBody 接收 http 请求 json 数据,body 传参
@RequestParam 指定请求参数名称,query 传参
@PathVariable 从请求路径获取参数
@ResponseBody 将返回对象转换为 json 数据返回
@RequestHeader 获取指定请求头的数据
@RestController @Controller+@ResponseBody

SpringBoot 注解

注解 说明
@SpringBootConfiguration 组合了@Configuration注解,实现配置文件功能
@EnableAutoConfiguration 打开自动配置功能
@ComponentScan Spring 组件扫描

Spring Cloud 组件

  • 注册中心: Eureka, Nacos
  • 负载均衡: Ribbon
  • 远程调用: Feign, Dubbo
  • 服务熔断: Hystrix, sentinel
  • 网关: Gateway

Spring Cloud 如何实现服务注册和发现

eureka:

  • 服务注册: 服务提供者将自己的信息注册到 eureka, eureka 保存服务信息, 比如服务名称, ip, 端口等
  • 服务发现: 消费者从 eureka 拉取服务列表, 若服务提供者有集群则消费者会根据负载均衡算法选择其中一个进行调用
  • 服务监控: 提供者每 30 秒向 eureka 发送心跳报告健康状态, eureka 90 秒没接受到心跳则会剔除该服务

nacos 与 eureka 不同点:

  • nacos 实例有临时实例(默认ephemeral=true)和非临时实例. 临时实例采用心跳模式, 心跳不正常会被剔除; 非临时实例采用服务端主动检测的模式, 不正常也不会被剔除
  • nacos 支持服务列表变更的消息推送模式
  • nacos 集群默认为 AP, 当集群中存在非临时实例时则为 CP; eureka 采用 AP 方式
  • nacos 还可以当配置中心, eureka 不行

Ribbon 负载均衡

负载均衡策略:

  • RoundRobinRule: 轮询
  • WeightedResponseTimeRule: 按照权重选择,响应时间越长,权重越小
  • RandomRule: 随机
  • BestAvailableRule: 忽略短路服务器,选择并发数较低的服务器
  • RetryRule: 重试机制,先轮询,服务器宕机则重试
  • AvailabilityFilteringRule: 可用性敏感策略,先过滤非健康的,再选择连接数较小的实例
  • ZoneAvoidanceRule: 以区域可用的服务器为基础进行服务器选择,使用 Zone(地区、机房等)对服务器进行分类,然后对 Zone 内多个服务器进行轮询

自定义负载均衡:

  • 创建类实现IRule接口,指定负载均衡策略(全局)
  • 配置文件中可以配置调用某一个服务时使用的负载均衡策略(局部)

服务雪崩

服务雪崩:一个服务失败导致整条链路的服务都失败

服务降级:服务自我保护或者保护下游服务的一种方式,确保服务不会受请求突增影响变得不可用。Feign 中的fallback定义降级逻辑。

服务熔断:降级逻辑达到阈值(10 秒内失败率超过 50%)后打开熔断状态,然后对于请求快速失败,触发熔断机制后每 5 秒请求一下,判断服务是否可用。Hystrix 熔断机制一共有关闭、打开和半开三个状态,状态转移图如下:

flowchart LR A[Closed] B[Open] C[Half-Open] D{尝试放行一次请求} A --> |success| A A --> |达到失败阈值| B B --> |快速失败| B B --> |熔断时间结束| C C --> D D --> |fail 打开断路器| B D --> |success 关闭断路器| A

微服务监控

服务监控、链路追踪、服务告警:skywalking:

  • 服务(service):业务资源应用系统(每个微服务是一个服务)
  • 端点(endpoint):应用系统对外暴露的功能接口(接口)
  • 实例(instance):物理机

限流

Nginx

控制速率(突发流量):漏桶算法,漏桶以固定速率漏出请求,多余请求等待或抛弃

控制并发连接数

网关

令牌桶:固定速率生成令牌放入桶中,桶满后暂停生成,请求需要申请到令牌才会处理,否则被阻塞或者丢弃

漏桶可以保证平滑,令牌桶会有波动

Seata 分布式事务

seata 架构:

XA 模式

RM 一阶段的工作:

  1. 注册分支事务到 TC
  2. 执行分支业务 sql 但不提交
  3. 报告执行状态到 TC

TC 二阶段的工作:

  1. TC 检测各分支事务执行状态。若都成功,通知所有 RM 提交事务;否则通知所有 RM 回滚事务

RM 二阶段的工作:

  1. 接收 TC 指令,提交或回滚事务

互相等待,资源锁定周期过长

AT 模式

阶段一 RM 的工作:

  1. 注册分支事务
  2. 记录 undo log
  3. 执行业务 sql 并提交
  4. 报告事务状态

阶段二提交时 RM 的工作:

  1. 删除 undo log

阶段二回滚时 RM 的工作:

  1. 根据 undo log 恢复数据到更新前

TCC 模式

  1. Try:资源的检测和预留
  2. Confirm:完成资源操作业务;要求 Try 成功 Confirm 一定要能成功
  3. Cancel:预留资源释放,可以理解为 Try 的反向操作

TCC 模式的三步都需要代码实现,其他两种模式都是框架完成,代码侵入度更低

MQ 分布式事务

在一个事务中通过 mq 发送消息调用另一个服务,消费者监听 mq 消息,执行本地事务

异步调用,解耦合,性能好

接口幂等性

  1. token + redis
  1. 分布式锁

    具体见分布式锁

xxl-job 任务调度

路由策略

任务执行失败

  • 故障转移,使用健康实例执行任务
  • 失败重试,设置重试次数
  • 查看日志分析,邮件告警

大数据量任务同时执行

路由策略选择分片广播,一次任务调度将会广播触发对应集群中所有执行器执行一次任务,任务执行代码中获取分片参数执行对应任务