文章目录
Springboot
启动执行流程
- 启动springboot程序会先创建SpringApplication对象、参数的初始化工作、判断程序类型以及初始化器和监听器,加载spring.factories文件到缓存中
- SpringApplication对象创建完成之后开始执行run方法,然后执行2个方法prepareContext,refreshContext,在这2个过程中完成了自动装配的核心功能
前者主要完成对上下文对象的初始化操作,包含了属性值的设置,比如环境对象;并且在load方法中启动类作为一个beandefinition注册到registry中,方便后续在进行beanfactorypostprocessor调用,找到对应的主类,来完成|@springbootApplication,@enableAutoConfiguration等注解的解析工作
后者会进行整个容器刷新过程,会调用spring的refresh方法,来完成应用程序的启动,在自动装配过程中,会调用invokebeanFactoryPostProcessor方法,在此方法中主要是对configurationClassPostProcessor处理,在调用的时候会先调用BDRPP中的postProcessBeanDefinitionRegistry方法,然后调用postProcessBeanFactory方法,而在执行postProcessBeanDefinitionRegistry方法的过程中会处理各种注解,包含了|@properySource,@componentScan,@componentScans,@bean,@Import注解的解析
- 在解析@import注解的时候,会有一个getImports方法,从主类开始递归调用,解析所有包含@import的注解,然后在processImport方法中对import进行分类,AutoConfigurationImportSelector归属于importSelector的子类,在后续会调用DeferredImportSelectorHandler中的process方法,来完成enableAutoConfiguration的加载
自动装配
@EnableAutoConfiguration
其中最关键的要属@Import(AutoConfigurationImportSelector.class),借助AutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。
bean加载
spring事务
- 通过AOP生成代理对象,在代理对象中重写方法;
jdk动态代理:代理对象反编译可以看到继承proxy实现接口,并且重写接口的所有方法(invocationHandler)
cglib动态代理:CGLib采用了非常底层的字节码技术(ASM),其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。(methodInterceptor) - spring事务是通过数据库连接实现的。当前线程中保存了一个map,key是数据源,value是数据库连接。
我们说的同一个事务,其实是指同一个数据库连接,只有拥有同一个数据库连接才能同时提交和回滚。如果在不同的线程,拿到的数据库连接肯定是不一样的,所以是不同的事务。
spring事务失效
- 方法的访问权限不是public:spring事务的源码中对方法的访问权限做了判断,在AbstractFallbackTransactionAttributeSource类的computeTransactionAttribute方法中有判断
- 方法被final修饰:spring事务底层使用了aop,也就是通过jdk动态代理或者cglib,帮我们生成了代理类,在代理类中重写该方法,如果被final或者static修饰,则无法重写
- 在方法中调用本类的其他方法:jdk代理是因为没有进入代理类
- (类本身) 未被spring管理
- 多线程调用:事务是根据数据库连接来实现的,多线程数据库连接不同
- (存储引擎)表不支持事务
- 错误的传播特性
- 未抛出异常、自定义异常
编程式事务
@Autowired
private TransactionTemplate transactionTemplate;
public void save(final User user) {
queryData1();
queryData2();
transactionTemplate.execute(transactionStatus -> {
addData1();
updateData2();
return Boolean.TRUE;
});
}
Mybatis
多数据源
多套数据源
定义:在项目中针对一个数据库都为其建立一套独立的数据处理逻辑,包括数据源(DataSource),会话工厂(SqlSessionFactory),连接,DAO操作。
动态数据源
定义:确定数量的多个数据源共用一个会话工厂,根据条件动态选取数据源进行连接、SQL 操作。
参数化变更数据源
定义:根据参数添加数据源,并进行数据源切换,数据源数量不确定。通常用于对多个数据库的管理工作。
mybatis拦截器
Mybatis拦截器设计的初衷就是为了供用户在某些时候可以实现自己的逻辑而不必去动Mybatis固有的逻辑。通过Mybatis拦截器我们可以拦截某些方法的调用,我们可以选择在这些被拦截的方法执行前后加上某些逻辑,也可以在执行这些被拦截的方法时执行自己的逻辑而不再执行被拦截的方法。
拦截的对象有Executor、ParameterHandler、StatementHandler、ResultSetHandler
- Executor
Mybatis中所有的Mapper语句的执行都是通过Executor进行的。Executor是Mybatis的核心接口。从其定义的接口方法我们可以看出,对应的增删改语句是通过Executor接口的update方法进行的,查询是通过query方法进行的
public interface Executor {
...
/**
* 执行update/insert/delete
*/
int update(MappedStatement ms, Object parameter) throws SQLException;
/**
* 执行查询,先在缓存里面查找
*/
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
/**
* 执行查询
*/
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
/**
* 执行查询,查询结果放在Cursor里面
*/
<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
...
}
- ParameterHandler
ParameterHandler用来设置参数规则,当StatementHandler使用prepare()方法后,接下来就是使用它来设置参数。所以如果有对参数做自定义逻辑处理的时候,可以通过拦截ParameterHandler来实现。
public interface ParameterHandler {
...
/**
* 设置参数规则的时候调用 -- PreparedStatement
*/
void setParameters(PreparedStatement ps) throws SQLException;
...
}
- StatementHandler
StatementHandler负责处理Mybatis与JDBC之间Statement的交互。
一般只拦截StatementHandler里面的prepare方法。
public interface StatementHandler {
...
/**
* 从连接中获取一个Statement
*/
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
/**
* 设置statement执行里所需的参数
*/
void parameterize(Statement statement)
throws SQLException;
/**
* 批量
*/
void batch(Statement statement)
throws SQLException;
/**
* 更新:update/insert/delete语句
*/
int update(Statement statement)
throws SQLException;
/**
* 执行查询
*/
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;
<E> Cursor<E> queryCursor(Statement statement)
throws SQLException;
...
}
- ResultSetHandler
ResultSetHandler用于对查询到的结果做处理。所以如果你有需求需要对返回结果做特殊处理的情况下可以去拦截ResultSetHandler的处理。
public interface ResultSetHandler {
/**
* 将Statement执行后产生的结果集(可能有多个结果集)映射为结果列表
*/
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
/**
* 处理存储过程执行后的输出参数
*/
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
springcloud
分布式锁
springboot整合redisson实现分布式锁
sentinel 限流熔断
3种流控模式
- 直接 模式: 直接模式就是对当前入口直接限流
- 关联 模式 : 当关联的资源达到阈值时就会对配置的资源进行限流
比如: 下单接口和查询订单接口,当我们对查询接口设置了流控规则并关联了下单接口,当下单接口的请求数量达到阈值时,就会对查询接口进行限流,这样就能保证下单接口的安全性 - 链路模式 : 对调用链路的入口进行限流,Sentinel不仅可以对接口入口进行限流还可以对方法进行限流.
例如 我们通过@SentinelResource 对 方法getUser 进行了限流,通过链路模式关联了接口A,在调用过程中,接口A和接口B都会调用getUser方法,当getUser方法达到调用阈值时,就会对关联的接口A进行限流,而接口B可以不受限制的进行调用.
使用链路模式还需要配置一下web-context-unify配置,限流才会生效
3种流控效果
- 快速失败: 当并发量超过设置的阈值后,超出的请求会被直接拒绝.
- Warm Up(激增流量) : 流控预热模式有一个预热时长参数,当大量请求过来时,并不是直接处理相当于阈值数量的请求,而是在设置的预热时长内,逐渐递增式的处理请求,直到达到阈值数量.递增初始值的冷加载因子值为设置的阈值除以3.
- 排队等待(脉冲流量): 相当于队列,当大量请求过来时,超过阈值的请求不会马上拒绝,而是进行排队等待,并提供了一个设置超时时间的参数,在超时时间到期之前,如果前面的请求被出来完,排队等待中的请求会被继续处理
3种熔断策略
-
慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
-
异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
-
异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
注意异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。为了统计异常比例或异常数,需要通过 Tracer.trace(ex) 记录业务异常。
sentinel整合Openfeign
- 添加依赖
<!-- 微服务调用 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 开启Openfeign对sentinel支持配置
#开启Openfeign对sentinel的支持默认为false
feign:
sentinel:
enabled: true
- 添加对Openfeign接口的实现
public class UserFeignServuceFallback implements UserFeignServuce {
@Override
public String test() {
//TODO 降级操作 实现feign接口的方法 可以在这里做失败处理 比如可以在这里记录降级日志,后续自动补偿
//比如 积分服务, 在服务降级的请求下,如果积分服务被关闭,无法调用,可以现在这里记录日志,需要添加的用,和对应的积分
// 可以通过另外的定时任务捞取日志中的失败操作对用户进行补偿
return null;
}
}
- 在Openfeign接口添加 fallback 属性,并指向实现类
@FeignClient(value = "manage-client",fallback = UserFeignServuceFallback.class)
public interface UserFeignServuce {
@RequestMapping(value = "/index/test")
String test();
}
热点参数限流
热点参数规则使用必须要配合 @SentinelResource 注解来处理;
系统保护规则
Sentinel 系统自适应保护从整体维度对应用入口流量进行控制,结合应用的 Load、总体平均 RT、入口 QPS 和线程数等几个维度的监控指标,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。
Seata
Seata术语
-
TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。 -
TM (Transaction Manager) - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。 -
RM (Resource Manager) - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。