2020年6月6日,博客搭建基本完成啦
2023蓝桥杯广东省赛个人题解
2023蓝桥杯广东省赛个人题解
仅供参考,不保证思路和代码的正确性。
试题 A: 日期统计
暴力,忘记判重,白给
1 | #include<bits/stdc++.h> |
试题 B: 01 串的熵
暴力,记得处理四舍五入,11027421
1 | #include<bits/stdc++.h> |
试题 C: 冶炼金属
思维,注意处理边界情况
1 | #include<bits/stdc++.h> |
试题 D: 飞机降落
全排列处理,复杂度为10! * 10,可能不是最优解
1 | #include<bits/stdc++.h> |
试题 E: 接龙数列
简单动态规划
1 | #include<bits/stdc++.h> |
试题 F: 岛屿个数
首先bfs每个点,判断是否能通过0到达岛外,可以斜着走,无法到达就说明是子岛屿,记得剪枝。第二次bfs判断连通块个数,即为答案。
1 | #include<bits/stdc++.h> |
试题 G: 子串简写
前缀和,但这个位置的题目真的这么简单么,怀疑可能读错题。
1 | #include<bits/stdc++.h> |
试题 H: 整数删除
维护链表和一个set,每次取出set中最小的,判断是否合法,并对链表节点和值进行修改。
1 | #include<bits/stdc++.h> |
试题 I: 景区导游
最近公共祖先+简单容斥,LCA边想边敲,敲的很烂,思路应该问题不大。
1 | #include<bits/stdc++.h> |
试题 J: 砍树
正解没有想到,枚举砍掉的边,用LCA判断是否在一颗子树上,可以通过30%样例。
1 | #include<bits/stdc++.h> |
分布式锁
Java并发编程的艺术
RDB与AOF持久化对比
RDB | AOF | |
---|---|---|
持久化方式 | 定时对整个内存做快照 | 记录每一次执行的命令 |
数据完整性 | 不完整,两次备份之间会有数据丢失 | 相对完整,取决于刷盘策略 |
文件大小 | 会有压缩,体积小 | 记录命令,体积大 |
宕机恢复速度 | 很快 | 慢 |
数据恢复优先级 | 低,因为完整性不如AOF | 高 |
系统资源占用 | 高,大量CPU与内存消耗 | 低,主要是磁盘IO资源,重写时占用高 |
使用场景 | 容忍数分钟数据丢失,追求更快启动速度 | 对数据安全性要求较高 |
Seata
Seata是什么?
Seata是2019年1月蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。致力于提供高性能和简单易用的分布式事务服务,为用户打造一站式的分布式解决方案。
Seata架构
Seata事务管理中有三个重要角色
- TC - 事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚
- TM - 事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务
- RM - 资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚
Seata提供了四种不同的分布式事务解决方案
- XA模式:强一致分阶段事务模式,牺牲了一定的可用性,无业务侵入
- TCC模式:最终一致的分阶段事务模式,有业务侵入
- AT模式:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式
- SAGA模式:长事务模式,有业务侵入
XA模式
原理
XA规范是X/Open组织定义的分布式事务处理(DTP)标准,XA规范描述了全局的TM与局部RM之间的接口,几乎所有主流的数据库都对XA规范提供了支持
- RM一阶段工作:
- 注册分支事务到TC
- 执行分支事务sql但不提交
- 报告执行状态到TC
- TC二阶段工作:
- TC检测各分支事务执行状态
- 判断提交或回滚
- RM二阶段工作:
- 接收TC指令,提交或回滚事务
实现XA模式
Seata的starter已经完成了XA模式的自动装配
- 修改配置文件
1 | seata: |
- 发起全局事务的入口添加@GlobalTransactional注解
1 | @Override |
AT模式
原理
AT模式同样是分阶段提交的事务模型,不过弥补了XA模型中资源锁定周期过长的缺陷
- RM一阶段工作:
- 注册分支事务到TC
- 记录undo-log(数据快照)
- 执行分支事务sql并提交
- 报告执行状态到TC
- TC二阶段工作:
- TC检测各分支事务执行状态
- 判断提交或回滚,提交删除快照,回滚读取快照
- RM二阶段回滚工作:
- 根据undo-log恢复数据
AT模式的写隔离
全局锁:由TC(在数据库中)记录当前正在操作某行数据的事务,该事务持有全局锁,具备执行权
- 事务1获取了全局锁,准备回滚,等待事务2释放DB锁(读写隔离)
- 事务2持有DB锁,等待全局锁,重试默认30次,间隔10毫秒,超时释放(避免死锁)
- 事务1获取DB锁,将数据库当前数据和更新后undo-log数据对比,判断是否一致
TCC模式
与AT模式相似,每阶段都是独立事务,但不加锁。不同的是TCC通过人工编码来实现数据恢复,需要实现三个方法
- Try:资源的检测和预留,比如可用余额预留为冻结余额,不同的操作冻结的余额相互隔离
- Confirm:完成资源操作业务;要求Try成功Confirm一定要成功
- Cancel:预留资源释放,try的反向操作
优点
- 一阶段完成直接提交事务,释放数据库资源,性能好
- 相比AT模型,无需生成快照,无需使用全局锁,性能最强
- 不依赖数据库事务,而是依赖补偿操作,可以用非事务数据库
缺点
- 有代码侵入,需要人为编写try、Confirm和Cancel接口
- 软状态,最终一致
- 需要考虑Confirm和Cancel失败情况,做好幂等处理
空回滚
当某分支事务的try阶段阻塞时,可能导致全局事务超时而触发二阶段的cancel操作,在未执行try操作时先执行了cancel操作,这时cancel不能做回滚,就是空回滚
业务悬挂
对于已经空回滚的业务,如果以后继续执行try,就永远不可能confirm或cancel,这就是业务悬挂。应当组织执行空回滚后try操作,避免悬挂
业务分析
为了实现空回滚,防止业务悬挂,以及幂等性要求。我们必须在数据库记录预留资源信息同时,记录当前事务id和执行状态
声明TCC接口,并实现三个方法
1 | @Service |
Saga模式
Saga模式时SEATA提供的长事务解决方案。也分为两个阶段
- 一阶段:直接提交事务,与TCC不同的是不做预留操作
- 二阶段:失败则通过编写补偿业务来回滚
优点
- 事务参与者可以基于事件驱动实现异步调用,吞吐高
- 一阶段直接提交事务,无锁,性能好
- 不用编写TCC中三个阶段,实现简单
缺点
- 软状态持续时间不确定,时效性差
- 无锁,无事务隔离,会有脏写
分布式事务
在分布式系统下,一个业务跨越多个服务或数据源,每个服务都是一个分支事务,要保证所有分支事务最终状态一致,这样的事务就是分布式事务。
CAP定理
分布式系统三个指标:
- Consistency(一致性):用户访问分布式系统中的任意节点,得到的数据必须一致
- Availability(可用性):用户访问集群中的任意健康节点,必须能得到响应,而不是超时或拒绝
- Partition tolerance(分区容错性):出现分区问题,C和A无法同时满足
- 分区:因为网络故障或其它原因导致分布式系统中的部分节点与其它节点失去连接,形成独立分区
- 容错:在集群出现分区时,整个系统也要持续对外提供服务
Eric Brewer认为,分布式系统无法同时满足三个指标,这个结论就是CAP定理
BASE理论
BASE理论时对CAP的一种解决思路
- Basically Available(基本可用):分布式系统出现故障时,允许损失部分可用性,即保证核心可用
- Soft State(软状态):在一定时间内,允许出现中间状态,比如临时的不一致状态
- Eventually Consistent(最终一致性):虽然无法保证强一致,但是在软状态结束后,最终达到数据一直
AP模式
各子事务分别执行和提交,允许出现结果不一致,然后采用弥补措施恢复数据即可,实现最终一致
CP模式
各个子事务执行后互相等待,同时提交,同时回滚,达成强一致,但事务等待过程中,处于弱可用状态
事务协调者
各个子系统之间必须能感知到彼此的事务状态,才能保证状态一致,因此需要一个事务协调者来协调每一个事务的参与者(子系统事务)
授权规则
授权规则可以对调用方的来源做控制,有白名单和黑名单两种方式
- 白名单:来源在白名单内的调用者允许访问
- 黑名单:来源在黑名单内的调用者不允许访问
可以解决绕过网关直接访问微服务的问题
授权方法
- 实现RequestOriginParser接口,自定义处理逻辑
1 | @Component |
- 在gateway服务中,利用过滤器添加指定请求头
1 | spring: |
- 在sentinel中添加约定好的授权规则
自定义异常
实现BlockExceptionHandler接口
BlockException包含多个子类:
异常 | 说明 |
---|---|
FlowException | 限流异常 |
ParamFlowException | 热点参数限流的异常 |
DegradeException | 降级异常 |
AuthorityException | 授权规则异常 |
SystemBlockException | 系统规则异常 |
规则持久化
规则管理模式
- 原始模式:默认模式,将规则保存在内存,重启服务丢失
- pull模式:控制台将配置的规则推送给Sentinel客户端,客户端会保存到本地文件或数据库中。之后定时轮询更新
- push模式:控制台将配置规则推送到远程配置中心,例如Nacos。Sentinel客户端监听Nacos,获取配置变更的推送消息,完成本地更新
隔离和降级
虽然限流可以尽量避免因高并发而引起的服务故障,但服务还会因为其它原因而故障。而要将这些故障控制在一定范围,避免雪崩,就要靠线程隔离(舱壁模式)和熔断降级的手段。
Feign整合Sentinel
SpringCloud中,微服务调用都是通过Feign来实现的,因此做客户端保护必须整合Feign和Sentinel
- 修改配置文件,开启Feign的Sentinel功能
- 给FeignClient编写失败后的降级逻辑
- 方式一:FallbackClass,无法对远程调用的异常做处理
- 方式二:FallbackFactory,可以对远程调用的异常做处理
使用FallbackFactory实现降级逻辑
- 定义类,实现FallbackFactory接口的create方法
1 | @Slf4j |
- 将实现的FallbackFactory注册为一个Bean
1 | @Bean |
- 在Client接口中使用FallbackFactory(注解添加)
1 | @FeignClient(value = "userservice", fallbackFactory = UserClientFallBackFactory.class) |
线程隔离
- 线程池隔离
- 支持主动超时和异步调用
- 但线程额外开销较大
- 场景:低扇出
- 信号量隔离(Sentinel默认)
- 轻量级,无额外开销
- 不支持主动超时、异步调用
- 场景:高频调用,高扇出
熔断降级
也是解决雪崩问题的重要手段。由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求
断路器三种状态
- Closed:不会拦截任何请求
- Open:熔断,拦截进入该服务的请求,有持续时间
- Half-Open:Open状态时间结束,会放行请求,根据结果切换状态
熔断策略-慢调用
业务响应时长(RT)大于指定时长的请求认定为慢调用请求。在指定时间内,如果请求数量超过设定的最小数量且慢调用比例大于设定的阈值,则触发熔断
熔断策略-异常比例
慢调用比例换成抛出异常比例即可
熔断策略-异常数
指定异常次数
Sentinel入门与限流
雪崩问题
微服务调用链路中的某个服务故障,引起整个链路中的所有微服务都不可用
解决雪崩问题的常见方式有四种(前三种避免故障传递,流量控制预防故障发生):
- 超时处理:设定超时时间,请求超过一定时间没有响应就返回错误信息,不会无休止等待
- 舱壁模式:限定每个业务能使用的线程数,避免耗尽整个tomcat资源,因此也叫线程隔离
- 熔断降级:由断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截访问该业务的一切请求
- 流量控制:限制业务访问的QPS(每秒钟请求数量),避免服务因流量的突增而故障
服务保护技术对比(Sentinel与Hystrix)
Sentinel | Hystrix | |
---|---|---|
隔离策略 | 信号量隔离 | 线程池隔离/信号量隔离 |
熔断降级策略 | 基于慢调用比例或异常比例 | 基于失败比率 |
实时指标实现 | 滑动窗口 | 滑动窗口(基于RxJava) |
规则配置 | 支持多种数据源 | 支持多种数据源 |
扩展性 | 多个扩展点 | 插件的形式 |
基于注解的支持 | 支持 | 支持 |
限流 | 基于QPS,支持基于调用关系的限流 | 有限的支持 |
流量整形 | 支持慢启动、匀速排队模式 | 不支持 |
系统自适应保护 | 支持 | 不支持 |
控制台 | 开箱即用,可配置规则、查看秒级监控、机器发现等 | 不完善 |
常见框架适配 | Servlet、Spring Cloud、Dubbo、gRPC等 | Servlet、Spring Cloud Netflix |
安装Sentinel控制台
- 运行jar包
- 访问页面,默认账号密码为sentinel
- 通过配置修改
配置项 | 默认值 | 说明 |
---|---|---|
server.port | 8080 | 服务端口 |
sentinel.dashboard.auth.username | sentinel | 默认用户名 |
sentinel.dashboard.auth.password | sentinel | 默认密码 |
- 启动时修改
1 | java -jar sentinel-dashboard-1.8.1.jar -Dserver.port=8090 |
微服务整合Sentinel
- 引入sentinel依赖
1 | <dependency> |
- 配置控制台地址
1 | spring: |
- 访问微服务任意端点,触发sentinel监控
Sentinel限流规则
簇点链路
项目内的调用链路,链路中被监控的每个接口就是一个资源。默认情况下Sentinel会监控SpringMVC每一个端点
在Sentinel控制台设置流控规则
- 设置QPS,并使用Apache JMeter测试
- 高级配置:流控模式、流控效果
流控模式:直接、关联、链路
- 直接:默认模式,统计当前资源请求,触发阈值对当前资源限流
- 关联:统计与当前资源相关的另一个资源,触发阈值对当前资源限流(A触发阈值对B限流),如触发修改订单阈值,对查询订单限流
- 链路:只统计从指定链路访问到本地的资源的请求,触发阈值时,对指定链路限流
流控效果,请求达到流控阈值时应该采取的措施,包括三种:快速失败、warm up、排队等待
- 快速失败:达到阈值后,新的请求立即被拒绝抛出FlowException异常
- warm up:预热模式,对超出阈值的请求同样拒绝,但这种模式的阈值会动态变化,从小增大到最大阈值
- 排队等待:让所有请求按照先后次序排队执行,间隔不小于指定时常
添加限流方法
Sentinel默认只标记Controller中的方法为资源,如果要标记其它方法,需要利用@SentinelResource注解
Sentinel默认会将Controller方法做context整合,导致链路模式的流控失效,需要修改application.yml
1 | spring: |
流控效果-warm up
应对服务冷启动的一种方案,请求阈值初始值为threshold/coldFactor,持续指定时长后,逐渐提高到threshold,coldFactor(冷启动因子)默认值为3
流控效果-排队等待
让所有请求进入一个队列中,然后按着阈值允许的时间间隔依次执行。后来的请求必须等待前面执行完成,如果请求预期时间超出最大时长,则会被拒绝
- 例如,QPS=5,也就是每200ms处理一个请求。timeout=2000,意味着等待超过2000ms就会抛出异常
热点参数限流
分别统计与设定参数值相同的请求,判断是否超过QPS阈值
- 例如参数索引为0,单机阈值为5,统计窗口时长为1,含义为:对当前资源0号参数(第一个参数)的请求做统计,每1秒的请求数量不超过5
- 高级选项可以对部分参数做例外配置