Spring面试笔记
2025-10-20 12:39:14 0 举报
AI智能生成
Spring面试笔记
作者其他创作
大纲/内容
Spring
Spring是一个开源的、轻量级的J2EE框架
- 特性
非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API
控制反转:IoC(Inversion of Control),指的是将对象的创建权交给 Spring
依赖注入:DI(Dependency Injection),是指依赖的对象不需要手动调用 setXX 方法去设置,而是通过配置赋值
面向切面编程:AOP(Aspect Oriented Programming)
容器:Spring 是一个容器,因为它包含并且管理应用对象的生命周期
组件化:Spring 实现了使用简单的组件配置组合成一个复杂的应用
一站式:在 IoC 和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库
IoC
IoC控制反转
可以理解成一种思想,用于减少代码间的耦合,核心思想是将对象的装载与生命周期交给IoC容器管理。
可以理解成一种思想,用于减少代码间的耦合,核心思想是将对象的装载与生命周期交给IoC容器管理。
IoC容器
- IoC容器是实现控制反转的核心,主要负责:
读取配置信息:通过XML文件、Java注解或Java配置类读取配置信息,了解哪些对象需要被创建以及如何创建和组装这些对象。
实例化Beans:根据提供的配置信息,IoC容器负责实例化定义的Beans。
装配依赖关系:依赖注入(DI)是实现对象间依赖关系装配的关键机制。
管理Bean的生命周期:IoC容器管理Bean的整个生命周期,从创建到销毁。 - BeanFactory接口
IoC容器的核心接口,主要负责管理Bean的生命周期和依赖关系。
与FactoryBean接口的区别:FactoryBean是一种特殊的Bean,用于定制或增强Bean,从BeanFactory获取的是已在IoC容器注册的Bean,从FactoryBean获取的是FactoryBean#getObject()方法返回的对象,而不是FactoryBean本身的实例。主要方法:
isSingleton:是否单例对象
getObjectType:获取返回对象的类型
getObject:自定义创建对象的过程(new、反射、动态代理)) - ApplicationContext接口
继承自BeanFactory接口,是IoC容器的高级形式,提供了比BeanFactory更丰富的功能,通常情况下推荐使用ApplicationContext而不是BeanFactory。不同场景的实现:
ClassPathXmlApplicationContext:从类路径下的XML配置文件中加载上下文定义。
FileSystemXmlApplicationContext:从文件系统路径下的XML配置文件中加载上下文定义。
AnnotationConfigApplicationContext:从一个或多个基于Java的配置类中加载Spring应用上下文。
WebApplicationContext:为Web应用提供上下文,与Spring的Web MVC框架集成。
依赖注入(Dependency Injection, DI)
- 可以理解成一种手段,指在程序运行期间,由容器动态地将依赖对象注入到相关的属性中。
- 依赖注入方式
构造器注入:通过类的构造器注入依赖,确保了对象在使用前已经被正确初始化,缺点是构造器可能会变得复杂,参数列表变长。
设置器注入:通过对象属性的setter方法手动传入依赖对象注入,缺点是如果忘记调用设置器,使用时可能因未初始化而引发异常。
字段注入:通过在字段属性上添加注解(@Autowired、@Resource),底层通过反射注入,代码更简洁,缺点是破坏类的封装性。
方法注入:通过在方法上添加注解(@Autowired、@Resource)注入。 - 注解
@Autowired:由Spring提供,默认按类型匹配,找不到则报错,可以结合@Qualifier按名称匹配,可用在构造器、字段、setter方法。
@Resource:由JDK提供,默认按名称匹配,找不到则按类型匹配,再找不到则报错,可用在字段、setter方法。
@Inject:由JDK提供,默认按类型匹配,找不到则报错,组合@Qualifier、@Named根据名称查找。
@Value:用于注入基本类型和String类型的值。
Bean
- Bean是由Spring IoC容器实例化、组装并管理的对象。
- 作用域
1、Singleton : 唯一bean实例,Spring中的bean默认都是单例的。
2、Prototype : 每次请求都会创建一个新的原型 bean 实例。
3、Request : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。
4、Session : 每一次 HTTP 会话都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效
5、Application:与ServletContext的生命周期相同。每个ServletContext会创建一个Bean实例。
6、Websocket:与Websocket会话的生命周期相同。每个Websocket会话都会创建一个新的Bean实例。 - 生命周期
Bean的生命周期是指从Bean定义被加载到IoC容器中开始,直到Bean实例被销毁的整个过程。
1、筛选出非懒加载的单例BeanDefinition进行创建:Spring容器启动时会查找并注册所有的BeanDefinition。对于非懒加载(即lazy-init="false")的单例(singleton)Bean,Spring会在启动的过程中创建它们,以确保在首次使用前就完成初始化。
2、合并BeanDefinition:如果有父子Bean定义,则子Bean定义会合并父Bean定义的配置。这一步确保了Bean的配置完整性。
3、推断构造方法:如果Bean的构造方法不是显式指定的,Spring会尝试推断构造方法。对于基于注解的配置,Spring会根据@Autowired注解等来推断用哪个构造方法来实例化Bean。
4、实例化:通过构造方法实例化Bean对象。这一步是创建Bean实例的过程。
5、属性填充:Spring根据Bean定义信息填充属性,这可能包括对其他Bean的引用(依赖注入)。
6、执行*Aware回调:执行一系列*Aware接口的回调,注入上下文信息。
7、初始化前:指BeanPostProcessor的postProcessBeforeInitialization方法的调用。这是自定义初始化逻辑执行之前的扩展点。
8、初始化:如果Bean实现了InitializingBean接口,Spring会调用其afterPropertiesSet方法。另外,如果Bean定义中指定了自定义的初始化方法,该方法也会被调用。
9、初始化后:指BeanPostProcessor的postProcessAfterInitialization方法的调用。这是自定义初始化逻辑执行之后的扩展点。AOP代理正是在这一步通过BeanPostProcessor实现的,因此AOP代理的创建发生在Bean初始化完成之后。
10、使用:此时,Bean已经准备好被应用程序使用了。
11、销毁:如果Bean实现了DisposableBean接口,Spring会调用其destroy方法。另外,如果Bean定义中指定了自定义的销毁方法,该方法也会被调用。这一步是在Spring容器关闭时进行,用于资源的清理和释放。 - 声明方式
XMl配置文件:<Bean>标签
注解:@Component、@Controller、@Service、@Repository、@Bean
@Import
1、导入Bean
2、导入@Configurationt配置类
3、导入ImportSelector接口
4、导入ImportBeanDefinitionRegistrar接口
5、导入BeanDefinitionRegistryPostProcessor接口
扩展:FactoryBean、SPI机制 - 线程安全问题
1、Spring没有提供Bean的线程安全策略,需要开发人员自行处理线程安全问题
2、Bean的线程安全问题取决于是有状态还是无状态,大部分Bean(Controller、Service等)都是无状态,不存在线程安全问题
3、保证线程安全:定义原型Bean、使用ThreadLocal - 初始化、销毁
1、xml方式:<bean>标签中指定
2、注解方式:@Bean中指定
3、注解指定:@PostConstruct(bean创建并完成属性赋值后执行)、@PreDestroy(容器销毁前执行)
4、接口方式:InitializingBean、DisposableBean
*Aware接口
- 在Spring框架中用于提供回调机制,执行时机在Bean的属性填充之后和初始化之前,允许Bean在创建过程中能够感知(aware)到Spring容器的某些状态或环境信息,并对这些信息进行相应的处理或使用。这类接口的命名和设计体现了一种设计模式,旨在减少Bean对容器特定功能的直接依赖,从而提高了代码的解耦性和灵活性。
- 主要作用
让Bean获得Spring容器的某些能力或上下文环境的信息。实现这些接口的Bean在创建过程中,会被Spring容器自动调用相应的方法,注入特定的对象或信息。这样,Bean就可以利用Spring框架提供的丰富功能,而不需要直接与框架耦合。 - 命名含义
*Aware接口的命名遵循了一定的模式:接口名称以功能或所能感知的上下文信息为前缀,以Aware为后缀。Aware这个词意味着“意识到的”、“知道的”,在这里表示Bean“意识到”或“知道”了某些信息或能力。这种命名直观地反映了接口的作用——让Bean能够感知到或获得特定的容器功能或信息。 - 常见的*Aware接口
ApplicationContextAware: 允许Bean获得ApplicationContext,从而让Bean能够访问应用上下文的各种资源和服务。
BeanFactoryAware: 让Bean获得对BeanFactory的访问,这是Spring的核心容器,负责管理Bean的创建和配置。
BeanNameAware: 允许Bean获得自己在容器中的名称。
ResourceLoaderAware: 让Bean获得ResourceLoader,可以用来加载外部资源。
EnvironmentAware: 允许Bean获得Environment,用于访问Spring应用的环境相关信息,如配置属性。
MessageSourceAware: 允许Bean获得MessageSource,用于获取国际化消息文本。
循环依赖
- 多个单例Bean之间存在相互依赖关系,形成闭环,A依赖B,B依赖A,在Spring中单例Bean的创建过程都涉及三级缓存的使用,以处理可能出现的循环依赖问题。
- 三个Map
一级缓存:singletonObjects(单例对象),存储已经完全初始化的Bean,可以直接用于依赖注入,采用ConcurrentHashMap确保线程安全。
二级缓存:earlySingletonObjects(早期单例对象),存储已经实例化但未填充属性的Bean,即Bean的早期引用(由三级缓存生成并保存到二级缓存)。
三级缓存:singletonFactories(单例工程),存储创建单例Bean的工厂(ObjectFactory),用于生成Bean的早期引用,同时允许对Bean进行代理等操作,以支持AOP等高级特性。 - 四大方法
getSingleton:获取单例池中的Bean
doCreateBean:创建Bean
populateBean:属性填充
addSingleton:将Bean放入单例池 - 步骤
创建A
1、先调用getSingleton()从缓存中获取Bean,没有则doCreateBean()创建,在doCreateBean()创建A的实例之前,Spring会将一个ObjectFactory(实际上是一个lambda表达式,这个表达式能够生成A的早期引用)放入三级缓存中。这个早期引用主要用于处理循环依赖,它可能是原始Bean的引用,或者是经过AOP增强后的代理对象。
2、对象创建后调用populateBean()开始填充属性,发现A中依赖B
3、调用getSingleton()从缓存中获取B,缓存中没有B,暂停A的创建过程,进入创建B的流程
创建B
1、doCreateBean()创建B的实例并将B的早期引用ObjectFactory放入三级缓存
2、调用populateBean()开始填充属性,发现B中依赖A
3、从一级缓存查找A、没有则查找二级缓存、没有则查找三级缓存
4、在三级缓存中获取到A(这里实际就是执行lambda表达式获取早期暴露的最终版本Bean,原始Bean或代理Bean),将A移至二级缓存并在三级缓存中删除
5、将缓存中获取到的A赋予当前B对象中的a属性
6、B对象完成后续初始化过程后调用addSingleton()进入一级缓存,并删除二三级缓存中的B,此时B对象完成
继续创建A
4、A对象重新获取B,此时一级缓存中存在B对象,直接获取
5、A对象完成后续初始化过程后调用addSingleton()进入一级缓存,并删除二三级缓存中的A,此时A对象完成 - 其他
1、构造方法存在循环依赖问题,可以通过@Lazy解决。
2、只有单例Bean会通过三级缓存解决循环依赖问题。
3、Spring解决循环依赖问题依靠的是“中间态”Bean(已经实例化但还没有初始化的Bean,半成品)。
4、为什么一级缓存使用ConcurrentHashMap?
避免多线程环境下Bean被重复创建或状态不一致的问题。
5、只有一级缓存能否解决循环依赖?
理论上可以“暴力”实现,比如一级缓存的value存储对象,对象中标识是成品还是半成品,但效率很低。
6、只有二级缓存能否解决循环依赖?
一般情况下能解决(未使用AOP时)。
7、为什么需要第三级缓存?
第三级缓存主要解决最终版本的Bean对象问题,为了解决使用了AOP的场景。
AOP
- AOP是IoC整体流程中的一个扩展点。
- 实现方式
JDK动态代理:基于接口创建代理类,执行效率较低,使用反射。
CgLib动态代理:基于父子类实现,代理类中包含目标类(被代理类)引用,执行效率较高,未使用反射。 - 通知
@Beform:前置通知,目标方法之前执行。
@After:后置通知,目标方法之后执行(始终执行)。
@AfterReturning:返回通知,目标方法结束前执行(异常不执行)。
@AfterThrowing:异常通知,出现异常时执行。
@Around:环绕通知,环绕目标方法执行。
事务
- 7种传播行为:指多个事务方法相互调用时,事务如何在这些方法间传播
1、PROPAGATION_REQUIRED(默认):如果当前没有事务就新建一个事务,如果已经存在一个事务中,加入到这个事务中。
2、PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
3、PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
4、PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
5、PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6、PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
7、PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行,没有事务则执行与PROPAGATION_REQUIRED类似的操作。 - 5种隔离级别:指一个事务对数据的修改与另一个并行的事务的隔离程度
1、DEFAULT:使用数据库本身使用的隔离级别,ORACLE(读已提交) MySQL(可重复读)。
2、READ_UNCOMITTED:读未提交(脏读)最低的隔离级别,一切皆有可能。
3、READ_COMMITED:读已提交,ORACLE默认隔离级别,有幻读以及不可重复读风险。
4、REPEATABLE_READ:可重复读,解决不可重复读的隔离级别,但还是有幻读风险。
5、SERLALIZABLE:串行化,最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,这样就解决了脏读、不可重复读和幻读的问题了。 - 失效场景
1、未启用spring事务管理功能:@EnableTransactionManagement 注解用来启用spring事务自动管理事务的功能。
2、非public方法:@Transaction 可以用在类上、接口上、public方法上,如果将@Trasaction用在了非public方法上,事务将无效。
3、自身调用问题:spring是通过AOP的方式对需要spring管理事务的bean生成了代理对象,然后通过代理对象拦截了目标方法的执行,在方法前后添加了事务的功能,所以必须通过代理对象调用目标方法事务才会起效。
4、异常类型错误:RuntimeException和Error的情况,spring事务才会回滚。
5、异常被吞了:当业务方法抛出异常,spring感知到异常的时候才会做事务回滚的操作,若方法内部将异常给吞了,那么事务无法感知到异常了,事务就不会回滚了。
设计模式
- 单例模式(Singleton):这是Spring中最常用的设计模式之一。默认情况下,Spring容器中的bean都是单例的,即每个bean定义对应一个实例。
- 工厂模式(Factory Method):Spring使用工厂模式来创建bean实例。BeanFactory是一个最典型的工厂类,它定义了容器的最基本形式和对象的获取方法。
- 原型模式(Prototype):除了单例模式,Spring还支持原型模式,即每次请求都创建一个新的bean实例。
- 代理模式(Proxy):Spring AOP功能的实现就是基于代理模式的。它使用JDK动态代理或CGLIB代理来为目标对象创建代理,以实现方法拦截和增强功能。
- 模板方法模式(Template Method):Spring中有很多以Template结尾的类(如JdbcTemplate、RestTemplate等),这些类都是模板方法模式的应用。它们定义了一个操作的骨架,将某些步骤延迟到子类中实现。
- 观察者模式(Observer):Spring事件处理机制就是观察者模式的应用。当一个事件被发布时,所有注册为监听该事件的监听器都会被通知。
- 策略模式(Strategy):Spring中的资源访问策略、实例化策略等,都是策略模式的体现。它允许在运行时选择最合适的策略。
- 装饰器模式(Decorator):Spring中的输入输出流的处理就使用了装饰器模式,它通过包装一个类来扩展其功能,而无需修改原有类的代码。
- 适配器模式(Adapter):Spring中的AOP和消息服务(如JMS)等功能,使用适配器模式来实现不同技术之间的适配。
- 责任链模式(Chain of Responsibility):Spring Security中的过滤器链就是责任链模式的应用,每个过滤器执行特定的安全检查任务,然后将请求传递给下一个过滤器处理。
- 建造者模式(Builder):在Spring框架中,用于构建复杂对象时(如使用BeanDefinitionBuilder构建BeanDefinition对象),经常采用建造者模式。
- 享元模式(Flyweight):用于优化重复对象的存储,Spring中的Scope就是一个例子,尤其是在使用单例和原型bean时。
Spring MVC
Spring MVC是一个基于Servlet的Web框架,专注于构建Web应用程序。它在Spring的基础上提供了模型-视图-控制器(MVC)的实现。
- 特性:
强大的配置功能,支持RESTful Web服务。
灵活的URL到视图的映射。
丰富的数据绑定和验证机制。
工作流程
- 用户发送请求至前端控制器DispatcherServlet
- DispatcherServlet将请求转发至HandlerMapping
- 经过一系列处理器、拦截器、适配器
- 调用具体Controller进行业务处理
- Controller返回ModelAndView对象到DispatcherServlet
- DispatcherServlet将ModelAndView传递给ViewReslover解析视图
- DispatcherServlet拿到视图结果后响应给用户
Spring Boot
Spring Boot是基于SpringFramework封装的上层应用,是简化Spring应用开发的脚手架,规避了繁琐的配置操作,以便简化开发,让更多时间精力放到业务开发上。
- 特性:
自动配置:Spring Boot可以根据项目中添加的依赖自动配置Spring应用。
起步依赖:通过提供一系列的起步依赖,Spring Boot简化了构建配置和依赖管理。
命令行界面:Spring Boot CLI(命令行界面)允许开发者通过Groovy快速开发Spring应用。
Actuator:提供生产级别的功能,如监控和管理应用。
嵌入式服务器:支持嵌入式Tomcat、Jetty和Undertow服务器,无需部署WAR文件。
核心注解
- @SpringBootApplication:将一个类标记为SpringBoot应用入口,是@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan的组合注解。
- @SpringBootConfiguration:将一个类标记为SpringBoot应用的主配置类,本质是Spring的@Configuration。
- @EnableAutoConfiguration:启用Spring应用的自动配置功能,告诉Spring Boot基于添加的jar依赖自动配置应用程序的组件。
- @ComponentScan:指定Spring在初始化时应该扫描哪些包来发现和注册bean。
- @ConfigurationProperties:将配置文件中的属性绑定到JavaBean上。
- @RestController:是@Controller和@ResponseBody的结合体,用于创建RESTful服务。
启动流程
- 启动入口
Spring Boot应用的启动从一个带有@SpringBootApplication注解的主类开始。 - 创建SpringApplication实例
SpringApplication.run(MyApp.class, args)通过传入的主类创建一个SpringApplication实例,负责管理Spring应用的启动流程。 - 准备运行环境
SpringApplication会准备运行环境,识别和配置Spring Profiles,加载外部配置文件(如application.properties或application.yml)。 - 创建ApplicationContext
根据应用的类型(是否是Web应用),SpringApplication选择创建一个合适的ApplicationContext实例(例如,对于Web应用,通常是AnnotationConfigServletWebServerApplicationContext)。 - 初始化自动配置
@EnableAutoConfiguration注解是触发自动配置的关键,本质上利用了@Import(AutoConfigurationImportSelector.class)来导入配置。
AutoConfigurationImportSelector是自动配置的核心,它会读取类路径下所有可用jar包下META-INF/spring.factories文件中的配置。 - 加载自动配置类
自动配置类被加载并解析,基于@Conditional注解族(如@ConditionalOnClass、@ConditionalOnBean等)来决定是否激活。。 - Bean定义注册
符合条件的自动配置类会将其内部定义的bean注册到Spring容器中。 - 应用启动
完成所有自动配置后,ApplicationContext被刷新,这意味着所有的bean都被实例化并初始化。此时,Spring Boot应用完成启动,开始接受处理请求。
自动配置的优点
- 减少配置:自动配置减少了许多常见组件的显式配置需求,使得开发者可以更快地启动和运行Spring应用。
- 智能默认:Spring Boot提供了许多智能默认配置,这些配置通常符合大多数应用的需求,但也可以根据需要轻松覆盖。
- 灵活性:尽管自动配置提供了很多默认行为,但Spring Boot并不会锁定它们。可以通过提供自己的配置来覆盖默认配置,或者通过排除特定的自动配置类来绕过某些自动配置。
Starter
1、自定义starter命名:xxx-spring-boot-starter
2、META-INF/spring.factories(org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.starter.autoconfiguration.HelloAutoConfiguration)
2、META-INF/spring.factories(org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.starter.autoconfiguration.HelloAutoConfiguration)
CAP理论
CAP理论,也被称为布鲁尔定理(Brewer's theorem),由加州大学伯克利分校的Eric Brewer教授在2000年提出,CAP是三个单词的首字母缩写,分别是:
CAP理论,也被称为布鲁尔定理(Brewer's theorem),由加州大学伯克利分校的Eric Brewer教授在2000年提出,CAP是三个单词的首字母缩写,分别是:
- 一致性(Consistency):所有节点在同一时间具有相同的数据。
- 可用性(Availability):保证每个请求都能得到响应(无论成功或失败),不会出现超时或错误。
- 分区容错性(Partition tolerance):系统中任意信息的丢失或失败不会影响系统的继续运作。
BASE理论
BASE理论是对CAP中一致性和可用性权衡的一种实现策略,主要用来指导大规模分布式系统的设计,以下三个概念的缩写:
BASE理论是对CAP中一致性和可用性权衡的一种实现策略,主要用来指导大规模分布式系统的设计,以下三个概念的缩写:
- 基本可用(Basically Available):分布式系统在出现故障的时候,允许损失部分可用性,比如响应时间的延长、系统功能的部分下线等。
- 软状态(Soft State):允许系统中的数据存在中间状态,并认为这种状态不会影响系统的整体可用性。这意味着系统不需要实时达到数据一致性,但最终会一致。
- 最终一致性(Eventually Consistent):系统保证在没有新的更新操作的情况下,最终所有的副本都将达到一致的状态。数据的一致性不需要实时保证,但最终会达到一致。
Spring Cloud
Spring Cloud基于Spring Boot,专门用于简化分布式系统(如微服务架构)的开发与管理,简化分布式系统开发的复杂性。
- 特性:
提供了在分布式系统中常见模式的实现,如配置管理、服务发现、断路器、路由过滤、分布式事务等,与Spring Boot紧密集成,易于开发和部署。
Spring Cloud Netflix
Eureka
Eureka是Spring Cloud体系中的一个服务注册与发现组件,使得微服务架构中的服务可以相互发现并进行通信。Eureka是偏向于AP(可用性/分区容忍性)的设计。这与一些其他的服务发现组件(如Zookeeper,它更倾向于CP,即一致性和分区容忍性)形成了对比
EurekaServer:服务端,注册中心
EurekaClient:客户端
EurekaServer:服务端,注册中心
- 记录服务信息
- 心跳监控,(默认)90秒未收到心跳的服务信息会被删除
EurekaClient:客户端
- Provider:服务提供者
1、将自己注册到EurekaServer
2、每隔(默认)30秒向EurekaServer发送一次心跳 - Consumer:服务调用者
1、根据服务名称从EurekaServer拉取服务列表
2、基于服务列表做负载均衡,选取一个服务发起远程调用
自我保护机制
- 什么是自我保护机制?
自我保护机制是Eureka针对网络异常波动情况下对高可用的保障措施。 - 为什么会有自我保护机制?
当网络发生异常时,可能部分EurekaClient在90s内都没能成功发送心跳到EurekaServer,而EurekaServer默认90s未收到心跳会将对应EurekaClient从注册表中删除,但实际上这些EurekaClient并不是真正的掉线,而是因为网络波动短暂失联,此时本不应该删除这些EurekaClient,所以通过自我保护机制来解决这个问题。 - 什么时候触发?
15分钟内收到心跳数量低于期望心跳数量85%,就会触发这个自我保护机制
1、EurekaClient每隔30s向EurekaServer发送心跳,EurekaServer负责更新服务列表中对应EurekaClient的心跳最后更新时间
2、EurekaServer有一个定时任务每隔60s检查服务列表中超过90s未收到心跳的服务,先判断是否开启自我保护,未开启则剔除
3、EurekaServer有一个定时任务每隔15min更新自我保护阈值(服务总数*每分钟续约数(60s/客户端续约间隔)*自我保护阈值因子) - 人为关闭自我保护机制
eureka.server.enable-self-preservation=false(服务端是否开启自我保护机制 (默认true)) - 存在的原因
1、网络分区和短暂网络问题: 在分布式系统中,网络分区是指网络中的一部分节点由于网络故障而无法与系统中的其他节点通信。这种情况下,即使服务实例仍然健康运行,它们也可能因为无法与Eureka Server通信而被错误地从服务注册表中移除。自我保护模式能够防止这种由于短暂网络问题导致的服务实例错误注销。
2、防止雪崩效应: 在微服务架构中,如果大量服务实例因为短暂的网络问题而被错误地从Eureka Server注册表中移除,当网络恢复后,这些服务实例会同时尝试重新注册,这可能导致Eureka Server面临巨大的负载,进而影响其性能和稳定性,甚至可能导致更多的服务注册问题。自我保护模式通过保留这些服务实例的注册信息,防止了这种情况的发生。
3、提高系统的稳定性和可用性: 自我保护模式的设计初衷是在发生网络问题时牺牲一定的一致性,以换取系统的可用性和稳定性。通过保留服务实例信息,即使在网络问题发生时,客户端也能继续发现和调用那些实际上可能仍然可用的服务实例,从而减少系统整体的失败率。
4、减轻管理员的运维负担: 自我保护模式减少了因短暂网络问题而需要手动干预的情况,从而降低了系统管理员的运维负担。在自我保护模式下,即使发生了网络分区,服务实例也不会被立即从注册表中移除,给系统管理员更多的时间来诊断和解决网络问题,而无需担心服务注册表的瞬时不一致会立即影响到服务的可用性。
服务列表拉取时机
- 在Eureka的架构中,服务列表的同步是通过客户端拉取(client-fetch)机制实现的。
- 客户端启动时: 当Eureka Client启动并向Eureka Server注册自己之后,它会立即尝试从Eureka Server获取当前的服务列表。
- 定期刷新: Eureka Client会定期(默认每30秒)从Eureka Server拉取服务列表的更新。
- 手动触发: 应用开发人员或系统管理员可以通过Eureka Client提供的API或者操作界面手动触发服务列表的更新。这在调试或者特定场景下非常有用,比如在进行服务迁移或者扩容时,需要快速更新客户端的服务列表。
- 在检测到网络分区恢复后: 如果客户端因为网络分区(网络问题导致Eureka Client和Eureka Server之间的通信中断)而无法与Eureka Server通信,一旦网络恢复,客户端会重新与Eureka Server建立连接,并立即拉取最新的服务注册表。
多级缓存机制
EurekaServer为了避免同时读写内存数据造成并发问题,采用多级缓存机制保证数据安全与请求速度。
EurekaServer为了避免同时读写内存数据造成并发问题,采用多级缓存机制保证数据安全与请求速度。
- 启动时注册与全量拉取:Eureka Client在启动时会向Eureka Server注册自己,并进行一次全量拉取操作,以获取当前所有可用服务的列表。
- 定期增量拉取:为了最小化网络带宽的使用并减少Eureka Server的负载,Eureka Client之后会每30秒向Eureka Server请求增量更新。增量更新包含自上次更新以来所有服务实例的变更(包括新注册的服务、服务下线和服务续约等)。
- 回退机制 - 全量拉取:如果在处理增量更新时,Eureka Client发现数据存在不一致(例如,由于网络问题错过了一些更新),或者增量更新失败,它会采用回退机制,执行全量拉取以重新同步服务列表。这确保了即使在面对增量更新失败的情况下,Eureka Client也能保持与Eureka Server的数据一致性。
集群数据同步
- 新Eureka Server节点加入集群时数据同步过程
1、新节点加入集群后会向某个或某些节点请求全量服务注册表信息。 - 某Eureka Server节点收到Eureka Client更新消息时数据同步过程
1、更新本地服务注册表信息。
2、异步复制变更信息到集群中其他Eureka Server节点。 - 定时触发增量同步过程
1、每个Eureka Server节点会定期(默认每15秒)向其他节点请求增量更新并应用到本地注册表中。 - 定时触发全量同步过程
1、每个Eureka Server节点会定期(默认每30分钟)向其他节点请求增量更新并应用到本地注册表中。
OpenFeign
OpenFeign是一个声明式的Web服务客户端,专为简化微服务之间的HTTP调用而设计,可以通过简单地声明接口和注解来创建一个HTTP客户端,而无需手动编写底层的HTTP请求代码,让远程调用变得简单和优雅。
超时控制
通信框架
核心流程
在服务启动时,Spring会扫描加了@FeignClient注解的接口,创建远程接口的本地JDK Proxy代理实例注入到Spring IOC容器中。
- OpenFeign的超时配置主要涉及到连接超时和读取超时,Feign客户端的默认配置是使用Ribbon来管理HTTP请求,因此其超时设置通常也会涉及Ribbon的配置。
- 设置Feign客户端的默认连接超时和读取超时
feign.client.config.default.connectTimeout=10000(默认10秒)
feign.client.config.default.readTimeout=60000(默认60秒) - 设置Ribbon的默认连接超时和读取超时
ribbon.ConnectTimeout=1000(默认1秒)
ribbon.ReadTimeout=5000(默认5秒)
通信框架
- OpenFeign默认使用的是Java标准库中的轻量级HTTP通信客户端HttpURLConnection来执行网络请求,它适用于简单的HTTP调用,在性能和功能上可能不如一些专业的HTTP客户端库,比如Apache HttpClient或OkHttp。
- feign.httpclient.enabled=true #启用Apache HttpClient
- feign.okhttp.enabled=true #启用OkHttp
核心流程
在服务启动时,Spring会扫描加了@FeignClient注解的接口,创建远程接口的本地JDK Proxy代理实例注入到Spring IOC容器中。
- 启动时扫描:在Spring应用启动过程中,Spring框架会扫描所有的类和接口,寻找带有@FeignClient注解的接口。
- 代理创建:对于每一个带有@FeignClient注解的接口,Feign会使用JDK动态代理或者CGLIB来创建一个实现了该接口的代理对象,这个代理对象会封装远程服务调用的细节。
- 注入Spring IOC容器:创建的代理实例随后被注入到Spring的IoC容器中,其他的Bean中就可以通过依赖注入的方式使用这些代理实例。
- 远程调用处理:当调用代理实例的方法时,Feign会根据@FeignClient注解提供的信息(如服务名、URL、路径等)和方法上的Spring MVC注解(如@RequestMapping、@GetMapping等)来构建HTTP请求,并通过配置的HTTP客户端发送请求到远程服务。
- 结果返回:远程服务的响应会被代理实例接收,并转换为对应方法的返回值,返回给调用者。
- FeignInvocationHandler类
为了创建Feign的远程接口的代理实现类,Feign提供了自己的一个默认的调用处理器,叫做 FeignInvocationHandler 类,该类处于 feign-core 核心jar包中(Feign的JDK代理类-》继承FeignInvocationHandler类-》实现InvocationHandler接口) - HystrixInvocationHandler类
如果Feign与Hystrix结合使用,则会替换成 HystrixInvocationHandler 调用处理器类,类处于 feign-hystrix 的jar包中。
MethodHandler接口
MethodHandler的invoke(…)方法,主要职责是完成实际远程URL请求,然后返回解码后的远程URL的响应结果。
Client接口
MethodHandler的invoke(…)方法,主要职责是完成实际远程URL请求,然后返回解码后的远程URL的响应结果。
- DefaultMethodHandler类
DefaultMethodHandler是MethodHandler的默认实现类,但是这个只能执行目标方法,并不具备远程通信的功能。 - SynchronousMethodHandler类
Feign提供了默认的 SynchronousMethodHandler 实现类,提供了基本的远程URL的同步请求处理。
Client接口
- Client.Default类
默认的feign.Client 客户端实现类,内部使用HttpURLConnnection 完成URL请求处理。 - ApacheHttpClient类
内部使用 Apache httpclient 开源组件完成URL请求处理的feign.Client 客户端实现类。 - OkHttpClient类
内部使用 OkHttp3 开源组件完成URL请求处理的feign.Client 客户端实现类。 - LoadBalancerFeignClient类
内部使用 Ribbon 负载均衡技术完成URL请求处理的feign.Client 客户端实现类。
Ribbon
Ribbon是一个客户端负载均衡器,主要用于管理微服务架构中服务之间的通信。
负载均衡策略
负载均衡策略
- 轮询策略(RoundRobinRule):默认的负载均衡策略。它按顺序逐一调用服务列表中的每个服务实例。当达到列表末尾时,再次从列表的开头开始调用。
- 随机策略(RandomRule):如其名,此策略通过随机选择服务实例来进行调用,不按顺序,每次调用的选择都是随机的。
- 重试策略(RetryRule):这个策略在调用服务失败时会进行重试。它会在选定的服务实例上进行调用,如果调用失败,则在指定的重试时间内,使用子策略(如轮询)选择另一个服务实例进行调用。
- 响应时间加权策略(WeightedResponseTimeRule):此策略会根据服务实例的平均响应时间来分配权重,响应时间越短的实例将获得更高的权重,并且被更频繁地选中。
- 最少并发请求策略(BestAvailableRule):选择并发请求最少的服务实例,意在分摊负载到不同的实例上,减少单个实例的压力。
- 可用性过滤策略(AvailabilityFilteringRule):此策略会过滤掉那些因为多次访问故障而处于断路器跳闸状态的实例,以及并发连接数超过阈值的实例,然后对剩余的实例应用轮询策略。
- 区域感知轮询策略(ZoneAvoidanceRule):这个策略会考虑到服务实例的区域(zone)性能和服务实例的可用性,尝试均衡地分配请求到不同区域的服务实例,以避免向故障区域发送请求。
- 修改负载均衡策略
1、配置文件方式
user-service.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
user-service是目标服务的名称,NFLoadBalancerRuleClassName是负载均衡策略的属性,值是要使用的负载均衡策略类的全限定名。
2、编程方式
创建MySelfRule类(声明为@Configuration,@RibbonClient(name="服务名",configuration=MySelfRule.class)),定义返回IRule的方法并return RandomRule();(声明为@Bean)
LoadBalancer
LoadBalancer 是一个轻量级客户端负载均衡库。
负载均衡策略
负载均衡策略
- 轮询(Round Robin)
轮询策略是最简单的负载均衡策略之一,它按顺序逐一地将请求分配给服务实例列表中的每个实例。当到达列表末尾时,策略会重新开始从列表的第一个实例分配请求。这种策略确保了所有的服务实例都将接收到大致相同数量的请求。 - 随机(Random)
随机策略会随机选择一个服务实例来处理请求。这种策略简单且不需要记录状态,但不保证请求分配的绝对均匀。 - 加权轮询(Weighted Round Robin)
加权轮询策略是轮询策略的一个变种,它允许为每个服务实例分配一个权重,实例被选中处理请求的概率与其权重成正比。这使得可以根据实例的能力(例如,CPU、内存容量)来调整其负载。 - 加权随机(Weighted Random)
加权随机策略类似于加权轮询,但选择实例的方式是基于权重的随机选择,而不是轮询,这种策略结合了随机性和考虑实例能力的优点。 - 最少连接(Least Connections)
最少连接策略会选择当前活跃请求最少的实例来处理新的请求,这种策略试图通过考虑实例的当前负载来更平衡地分配请求,理论上能更好地利用资源。 - 响应时间(Response Time)
响应时间策略会记录每个实例的平均响应时间,并优先将请求分配给响应时间最短的实例,这种策略旨在优化延迟,但它需要跟踪和计算每个实例的响应时间。 - 自定义策略
除了上述内置策略之外,Spring Cloud LoadBalancer 也支持自定义负载均衡策略。开发者可以实现自己的策略,以满足特定的需求或优化。
Hystrix
Hystrix是一个提供断路器模式的库,用于处理分布式系统的延迟和容错,提高分布式系统的弹性。一般情况下,Hystrix更多地在客户端使用,因为客户端可以直接控制对远程服务的调用和处理失败情况。服务端使用Hystrix较少,但在某些复杂的服务间调用场景中,服务端也可能需要使用Hystrix来提供额外的保护。随着微服务架构的发展,除了Hystrix,还有其他的断路器实现,如Resilience4J、Spring Cloud Circuit Breaker等。
服务降级(Fallback)
服务降级是指当系统发生异常时,系统会使用一个备选方法(fallback)来处理请求,这个方法会在主逻辑失败时被调用,当主方法执行失败、超时、断路器打开或线程池/信号量拒绝时将会调用降级方法。
熔断(Circuit Breaker)
熔断机制类似于家用电路中的保险丝,当某个服务的错误率超过一定阈值时,Hystrix会暂时中断当前服务的调用,即“打开”断路器。这样做可以防止连续的服务调用导致服务雪崩。
限流(Resource Isolation)
限流是指限制系统内部分资源的使用量(如线程池、信号量),防止系统因为过载而崩溃,Hystrix主要通过线程池和信号量来实现资源隔离。
服务降级(Fallback)
服务降级是指当系统发生异常时,系统会使用一个备选方法(fallback)来处理请求,这个方法会在主逻辑失败时被调用,当主方法执行失败、超时、断路器打开或线程池/信号量拒绝时将会调用降级方法。
- 使用场景:服务降级通常用在下游服务不可用时,可以返回一个预设的默认值或者缓存的数据,确保用户能得到一个基本的响应,从而避免连锁故障。
- 对某个方法声明降级策略:当方法调用超过3s或报错,进入timeOutHandler方法处理
@HystrixCommand(fallback="timeOutHandler",commandProperties={
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000")
}) - 在类上声明全局降级方法,方法上声明@HystrixCommand,出错时进入globalFallbackMethod方法处理
@DefaultProperties(defaultFallback="globalFallbackMethod") - 声明统一降级处理类(FallbackService.class),实现需要降级的接口
@FeignClient(value="服务名",fallback=FallbackService.class) - 服务降级通常发生在以下几种情况:
1、远程服务执行超时、报错。
2、触发熔断(熔断器处于打开状态)。
3、触发限流(线程池或信号量已满,导致无法处理更多请求)。
熔断(Circuit Breaker)
熔断机制类似于家用电路中的保险丝,当某个服务的错误率超过一定阈值时,Hystrix会暂时中断当前服务的调用,即“打开”断路器。这样做可以防止连续的服务调用导致服务雪崩。
- 实现方式:Hystrix通过一个内部的断路器来实现这一功能。断路器在正常情况下是关闭的,当失败次数超过一定比例(默认是50%)后,断路器会打开。断路器打开后,所有请求都不会进行远程调用,直接调用降级逻辑。
- 恢复机制:一旦断路器打开,它会在一个配置的时间窗口后进入半开状态。在半开状态,如果接下来的一些请求执行成功,断路器会关闭,恢复服务调用;如果仍然失败,则继续保持打开状态。
限流(Resource Isolation)
限流是指限制系统内部分资源的使用量(如线程池、信号量),防止系统因为过载而崩溃,Hystrix主要通过线程池和信号量来实现资源隔离。
- 线程池隔离:每个HystrixCommand可以配置一个线程池,该线程池中的线程数量是有限,当线程池已满时,新的请求将会被拒绝,并且会触发降级逻辑。
- 信号量隔离:信号量隔离是在调用线程上执行Hystrix命令,通过限制并发请求的数量来实现限流,当请求达到信号量限制时,新的请求同样会被拒绝并触发降级逻辑。
zuul
Spring Cloud Zuul 是一个API 网关工具,提供动态路由、过滤、监控、弹性伸缩和安全功能,内部集成Hystrix和Ribbon来实现断路器模式和客户端负载均衡。
特性
特性
- 动态路由:动态路由允许请求被转发到不同的后端集群或服务实例,而无需预先硬编码URL。
- 请求过滤:请求过滤可以在请求被路由到应用程序之前或之后执行各种任务,如身份验证、请求日志记录、请求和响应的修改等。
- 弹性伸缩:通过与服务发现和负载均衡机制集成,可以根据后端服务的健康状况和负载动态调整请求路由。
- 安全:可以集成安全机制,如OAuth2、JWT等,来实现统一的安全策略。
- 监控和度量:提供对请求和路由行为的监控和度量,可以集成到监控系统中,如Prometheus、Grafana等。
- 压力测试:可以用来对后端服务进行压力测试,通过动态路由功能将流量导向测试环境。
GateWay
Spring Cloud的全新项目,基于Spring5.0+Spring Boot2.0和Project Reactor等技术开发的网关,旨在为微服务架构提供简单有效且统一的API路由管理方式。
特性
核心概念
- 基于异步非阻塞模型进行开发。
- 基于WebFlux框架实现,而WebFlux底层使用高性能的Reactor模型通信框架Netty。
- 基于Flter链方式提供网关基本功能:安全、监控/指标、限流。
特性
- 动态路由:匹配任何请求属性
- 断言(Predicate)、过滤器(Filter)
- 集成Hystrix断路器
- 集成Spring Cloud服务发现功能
- 请求限流、路径重写
核心概念
- 路由(Route):路由是构建网关的基础模块,由ID、目标URI、一系列断言与过滤器组成,断言为true则匹配该路由。
- 断言(Predicate):可以匹配HTTP请求中的所有内容(请求头或请求参数),如果请求与断言相匹配则进行路由。
- 过滤(Filter):指Spring框架中GatewayFilter实例,可以在请求被路由前后进行需改。
config
Config提供了服务端和客户端支持,用于在分布式系统中的外部化配置。使用一个中心化的配置服务器来管理应用程序所有环境中的配置。配置服务器可以使用文件系统中的配置文件、 Git、SVN、数据库等版本控制系统来管理配置内容。
特性
常用功能和配置
特性
- 版本控制配置内容:配置存储在版本控制系统中,如 Git,支持配置的版本管理和变更追踪。
- 环境分离:可以为不同的环境(开发、测试、生产等)提供不同的配置。
- 动态刷新:在不重启服务的情况下刷新配置(需要使用 @RefreshScope 或 Spring Cloud Bus)。
- 安全性:可以与 Spring Cloud Security 集成,对敏感配置进行加密和解密。
- 占位符解析:支持在配置文件中使用占位符,这些占位符会被解析为实际的配置值。
常用功能和配置
- 配置文件覆盖:客户端可以通过本地配置覆盖远程配置。
- 健康指标:Config Server 提供健康指标来监控配置服务的状态。
- 属性源标签:可以指定 Git 分支、标签或提交的哈希值来拉取配置。
- 搜索路径:可以配置多个搜索路径,用于在单个仓库中管理多个服务的配置。
- 文件格式支持:支持 properties、YAML、JSON 等多种配置文件格式。
- 访问控制:可以配置访问控制,限定谁可以访问 Config Server。
- 占位符解析:可以在配置文件中使用 spring.cloud.config.server.prefix 来设置 Config Server 的上下文路径。
Spring Cloud Alibaba
Seata
Seata 是一款开源的分布式事务解决方案,主要是通过 XID 和 3 个核心组件实现对分布式事务的协调和控制。
搭建Seata服务(TC)
微服务集成Seata
- 下载seata sever包。
- 找到conf目录中registry.conf文件,配置registry、config(注册中心和配置中心)信息。
- 在config指定的配置中心(Nacos)配置参数信息,如数据库、日志、超时等。
- 创建seata的基础表:创建seata库,global_table(全局事务表)、branch_table(分支事务表)、lock_table(全局锁表)。
- 运行seata server。
微服务集成Seata
- pom.xml中引入seata依赖包。
- application.properties中加入seata配置信息。
- 启动服务。
3个核心组件
TC 以 Seata 服务器(Server)形式独立部署,TM 和 RM 则是以 Seata Client 的形式集成在微服务中运行。
TC 以 Seata 服务器(Server)形式独立部署,TM 和 RM 则是以 Seata Client 的形式集成在微服务中运行。
- TC (Transaction Coordinator)
事务协调者(Seata服务),相当于中间人,主要负责维护全局事务和分支事务的状态。负责将RM的反馈结果响应给TM,并听从TM的最终决议,将具体决议(提交或回滚)发送给RM执行。 - TM (Transaction Manager)
事务管理器(事务发起方),根据RM第一阶段的执行结果进行决议,并将决议反馈给TC(相当于发号施令的)。 - RM (Resource Manager)
资源管理器(事务参与者),获取TC的执行命令去执行分支事务的第一阶段以及第二阶段,并将执行结果反馈给TC,相当于具体做事的。
事务模式
AT(无侵入式、最终一致),默认事务模式
1、基于支持本地 ACID 事务的关系型数据库
2、底层基于JDBC实现
一阶段(RM本地事务处理)
AT模式相关表
写隔离(Write Isolation)
全局锁
获取:提交本地事务前
释放:提交/回滚全局事务后
1、基于支持本地 ACID 事务的关系型数据库
2、底层基于JDBC实现
一阶段(RM本地事务处理)
- 全局事务的开始:TM(Transaction Manager)发起一个全局事务,并向TC(Transaction Coordinator,事务协调器)请求开启一个全局事务。TC生成一个全局唯一的事务ID(XID)并返回给TM。
- XID的传递:TM通过请求链路将XID传递给其他服务。在Seata中,服务之间的调用通常通过RPC框架实现,Seata的客户端库会自动将XID附加到RPC调用的上下文中,确保所有参与全局事务的服务(即RM,Resource Manager)都可以接收到这个XID。
- 注册分支事务、执行本地事务:每个RM在执行本地事务前,会先向TC注册一个分支事务,并关联到之前的XID。接着,RM按照以下步骤执行本地事务:生成前镜像SQL、执行业务SQL、生成后镜像SQL;
- 上报本地事务执行结果:每个RM在本地事务执行完毕后,会将执行结果上报给TC。这个结果包括本地事务是成功还是失败,以及相应的前后镜像数据。
- 全局事务的提交或回滚决策:TM根据业务逻辑的执行结果,向TC发起全局事务的提交或回滚请求。TC则根据TM的请求决定是协调各个RM提交各自的分支事务,还是根据各个分支事务的前镜像数据来回滚。
- 执行全局事务的提交或回滚:TC指导各个RM执行全局事务的提交或回滚。在回滚的情况下,RM会使用前镜像数据来撤销本地事务所做的变更。
- 完成全局事务:全局事务提交或回滚完成后,TC会进行相应的清理工作,比如删除与该全局事务相关的日志记录等。
- 提交:TM向TC发起全局事务完成请求,TC通知RM(RM删除对应UNDO_LOG),RM上报结果给TC。
- 回滚:TM向TC发起全局事务回滚请求,TC通知RM回滚(RM查询对应UNDO_LOG,先校验数据是否被修改,再根据镜像日志生成反向SQL执行回退),RM上报结果给TC。
AT模式相关表
- global_table:用于存储全局事务的状态信息。这个表记录了每个全局事务的唯一标识(XID)、状态(比如:开始、提交、回滚)、超时时间、事务名称等信息。
- branch_table:用于存储分支事务的信息。每个分支事务都会在这个表中有一个记录,包含分支事务的ID、对应的全局事务ID(XID)、资源信息(比如:数据库连接信息)、分支事务的状态(比如:注册、成功、失败)、锁定的资源信息等。
- lock_table:用于记录分布式事务过程中所涉及到的锁信息。在分布式事务中,不同的服务可能会操作同一条记录,为了保证事务的一致性,需要对这些记录加锁。lock_table表记录了这些锁的信息,包括被锁定资源的ID、锁定的资源类型、对应的分支事务ID等。
- undo_log:存储分支事务所涉及的数据修改的前镜像和后镜像。
写隔离(Write Isolation)
- 写隔离主要是为了防止在分布式事务中,不同事务对同一数据进行并发修改所引起的数据不一致问题。在Seata的AT模式下,通过使用lock_table来实现写隔离。
- 读隔离关注的是一个事务在读取数据时,如何能够看到一致的数据快照,而不受其他并发事务中的写操作影响。在Seata的AT模式中,读隔离是通过undo_log表来实现的。
全局锁
获取:提交本地事务前
释放:提交/回滚全局事务后
TCC(侵入式、最终一致)
1、Try:对业务资源的检查并预留;
2、Confirm:对业务处理进行提交,即 commit 操作,只要 Try 成功,那么该步骤一定成功;
3、Cancel:对业务处理进行取消,即回滚操作,该步骤会对 Try 预留的资源进行释放。
@LocalTCC:向 TC 注册一个分支事务
@TwoPhaseBusinessAction:定义了分支事务的 resourceId、commit方法、cancel方法、useTCCFence(默认false,是否开启事务控制表机制来解决空回滚、幂等、悬挂问题)
TCC 模式是分布式事务中非常重要的事务模式,但是幂等、悬挂和空回滚一直是 TCC 模式需要考虑的问题,Seata 框架在 1.5.1 版本完美解决了这些问题。TCC适用于多种数据存储场景:DB、Redis、文件系统(TCC提供了自定义实现方式足够灵活)
TCC问题(通过TCC事务控制表(tcc_fence_log)解决)
如何处理空回滚
空回滚:RM由于某些原因并未真正执行,报错触发了全局事务回滚,此时回滚是无意义/错误的
1、(TM)business.method中分别调用aService.method扣款、bService.method扣库存
2、调用aService.method时,aService服务宕机或网络超时异常,TM向TC发起回滚(此时aService.method并未成功执行,产生空回滚)
解决:Seata通过TCC事务控制表(tcc_fence_log)解决,aService.method、bService.method执行本地事务提交时会同时向tcc_fence_log表插入一条记录(包含事务的 XID 和 BranchID 信息,status=STATUS_TRIED),回滚前判断是否存在对应记录,不存在则不进行回滚
如何处理幂等
幂等:由于RM未能及时向TC响应二阶段结果,导致TC重复向RM发送commit/cancel指令
1、(TM)business.mthod中分别调用aService.method扣款,bService.method扣库存
2、bService.method提交/回滚,TC向RM(bService.method)发起commit/cancel,处理完commit/cancel后此时bService网络抖动或宕机未能向TC上报结果,导致TC重新向RM(bService.method)发起commit/cancel,产生幂等问题
解决:Seata通过TCC事务控制表(tcc_fence_log)解决,首先判断tcc_fence_log表是否存在记录,再判断status是否等于STATUS_COMMITTED(不会再次commit)/STATUS_ROLLBACKED(不会再次cancel)
如何处理悬挂
悬挂:由于网络原因导致RM本地事务在回滚之后执行,导致RM资源无法释放
1、(TM)business.mthod中分别调用aService.method扣款,bService.method扣库存
2、调用aService.method时出现网络拥堵,触发Seata全局事务超时限制导致TM向TC发起回滚,此时RPC请求才到达aService.method并执行本地事务(此时全局事务已结束,该操作永远无法提交释放),产生悬挂问题
解决:TC执行cancel前,判断tcc_fence_log表是否存在当前xid关联的记录(不存在则表示cancel优先与try),不存在则插入记录(status=STATUS_SUSPENDED),且不执行回滚操作;当aService.method执行提交本地事务并同时向tcc_fence_log表插入一条记录会出现主键冲突,则中断操作
1、Try:对业务资源的检查并预留;
2、Confirm:对业务处理进行提交,即 commit 操作,只要 Try 成功,那么该步骤一定成功;
3、Cancel:对业务处理进行取消,即回滚操作,该步骤会对 Try 预留的资源进行释放。
@LocalTCC:向 TC 注册一个分支事务
@TwoPhaseBusinessAction:定义了分支事务的 resourceId、commit方法、cancel方法、useTCCFence(默认false,是否开启事务控制表机制来解决空回滚、幂等、悬挂问题)
TCC 模式是分布式事务中非常重要的事务模式,但是幂等、悬挂和空回滚一直是 TCC 模式需要考虑的问题,Seata 框架在 1.5.1 版本完美解决了这些问题。TCC适用于多种数据存储场景:DB、Redis、文件系统(TCC提供了自定义实现方式足够灵活)
TCC问题(通过TCC事务控制表(tcc_fence_log)解决)
如何处理空回滚
空回滚:RM由于某些原因并未真正执行,报错触发了全局事务回滚,此时回滚是无意义/错误的
1、(TM)business.method中分别调用aService.method扣款、bService.method扣库存
2、调用aService.method时,aService服务宕机或网络超时异常,TM向TC发起回滚(此时aService.method并未成功执行,产生空回滚)
解决:Seata通过TCC事务控制表(tcc_fence_log)解决,aService.method、bService.method执行本地事务提交时会同时向tcc_fence_log表插入一条记录(包含事务的 XID 和 BranchID 信息,status=STATUS_TRIED),回滚前判断是否存在对应记录,不存在则不进行回滚
如何处理幂等
幂等:由于RM未能及时向TC响应二阶段结果,导致TC重复向RM发送commit/cancel指令
1、(TM)business.mthod中分别调用aService.method扣款,bService.method扣库存
2、bService.method提交/回滚,TC向RM(bService.method)发起commit/cancel,处理完commit/cancel后此时bService网络抖动或宕机未能向TC上报结果,导致TC重新向RM(bService.method)发起commit/cancel,产生幂等问题
解决:Seata通过TCC事务控制表(tcc_fence_log)解决,首先判断tcc_fence_log表是否存在记录,再判断status是否等于STATUS_COMMITTED(不会再次commit)/STATUS_ROLLBACKED(不会再次cancel)
如何处理悬挂
悬挂:由于网络原因导致RM本地事务在回滚之后执行,导致RM资源无法释放
1、(TM)business.mthod中分别调用aService.method扣款,bService.method扣库存
2、调用aService.method时出现网络拥堵,触发Seata全局事务超时限制导致TM向TC发起回滚,此时RPC请求才到达aService.method并执行本地事务(此时全局事务已结束,该操作永远无法提交释放),产生悬挂问题
解决:TC执行cancel前,判断tcc_fence_log表是否存在当前xid关联的记录(不存在则表示cancel优先与try),不存在则插入记录(status=STATUS_SUSPENDED),且不执行回滚操作;当aService.method执行提交本地事务并同时向tcc_fence_log表插入一条记录会出现主键冲突,则中断操作
XA(无侵入式、强一致)
1、不存在中间状态
2、与AT模式相比没有UNDO_LOG
3、XA 模式使用 DataSourceProxyXA
1、不存在中间状态
2、与AT模式相比没有UNDO_LOG
3、XA 模式使用 DataSourceProxyXA
Saga(侵入式、最终一致)
1、业务流程长、业务流程多
2、(RM)参与者包含其它公司或遗留系统服务,无法提供 TCC 模式要求的三个接口
1、业务流程长、业务流程多
2、(RM)参与者包含其它公司或遗留系统服务,无法提供 TCC 模式要求的三个接口
对比图
Nacos
Nacos是一个开源的动态服务发现、配置和服务管理平台,主要满足可用性(A)和分区容错性(P),也支持CP。
1、Naming和Configuration的前两个字母,s为Service。
2、Nacos就是注册中心+配置中心的组合,Nacos=Eureka+Config+Bus。
3、默认端口8848。
1、Naming和Configuration的前两个字母,s为Service。
2、Nacos就是注册中心+配置中心的组合,Nacos=Eureka+Config+Bus。
3、默认端口8848。
集群
- Nginx作为Nacos客户端与服务端的中间层
- Nacos客户端连接Nginx,Nginx负责转发到某一个Nacos节点
- Nacos集群之间的数据同步基于Mysql
0 条评论
下一页