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

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注