05微服务保护
我们现在的微服务项目已经可以使用了,但是在实际运行过程中会遇到一些问题,我们需要解决
1.常见问题
1.1级联问题(又叫雪崩问题)
我们现在有用户服务、商品服务、购物车服务、交易服务、支付服务5个部分,如果商品服务出问题了,不仅仅商品服务会有问题,而且调用商品服务的部分也会出问题,以购物车服务为例子,调用商品服务的请求会卡住,占用tomcat资源,随着不断接受各种请求,迟早会出现大量由购物车服务发出的对商品服务的请求被卡住,导致tomcat资源耗尽,最终购物车服务也不能使用

所以现在不仅是商品服务无法使用了,购物车服务也无法使用了,同理,调用购物车服务的其他也会逐渐无法使用。最终出现下图情况:

这个就是级联问题,又叫雪崩问题。
1.2恶意攻击
1.密码暴力破解。由于网站账号多为邮箱或手机号,攻击者极易获取这些信息。若想非法登录他人账号,攻击者会编写自动化程序,系统地尝试所有可能的密码组合,直至猜中正确密码。这种方式被称为暴力破解。
2.DDOS攻击。由于网络服务依赖有限的带宽和服务器资源,这些资源极易成为攻击目标。若想瘫痪目标网站或服务,攻击者会控制海量的“僵尸”计算机,协调它们同时向目标发起巨量的无效请求,直至耗尽其所有资源,导致合法用户无法访问。这种方式被称为分布式拒绝服务(DDoS)攻击
3.爬虫过度抓取。由于网站数据通常公开可访问,且缺乏严格的访问控制,这些数据极易被自动化程序抓取。若想大量获取甚至窃取网站的核心数据(如商品信息、用户内容等),攻击者会编写恶意爬虫程序,以远超人类操作的高频率、高并发请求访问目标网站,直至耗尽服务器带宽与计算资源,或导致关键数据被批量盗取。这种行为被称为爬虫过度抓取。
2.Sentinel
为了解决这些问题,我们需要实现限流、隔离、熔断等,如果我们自己手写会很麻烦,但是有现成的工具sentinel,可以简化操作。
2.1Sentinel安装
下载链接:Releases · alibaba/Sentinel 进入后选择一个sentinel-dashboard.jar进行下载,放到一个没有中文字符的路径,然后重命名为sentinel-dashboard.jar,在当前路径打开cmd,然后运行
java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
访问http://localhost:8090页面,就可以看到sentinel的控制台了:(账号密码默认都是sentinel)

2.2微服务中使用Sentinel
以cart-service为例:
引入依赖
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
application.yaml中添加:(这里添加了一个http-method-specify: true,因为我们的很多请求url一样,主要靠Get、Post、Put等前缀区分)
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8090
http-method-specify: true # 开启请求方式前缀
重启cart-service,然后访问查询购物车接口(这个一定要查询一遍,不然左侧不会出现cart-service这个模块),sentinel的客户端就会将服务访问的信息提交到sentinel-dashboard控制台。并展示出统计信息:

3.请求限流
请求限流就是对一个服务的某个功能的调用进行限制,限制其每秒最多调用几次,防止因突发流量(如秒杀、爬虫、热点事件)导致系统被压垮。
左侧点击簇点链路,然后选择要限流的请求,点击右侧”限流“即可,然后选择QPS模式,再填写单机阈值即可。下方的限流配置意思是每秒最多接受6个请求,再多了不接受了。


黑马微服务的资料day05中有一个Jmeter测试用的文件,拖拽进入Jmeter中即可使用

这个限流每秒请求10次,总共100秒。我们上面配置的限流是每秒6次,所以理论上应该是10次中大概成功6次。

运行结果可以再Jmeter中查看,也可以在sentinel中查看,可以看到成功率大概是60%,说明限流生效了。


4.线程隔离
每个tomcat服务器有最大的线程数量,比如说是10个线程。线程隔离就是规定对于某个资源的请求最多可以同时存在几个线程。
比如我规定A资源,最多4个线程,如果A资源出现问题,线程会一直卡在A资源的请求这里不会结束并回收,这时如果没有线程隔离,那tomcat服务器10个线程会都被A资源的请求占用,最终导致tomcat服务器不能使用。但是我现在线程隔离了,最多有4个线程会无法使用,还剩下6个可以使用。
基于这种特性,线程隔离主要用来处理上面提到的级联问题(也叫雪崩问题)
修改cart-service模块的application.yml文件,开启Feign的sentinel功能:
feign:
sentinel:
enabled: true # 开启feign对sentinel的支持
需要注意的是,默认情况下SpringBoot项目的tomcat最大线程数是200,允许的最大连接是8492,单机测试很难打满。所以我们需要配置一下cart-service模块的application.yml文件,修改tomcat连接:
server:
port: 8082
tomcat:
threads:
max: 50 # 允许的最大线程数
accept-count: 50 # 最大排队等待数量
max-connections: 100 # 允许的最大连接
然后重启cart-service服务,可以看到查询商品的FeignClient自动变成了一个簇点资源。然后就可以选择流控,然后选择”并发线程数“就可以开启线程隔离


我们利用Jemeter测试,每秒发送100个请求:

最终测试结果如下,进入查询购物车的请求每秒大概在100,而在查询商品时却只剩下每秒10左右,符合我们的预期。此时如果我们通过页面访问购物车的其它接口,例如添加购物车、修改购物车商品数量,发现不受影响

这就证明线程隔离起到了作用,尽管查询购物车这个接口并发很高,但是它能使用的线程资源被限制了,因此不会影响到其它接口。
5.服务熔断
5.1编写降级逻辑
触发限流或熔断后的请求不一定要直接报错,也可以返回一些默认数据或者友好提示,用户体验会更好。这种虽然请求失败,但是我可以返回一些默认数据、友好提示的功能,就叫”降级逻辑“
我们常用FallbackFactory实现降级逻辑
步骤1:在hm-api模块中给ItemClient定义降级处理类,实现FallbackFactory:

package com.hmall.api.client.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 lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import java.util.Collection;
import java.util.List;
@Slf4j
public class ItemClientFallback implements FallbackFactory<ItemClient> {
@Override
public ItemClient create(Throwable cause) {
return new ItemClient() {
@Override
public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
log.error("远程调用ItemClient#queryItemByIds方法出现异常,参数:{}", ids, cause);
// 查询购物车允许失败,查询失败,返回空集合
return CollUtils.emptyList();
}
@Override
public void deductStock(List<OrderDetailDTO> items) {
// 库存扣减业务需要触发事务回滚,查询失败,抛出异常
throw new BizIllegalException(cause);
}
};
}
}
步骤2:在hm-api模块中的com.hmall.api.config.DefaultFeignConfig类中将ItemClientFallback注册为一个Bean:

步骤3:在hm-api模块中的ItemClient接口中使用ItemClientFallbackFactory:

重启后,再次测试,发现被限流的请求不再报错,走了降级逻辑:

5.2服务熔断
对于商品服务的某个不太健康的接口(比如内部逻辑有问题的接口、请求非常慢的接口),我们应该停止调用,直接走降级逻辑,避免影响到当前服务。也就是将商品查询接口熔断。当商品服务接口恢复正常后,再允许调用。这其实就是断路器的工作模式了。
sentinel中的断路器不仅可以统计某个接口的慢请求比例,还可以统计异常请求比例。当这些比例超出阈值时,就会熔断该接口,即拦截访问该接口的一切请求,降级处理;当该接口恢复正常时,再放行对于该接口的请求。
断路器的工作状态切换有一个状态机来控制:

熔断配置有多种,比如下图这种:


这种是按照慢调用比例来做熔断,上述配置的含义是:
- RT超过200毫秒的请求调用就是慢调用
- 统计最近1000ms内的最少5次请求,如果慢调用比例不低于0.5,则触发熔断
- 熔断持续时长20s
配置完成后,利用Jemeter测试,可以发现:


在一开始一段时间是允许访问的,后来触发熔断后,查询商品服务的接口通过QPS直接为0,所有请求都被熔断了。而查询购物车的本身并没有受到影响。
项目下载地址
gupengzu/high-concurrency-project at 05Microservice-protection