day03
微服务保护和分布式事务
微服务保护
微服务雪崩问题:某个微服务出现故障,导致调用链上的集群都被阻塞
解决方案:
- 请求限流:限制或控制接口访问的并发流量,避免服务因流量激增而出现故障。
- 线程隔离:控制业务可用的线程数量,将故障隔离在一定范围内
- 服务熔断:将异常比例过高的接口断开,拒绝所有请求,直接走fallback
- fallback:失败处理逻辑,让业务失败时不抛出异常,返回默认数据或友好提示
常用工具:sentinel
sentinel是阿里云开源的一个微服务保护方案,主要包括核心jar包和控制台两个模块
请求限流
在sentinel中,针对某个
簇点
进行流控,限制QPS,簇点就是controller层的一个个请求方法
线程隔离
在sentinel中,针对某个
簇点
进行流控,设置并发线程数
,这样就能限制该业务的线程资源由于是对微服务调用,所以我们要配置openFeign整合sentinel:
<!--sentinel--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
- openFeign整合sentinel
feign: sentinel: enabled: true # 开启feign对sentinel的支持
- 默认情况下SpringBoot项目的tomcat最大线程数是200,允许的最大连接是8492,单机测试很难打满。因此修改tomcat连接配置
server: port: 8082 tomcat: threads: max: 50 # 允许的最大线程数 accept-count: 50 # 最大排队等待数量 max-connections: 100 # 允许的最大连接
- 在sentinel中设置簇点并发线程数即可实现线程隔离
服务熔断
由于设置请求限流和线程隔离,这样会导致接口的QPS较低,对于这些请求,我们不一定要抛出异常,可以进行
降级
或熔断处理
- 降级处理
触发限流或熔断后,不一定要直接抛出异常,可以让该请求走
降级逻辑fallback
,返回一些默认数据或友好数据给FeignClient编写失败后的降级逻辑有两种方式:
方式一:FallbackClass,无法对远程调用的异常做处理
方式二:FallbackFactory,可以对远程调用的异常做处理,我们一般选择这种方式。
降级处理实现(针对ItemClient):
- 定义一个类实现
FallbackFactory<ItemClient>
,这里指定泛型为<ItemClient>
表明是ItemClient的降级处理逻辑- 重写
create
方法,该方法的目的是返回一个新ItemClient对象- 在
return new ItemClient(){}
中重写接口方法,为各个接口添加降级处理
- 当触发限流时,就会走这里的降级逻辑
package com.hmall.api.fallback; import com.hmall.api.client.ItemClient; import com.hmall.api.dto.ItemDTO; import com.hmall.api.dto.OrderDetailDTO; import com.hmall.common.exception.BizIllegalException; import com.hmall.common.utils.CollUtils; import org.springframework.cloud.openfeign.FallbackFactory; import java.util.Collection; import java.util.List; public class ItemFallbckFactory implements FallbackFactory<ItemClient> { /** * 创建并返回一个新的ItemClient类 * @param cause * @return */ @Override public ItemClient create(Throwable cause) { return new ItemClient() { //对queryItemByIds接口做降级处理 @Override public List<ItemDTO> queryItemByIds(Collection<Long> ids) { return CollUtils.emptyList(); } //对deductStock接口做降级处理 @Override public void deductStock(List<OrderDetailDTO> items) { throw new BizIllegalException(cause); } }; } }
- 同时,要将ItemFallbckFactory注册为一个bean,在配置类中声明
@Bean public ItemFallbckFactory itemFallbckFactory(){ return new ItemFallbckFactory(); }
- 为ItemClient添加fallback参数
@FeignClient(value = "item-service", fallbackFactory = ItemFallbckFactory.class) public interface ItemClient {...}
- 服务熔断
在sentinel控制台中可针对某个簇点配置熔断策略,配置策略主要有三种:慢调用比例、异常比例、异常数
分布式事务
分布式事务指多个微服务的分支事务关联形成的全局事务,无法满足事务的ACID特性,因此需要引入外部服务来解决分布式事务
分布式事务解决方案——Seata
Seata架构
- TC (Transaction Coordinator) -协调者:维护全局和分支事务的状态,协调全局事务提交或回滚。
- TM (Transaction Manager) - 事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务。
- RM (Resource Manager) - 资源管理器:管理分支事务,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
Seata服务部署
-
微服务集成seata
为了方便集成seata,将配置文件上传到nacos
- 在微服务中引入相关依赖和seata依赖
<!--seata--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> </dependency>
- nacos添加seata配置文件.yaml,微服务拉取该配置文件
seata: registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址 type: nacos # 注册中心类型 nacos nacos: server-addr: 192.168.48.100: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"
- 将
@Transactional
注解改为seata提供的@GlobalTransactional
注解,即开启分布式事务的入口
seata的分布式事务解决方案
seata提供了四种解决方案:
- XA:当一个分支事务执行完毕后,不立刻提交,持有锁,报告状态,等所有分支事务完成后,由TC来通知提交/回滚
- TCC
- AT:记录undo.log快照,分支事务执行成功后直接提交,释放锁,报告状态
- SAGA
简述
AT
模式与XA
模式最大的区别是什么?
XA
模式一阶段不提交事务,锁定资源;AT
模式一阶段直接提交,不锁定资源。XA
模式依赖数据库机制实现回滚;AT
模式利用数据快照实现数据回滚。XA
模式强一致;AT
模式最终一致
RabbitMQ
RabbitMQ是基于Erlang语言开发的开源消息通信中间件,官网地址
RabbitMQ的docker部署
- 15672:RabbitMQ提供的管理控制台的端口
- 5672:RabbitMQ的消息发送处理接口
docker run \
-e RABBITMQ_DEFAULT_USER=skywalker \
-e RABBITMQ_DEFAULT_PASS=123321 \
-v mq-plugins:/plugins \
--name mq \
--hostname mq \
-p 15672:15672 \
-p 5672:5672 \
--network hm-net\
-d \
rabbitmq:3.8-management
RabbitMQ相关概念和原理
可将消息直接发给队列,或发给交换机,再由交换机转发给绑定的队列
publisher
:生产者,也就是发送消息的一方consumer
:消费者,也就是消费消息的一方queue
:队列,存储消息。生产者投递的消息会暂存在消息队列中,等待消费者处理exchange
:交换机,负责消息路由。生产者发送的消息由交换机决定投递到哪个队列。virtual host
:虚拟主机,起到数据隔离的作用。每个虚拟主机相互独立,有各自的exchange、queue
Spring集成RabbitMQ
RabbitMQ基于AMQP协议通信,Spring官方基于RabbitMQ提供了SpringAMQP消息收发模板工具,只需引入依赖并配置rabbitmq即可
- 引入依赖
<!--AMQP依赖,包含RabbitMQ--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
- 配置rabbitmq
spring: rabbitmq: host: 192.168.150.101 # 你的虚拟机IP port: 5672 # 端口 virtual-host: /hmall # 虚拟主机 username: hmall # 用户名 password: 123 # 密码
- 发送消息
注入
SpringAMQP
中的RabbitTemplate
对象,调用convertAndSend(args[])方法发送消息
- 接收消息
在方法上声明
@RabbitListener
注解,指定交换机、队列等参数,即可通过形参
接收到对应队列的消息
交换机类型
交换机的类型有四种:
- Fanout:广播,将消息交给所有绑定到交换机的队列。我们最早在控制台使用的正是Fanout交换机
- Direct:订阅,基于RoutingKey(路由key)发送给订阅了消息的队列
- Topic:通配符订阅,与Direct类似,只不过RoutingKey可以使用通配符
- Headers:头匹配,基于MQ的消息头匹配,用的较少。
声明队列和交换机
- 方式一:基于Bean来声明
- 方式二:基于
@RabbitListener
注解声明
配置JSON消息转换器
在生产者和消费者都要配置
//导入依赖
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.10</version>
</dependency>
@Bean
public MessageConverter messageConverter(){
// 1.定义消息转换器
Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
// 2.配置自动创建消息id,用于识别不同消息,也可以在业务中基于ID判断是否是重复消息
jackson2JsonMessageConverter.setCreateMessageIds(true);
return jackson2JsonMessageConverter;
}