微服务高级篇
微服务保护
初识Sentinel
雪崩问题
微服务调用链路中的某个服务出现故障,导致前一个服务请求越积越多,从而引起整个链路中的所有微服务都不可用,这就是雪崩

解决方法:
超时处理:设定超时时间,当请求超过一定时间没有响应就是返回错误信息,不会无休止等待

如图,当请求时间超过一秒钟没有响应时就会直接返回错误信息,但是当数据量上去之后我们哪怕给1秒等待时间,但是进来的请求越来越多却还是1秒释放一个线程,久而久之就会导致瘫痪
舱壁模式:限定每一个业务能使用的线程数,避免耗尽整个tomcat的资源,因此也叫线程隔离。每个服务所使用的线程都是固定的,所以一个业务消耗只能消耗这几个线程,不会出现线程饱满的状况

熔断降级:由断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截访问该业务的一切请求。

当从服务A访问服务D的业务中,传输错误的比例大于我们设定的比例,服务A就会马上熔断通往服务D的业务。完美解决了资源浪费的情况
限流控制:限制业务访问的QPS,避免服务因流量的突增而故障。

通过使用Sentinel将大量涌入的请求缩减到受保护的服务可以承受的范围内,就可以避免因为流量突增而导致的故障
微服务整合Sentinel
在order-service中整合Sentinel,并且连接Sentinel的控制台,步骤如下
引入sentinel依赖
1
2
3
4<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>配置控制台地址
1
2
3
4
5spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080访问微服务的任意端点,触发sentinel的监控

限流规则
簇点链路:就是项目内的调用链路,链路中被监控的每个接口就是一个资源。默认情况下sentinel会监控SpringMVC的每一个端点(Endpoint),因此SpringMVC的每一个端点(Endpoint)就是调用链路中的一个资源。
流控、熔断等都是针对簇点链路中的资源来设置的,因此我们可以点击对应资源后面的按钮来设置规则:
直接:统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式
关联:统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流
使用场景︰比如用户支付时需要修改订单状态,同时用户要查询订单。查询和修改操作会争抢数据库锁,产生竞争。业务需求是有限支付和更新订单的业务,因此当修改订单业务触发阈值时,需要对查询订单业务限流。
满足下面条件可以使用关联模式:
- 两个有竞争关系的资源
- 一个优先级较高,一个优先级较低

当/white资源访问量触发阙值时,就会对/read资源限流,避免影响/white资源
链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流
流控效果
Warm Up慢热
warm up也叫预热模式,是应对服务冷启动的一种方案。请求阈值初始值是threshold / coldFactor,持续指定时长后,逐渐提高到threshold值。而caldFactor的默认值是3
例如,我设置QPs的threshold为10,预热时间为5秒,那么初始阈值就是10/3,也就是3,然后在5秒后逐渐增长到10.排队等待
当请求超过QPs阈值时,快速失败和warm up会拒绝新的请求并抛出异常。而排队等待则是让所有请求进入一个队列中,然后按照阈值允许的时间间隔依次执行。后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝。
例如:QPS=5,意味着每200ms处理一个队列中的请求;timeout =2000,意味着预期等待超过2000ms的请求会被拒绝并抛出异常
Feign整合Sentinel
SpringCloud中微服务调用都是通过Fegin来实现的,因此做客户端保护必须整合Feign和Sentinel。
修改OrderService的application.yml文件,开启Feign的Sentinel功能
1
2
3fegin:
sentinel:
enabled: true给FeginClient编写失败后的降级逻辑
方法一:FallbackClass,无法对远程调用的异常做处理
方法二:FallbackFactory,可以对远程调用的异常做处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
public UserClient create(Throwable throwable) {
//创建UserClient接口实现类,实现其中的方法,编写失败降级的处理逻辑
return new UserClient() {
public User findById(Long id) {
//记录异常信息
log.error("查询用户异常",throwable);
//根据业务需求返回默认的数据,这里是空用户
return new User();
}
};
}
}在feing-api项目中的DefaultFeignConfiguration类中将userClientFallbackFactory注册为一个Bean:
1
2
3
4
public UserClientFallbackFactory userClientFallbackFactory(){
return new UserClientFallbackFactory();
}在feing-api项目中的Userclient接口中使用UserClientFallbackFactory:
1
2
3
4
5
6
public interface UserClient {
User findById( Long id);
}
线程隔离
线程隔离有两种方式实现:
线程池隔离
规定好线程,每一次只能涌进固定数量的业务从而达到线程隔离
信号量隔离( Sentinel默认采用)
在请求和服务中间添加一层计数器,设置好固定的数量,每一个请求进来计数器就-1。当计数器归零时则不会再进请求。当然当请求完成后便会归还给计数器。从而达到线程隔离作用

熔断降级
熔断降级是解决雪崩问题的重要手段。其思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。

断路器熔断策略有三种:慢调用、异常比例、异常数
慢调用:业务的响应时长(RT)大于指定时长的请求认定为慢调用请求。在指定时间内,如果请求数量超过设定的最小数量,慢调用比例大于设定的阈值,则触发熔断。例如:

解读:RT超过500ms的调用是慢调用,统计最近10000ms内的请求,如果请求量超过10次并且慢调用比例不低于0.5,则触发熔断,熔断时长为5秒。然后进入half-open状态,放行一次请求做测试。
异常比例或异常数∶统计指定时间内的调用,如果调用次数超过指定请求数,并且出现异常的比例达到设定的比例阈值(或超过指定异常数),则触发熔断。例如:


解读:统计最近1000ms内的请求,如果请求量超过10次,并且异常比例不低于0.5,则触发熔断,熔断时长为5秒。然后进入half-open状态,放行一次请求做测试。
分布式事务
在分布式系统下,一个业务跨越多个服务或数据源,每个服务都是一个分支事务,要保证所有分支事务最终状态一致,这样的事务就是分布式事务。
CAP定理
- Consistency(一致性):用户访问分布式系统中的任意节点,得到的数据必须一致
- Availability(可用性)∶用户访问集群中的任意健康节点,必须能得到响应,而不是超时或拒绝
- Partition(分区)∶因为网络故障或其它原因导致分布式系统中的部分节点与其它节点失去连接,形成独立分区。
BASE理论
BASE理论是对CAP的一种解决思路,包含三个思想:
- Basically Available(基本可用)︰分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。
- Soft State(软状态)∶在一定时间内,允许出现中间状态,比如临时的不、致状态。
- Eventually Consistent(最终一致性)∶虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致
Seata架构
Seata事务管理中有三个重要的角色:
- TC(Transaction Coordinator)-事务协调者︰维护全局和分支事务的状态,协调全局事务提交或回滚。
- TM(Transaction Manager)-事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务。
- RM(Resource Manager)-资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
微服务集成Seata
修改conf目录下的registry.conf文件:
内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29registry {
# tc服务的注册中心类,这里选择nacos,也可以是eureka、zookeeper等
type = "nacos"
nacos {
# seata tc 服务注册到 nacos的服务名称,可以自定义
application = "seata-tc-server"
serverAddr = "127.0.0.1:8848"
group = "DEFAULT_GROUP"
namespace = ""
cluster = "SH"
username = "nacos"
password = "nacos"
}
}
config {
# 读取tc服务端的配置文件的方式,这里是从nacos配置中心读取,这样如果tc是集群,可以共享配置
type = "nacos"
# 配置nacos地址等信息
nacos {
serverAddr = "127.0.0.1:8848"
namespace = ""
group = "SEATA_GROUP"
username = "nacos"
password = "nacos"
dataId = "seataServer.properties"
}
}在nacos添加配置
特别注意,为了让tc服务的集群可以共享配置,我们选择了nacos作为统一配置中心。因此服务端配置文件seataServer.properties文件需要在nacos中配好。
配置内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34# 数据存储方式,db代表数据库
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=123
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
# 事务、日志等配置
server.recovery.committingRetryPeriod=1000
server.recovery.asynCommittingRetryPeriod=1000
server.recovery.rollbackingRetryPeriod=1000
server.recovery.timeoutRetryPeriod=1000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
# 客户端与服务端传输方式
transport.serialization=seata
transport.compressor=none
# 关闭metrics功能,提高性能
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898其中的数据库地址、用户名、密码都需要修改成你自己的数据库信息。
启动TC服务
进入bin目录,运行其中的seata-server.bat即可:
启动成功后,seata-server应该已经注册到nacos注册中心了。
打开浏览器,访问nacos地址:http://localhost:8848,然后进入服务列表页面,可以看到seata-tc-server的信息:
微服务集成seata
引入依赖
首先,我们需要在微服务中引入seata依赖:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<!--版本较低,1.3.0,因此排除-->
<exclusion>
<artifactId>seata-spring-boot-starter</artifactId>
<groupId>io.seata</groupId>
</exclusion>
</exclusions>
</dependency>
<!--seata starter 采用1.4.2版本-->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>${seata.version}</version>
</dependency>修改配置文件
需要修改application.yml文件,添加一些配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14seata:
registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
# 参考tc服务自己的registry.conf中的配置
type: nacos
nacos: # tc
server-addr: 127.0.0.1:8848
namespace: ""
group: DEFAULT_GROUP
application: seata-tc-server # tc服务在nacos中的服务名称
cluster: SH
tx-service-group: seata-demo # 事务组,根据这个获取tc服务的cluster名称
service:
vgroup-mapping: # 事务组与TC服务cluster的映射关系
seata-demo: SH