一、雪崩问题
(1)雪崩问题
1.雪崩问题产生的原因是什么?
①微服务相互调用,服务提供者出现故障或阻塞。
②服务调用者没有做好异常处理,导致自身故障。
③调用链中的所有服务级联失败,导致整个集群故障。
2.解决问题的思路有哪些?
①尽量避免服务出现故障或阻塞。
保证代码的健壮性; 保证网络畅通; 能应对较高的并发请求;
②服务调用者做好远程调用异常的后备方案,避免故障扩散
(2)解决方案
解决雪崩问题的常见方案有哪些?
①请求限流:限制流量在服务可以处理的范围,避免因突发流量而故障。
②线程隔离:控制业务可用的线程数量,将故障隔离在一定的范围。
③服务熔断:将异常比例过高的接口断开,拒绝所有请求,直接走fallback。
④失败处理:定义fallback逻辑,让业务失败时不在抛出异常,而是返回默认数据或友好提示。
二、Sentinel
(1)初始Sentinel
Sentinel是阿里巴巴开源的一款微服务流量控制组件。
微服务整合Sentinel
①引入依赖
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
②配置控制台
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8090
http-method-specify: true # 开启请求方式前缀
簇点链路:就是单机调用链路,是一次请求进入服务后经过的每一个被Sentinel监控的资源链。
默认Sentinel会监控SpringMVC的每一个Endpoint(http接口)。限流、熔断等都是针对簇点链路中的资源设置的。而资源名默认就是接口的请求路径,Restful风格的API请求路径一般都相同,这会导致簇点资源名称重复。因此我们要修改配置,把请求方式+请求路径作为簇点资源名称。
(2)请求限流
在簇点链路后面点击流控按钮,即可对其做限流配置。我们利用Jemeter做限流测试,可以看出GET:/carts
这个接口的通过QPS稳定在6附近,而拒绝的QPS在4附近,符合我们的预期。
(3)线程隔离
在sentinel控制台中,会出现Feign接口的簇点资源,点击后面的流控按钮,即可配置线程隔离,注意,这里勾选的是并发线程数限制,也就是说这个查询功能最多使用5个线程,而不是5QPS。如果查询商品的接口每秒处理2个请求,则5个线程的实际QPS在10左右,而超出的请求自然会被拒绝。
将FeignClient作为Sentinel的簇点资源:
feign:
okhttp:
enabled: true
sentinel:
enabled: true
FeignClient的Fallback有两种配置方式:
方式一:FallbackClass,无法对远程调用的异常做处理
方式二:FallbackFactory,可以对远程调用的异常做处理,通常都会选择这种
①自定义类,实现FallbackFactory,编写对某个FeignClient的fallback逻辑
@Slf4j
public class ItemClientFallbackFactory implements FallbackFactory<ItemClient> {
@Override
public ItemClient create(Throwable cause) {
return new ItemClient() {
@Override
public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
log.error("查询商品失败!",cause);
return CollUtils.emptyList();
}
@Override
public void deductStock(List<OrderDetailDTO> items) {
log.error("扣减商品库存异常",cause);
throw new RuntimeException(cause);
}
};
}
}
②将刚刚定义的ItemClientFallbackFactory注册为一个Bean
@Bean
public ItemClientFallbackFactory itemClientFallbackFactory(){
return new ItemClientFallbackFactory();
}
③在ItemClient接口中使用ItemClientFallbackFactory
@FeignClient(value = "item-service",fallbackFactory = ItemClientFallbackFactory.class)
public interface ItemClient {
(4)服务熔断
熔断是解决雪崩问题的重要手段。思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。
点击控制台中簇点资源后的熔断按钮,即可配置熔断策略。
三、分布式事务
(1)介绍
在分布式系统中,如果一个业务需要多个服务合作完成,而且每一个服务都有事务,多个事务必须同时成功或失败,这样的事务就是分布式事务。其中的每个服务的事务就是一个分支事务。整个业务称为全局事务。
(2)初始Seata
分布式事务解决思路:
利用事务协调者,解决分布式事务,各个子事务之间必须能感知到彼此的事务状态,才能保证状态一致。
(3)部署TC服务
准备数据库表,准备配置文件,需要注意,要确保nacos、mysql都在hm-net网络中。如果某个容器不再hm-net网络,可以参考下面的命令将某容器加入指定网络:
docker network connect [网络名] [容器名]
Docker部署:
docker run --name seata \
-p 8099:8099 \
-p 7099:7099 \
-e SEATA_IP=1.12.232.19 \
-v ./seata:/seata-server/resources \
--privileged=true \
--network hm-net \
-d \
seataio/seata-server:1.5.2
(4)微服务集成Seata
①引入依赖
<!--统一配置管理-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--读取bootstrap文件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
②改造配置
首先在nacos上添加一个共享的seata配置,命名为shared-seata.yaml,
seata:
registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
type: nacos # 注册中心类型 nacos
nacos:
server-addr: 192.168.150.101:8848 # nacos地址
namespace: "" # namespace,默认为空
group: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUP
application: seata-server # seata服务名称
username: nacos
password: nacos
tx-service-group: hmall # 事务组名称
service:
vgroup-mapping: # 事务组与tc集群的映射关系
hmall: "default"
然后,改造trade-service
模块,添加bootstrap.yaml
spring:
application:
name: trade-service # 服务名称
profiles:
active: dev
cloud:
nacos:
server-addr: 192.168.150.101 # nacos地址
config:
file-extension: yaml # 文件后缀名
shared-configs: # 共享配置
- dataId: shared-jdbc.yaml # 共享mybatis配置
- dataId: shared-log.yaml # 共享日志配置
- dataId: shared-swagger.yaml # 共享日志配置
- dataId: shared-seata.yaml # 共享seata配置
可以看到这里加载了共享的seata配置。
然后改造application.yaml文件,内容如下
server:
port: 8085
feign:
okhttp:
enabled: true # 开启OKHttp连接池支持
sentinel:
enabled: true # 开启Feign对Sentinel的整合
hm:
swagger:
title: 交易服务接口文档
package: com.hmall.trade.controller
db:
database: hm-trade
(5)XA模式
XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA 规范 描述了全局的TM与局部的RM之间的接口,几乎所有主流的关系型数据库都对 XA 规范 提供了支持。Seata的XA模式如下:
一阶段的工作:
①RM注册分支事务到TC
②RM执行分支业务sql但不提交
③RM报告执行状态到TC
二阶段的工作:
①TC检测各分支事务执行状态
如果都成功,通知所有RM提交事务
如果有失败,通知所有RM回滚事务
②RM接收TC指令,提交或回滚事务
XA模式的优点是什么?
①事务的强一致性,满足ACID原则。
②常用数据库都支持,实现简单,并且没有代码侵入
XA模式的缺点是什么?
①因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差
②依赖关系型数据库实现事务
实现XA模式的步骤:
Seata的starter已经完成了XA模式的自动装配,实现非常简单,步骤如下:
①修改application.yaml文件(每个参与事务的微服务)(可在nacos的全局配置中修改),开启XA模式
seata:
data-source-proxy-mode: XA # 开启数据源代理的XA模式
②给发起全局事务的入口方法添加@GobalTransaction注解
@Override
@GlobalTransactional
public Long createOrder(OrderFormDTO order) {
// 创建订单 ... 略
// 清理购物车 ...略
// 扣减库存 ...略
return order.getId();
}
(6)AT模式
Seata主推的是AT模式,AT模式同样是分阶段提交的事务模型,不过缺弥补了XA模型中资源锁定周期过长的缺陷。
阶段一RM的工作:
①注册分支事务
②记录undo-log(数据快照)
③执行业务sql并提交
④报告事务状态
阶段二提交时RM的工作:
删除undo-log即可
阶段二回滚时RM的工作:
根据undo-log恢复数据到更新前
简述AT模式与XA模式最大的区别是什么?
①XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源。
②XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。
③XA模式强一致;AT模式最终一致
实现AT模式:
①首先,添加资料中的seata-at.sql(表)到微服务对应的数据库中
CREATE TABLE IF NOT EXISTS `undo_log`
(
`branch_id` BIGINT NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id',
`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',
`log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',
`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';
②修改application.yml文件,将事务模式修改为AT模式(默认为AT模式):
seata:
data-source-proxy-mode: AT # 开启数据源代理的AT模式