1.高并发带来的问题
在微服务架构 中,我们将业务拆分成⼀个个的服务 ,服务与服务之间可以相互调⽤,但是由于**⽹络原因或者⾃身的原因**,服务并不能保证服务的100%可⽤,如果单个服务出现问题,调⽤这个服务就会出现**⽹络延迟** ,此时若有大量的网络涌入 ,会形成任务堆积 ,最终导致服务瘫痪。
2.模拟场景
接下来,我们来模拟⼀个⾼并发的场景。
创建⼀个服务提供者项目,在 controller 书写两个⽅法供消费者调⽤,⼀个⽅法被延迟,另⼀个方法正常。
2.1 新建一个cloud.sentinel.provider项目
java
package com.jiazhong.mingxing.project.cloud.sentinel.provider.controller;
import com.jiazhong.mingxing.cloud.commons.util.JsonResult;
import com.jiazhong.mingxing.cloud.commons.util.ResultTool;
import lombok.SneakyThrows;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/sentinel_provider")
public class SentinelProviderController {
@SneakyThrows
@GetMapping("/a")
public JsonResult a() {
Thread.sleep(3000);
return ResultTool.success("This is SentinelProviderController`a method");
}
@GetMapping("/b")
public JsonResult b() {
return ResultTool.success("This is SentinelProviderController`b method");
}
}
启动项目
2.2新建cloud.sentinel.consumer项目
写一个SentinelProviderFeign接口
java
package com.jiazhong.mingxing.project.cloud.sentinel.provider.feign;
import com.jiazhong.mingxing.cloud.commons.util.JsonResult;
import com.jiazhong.mingxing.project.cloud.sentinel.provider.feign.impl.SentinelProviderFeignImpl;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(value = "cloud-sentinel-provider", path = "sentinel_provider", fallback = SentinelProviderFeignImpl.class)
public interface SentinelProviderFeign {
@GetMapping("/a")
JsonResult a();
@GetMapping("/b")
JsonResult b();
}
写service以及实现类
java
package com.jiazhong.mingxing.project.cloud.sentinel.provider.service;
import com.jiazhong.mingxing.cloud.commons.util.JsonResult;
import org.springframework.stereotype.Service;
@Service
public interface SentinelConsumerService {
JsonResult a();
JsonResult b();
}
java
package com.jiazhong.mingxing.project.cloud.sentinel.provider.feign.impl;
import com.jiazhong.mingxing.cloud.commons.util.JsonResult;
import com.jiazhong.mingxing.cloud.commons.util.ResultTool;
import com.jiazhong.mingxing.project.cloud.sentinel.provider.feign.SentinelProviderFeign;
import org.springframework.stereotype.Component;
@Component
public class SentinelProviderFeignImpl implements SentinelProviderFeign {
@Override
public JsonResult a() {
return sentinelProviderFeign.a();
}
@Override
public JsonResult b() {
return sentinelProviderFeign.b();
}
}
新建controller类
java
package com.jiazhong.mingxing.project.cloud.sentinel.provider.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.jiazhong.mingxing.cloud.commons.util.JsonResult;
import com.jiazhong.mingxing.cloud.commons.util.ResultTool;
import com.jiazhong.mingxing.project.cloud.sentinel.provider.service.SentinelConsumerService;
import lombok.SneakyThrows;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/sentinel_consumer")
public class CloudSentinelConsumerController {
private SentinelConsumerService sentinelConsumerService;
public CloudSentinelConsumerController(SentinelConsumerService sentinelConsumerService) {
this.sentinelConsumerService = sentinelConsumerService;
}
@GetMapping("/a")
public JsonResult a() {
return sentinelConsumerService.a();
}
@GetMapping("/b")
public JsonResult b() {
return sentinelConsumerService.b();
}
}
修改yml文件
java
server:
port: 8022
jetty:
threads:
max: 10
spring:
application:
name: cloud-sentinel-consumer
#这里是微服务的内容
#注册
cloud:
nacos:
discovery:
namespace: 5dde225c-61e0-4015-8a2f-5eabe8407195 # 你的命名空间
server-addr: 192.168.xx.xx:8848 # 自己的地址
#ephemeral: false
sentinel:
transport:
port: 8719 #跟控制台交流的端⼝,随意指定⼀个未使⽤的端⼝即可
dashboard: localhost:8888 # 指定控制台服务的地址
web-context-unify: false
feign:
sentinel:
enabled: true
启动该项目
2.3.下载压测工具JMeter
下载地址:https://jmeter.apache.org/
解压,启动jmeter.sh






2000个请求,访问a,a占用资源,来一个正常的b,没有资源处理,响应时间会增加。

最后,观察运行结果。
此时会发现 , 由于 a ⽅法囤积了⼤量请求,导致 b ⽅法的访问出现了问题,这就是服务雪崩的雏形。
3.服务雪崩效应
在分布式系统中,由于⽹络原因或⾃身的原因,服务⼀般⽆法保证100% 可⽤。如果⼀个服务出现了问题,调⽤这 个服务就会出现线程阻塞的情况,此时若有⼤量的请求涌⼊,就会出现多条线程阻塞等待,进⽽导致服务瘫痪。 由 于服务于服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的" 雪崩 " 效应。
雪崩发生过的原因多种多样,有不合理的容量设计,或者是⾼并发下某⼀个⽅法响应变慢,亦或是某台机器的资源耗 尽。我们⽆法完全杜绝雪崩源头的发⽣, 只有做好⾜够的容错,保证在⼀个服务发⽣问题,不会影响到其他服务的 正常运⾏。也就是 " 雪落⽽不雪崩 " 。
4.常见容错方案
要防⽌雪崩的扩散,我们就要做好服务的容错,容错说⽩了就是保护⾃⼰不被猪队友拖垮的⼀些措施,下⾯介绍常见的服务容错思路和组件。 常⻅的容错思路有隔离、超时、限流、熔断、降级这几种,下⾯分别介绍⼀下。
4.1隔离
它是指将系统按照⼀定的原则划分为若⼲服务模块,各个模块之间相对独⽴,⽆强影响。当有故障发⽣时,能将问 题和影响隔离在某个模块内部, ⽽不扩散⻛险,不波及其他模块,不影响整体的系统服务。
常⻅的隔离⽅式有:
- 线程池隔离。
- 信号量隔离。

4.2超时
在上游( A 服务)服务调⽤下游( B 服务)服务的时候,设置⼀个最⼤响应时间,如果超过这个时间,下游未作出反 应,就断开请求,释放掉线程。
4.3限流
限流就是限制系统的输入和输出流量已达到保护系统 的目的,为了保护系统的稳固巡行,一旦达到的需要限制的阈值,就需要限制流量并采取少量措施以完成限制流量的目的。
4.4熔断
在互联⽹系统中,当下游( B 服务)服务因访问压⼒过⼤⽽响应变慢或失败,上游( A 服务)服务为了保护系统整体的可⽤性,可以暂时切断对下游服务的调⽤。这种牺牲局部,保全整体的措施就叫做熔断。

服务熔断⼀般有三种状态:
- 熔断关闭状态(Closed) B服务没有故障时,熔断器所处的状态,对调⽤⽅(A服务)的调⽤不做任何限制。
- 熔断开启状态(Open) B服务有故障时,后续对该服务接⼝的调⽤不再经过⽹络(不在对B 服务进行访问),直接执⾏本地的fallback的⽅法。
- 半熔断状态(Half-Open) 尝试恢复服务调⽤,允许有限的流量调⽤该服务,并监控调⽤成功率。如果成功率达到预期,则说明服务已恢复,进⼊熔断关闭状态; 如果成功率仍旧很低,则重新进⼊熔断开启状态。
4.5降级
降级其实就是为服务提供⼀个托底(备选)⽅案,⼀旦服务⽆法正常调⽤,就使⽤托底(备选)方案。

我们使用容错组件Sentinel
5.Sentinel入门
5.1什么是Sentinel
Sentinel 是 Alibaba 开源的分布式系统流量控制、熔断降级、系统负载保护工具 ,核心目标是 保障微服务架构的稳定性,避免因流量突增、服务故障、资源耗尽等问题导致整体系统雪崩。
它与 Spring Cloud 生态深度集成(如 Spring Cloud Alibaba Sentinel),可无缝对接 Nacos、Gateway、Feign 等组件,是微服务架构中 "稳定性防护" 的核心组件之一。
基本概念
- 资源:是 Sentinel 的关键概念。它可以是 Java 应⽤程序中的任何内容,可以是⼀个服务,也可以是⼀个⽅法,甚⾄可以是⼀段代码。就是 Sentinel 需要保护的东⻄。 我们⼊⻔案例中的 customer/b ⽅法就可以认为是⼀个资源。
- 规则:作⽤在资源之上,定义以什么样的⽅式保护资源,主要包括流量控制规则、熔断降级规则以及系统保护规则。规则就是⽤来定义如何进⾏保护资源的。 我们⼊⻔案例就是为 customer/b 资源设置了⼀种流控规则,限制了进⼊ customer/b 的流量。
5.2Sentinel的组成部分
- 核心库( Java 客户端)不依赖任何框架|库,能够运⾏于所有 Java 运⾏环境,同时对 Dubbo/Spring Cloud 等框架也有较好的⽀持。
- 控制台( Dashboard )基于 Spring Boot 开发,打包后可以直接运⾏,不需要额外的 Tomcat 等应⽤容器。
5.3微服务集成Sentinel
微服务集成 Sentinel ⾮常简单 , 只需要加⼊ Sentinel 的依赖即可。
5.3.1导入依赖
XML
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency
5.3.2安装控制台
Sentinel 提供⼀个轻量级的控制台,它提供机器发现、单机资源实时监控以及规则管理等功能。 下载 jar 包,存放到任意⽂件夹。 https://github.com/alibaba/Sentinel/releases(下载1.8.9)
5.3.3启动控制台
通过 cmd 命令可以启动控制台。
XML
## 其中8888是端⼝号,1.8.5是下载的版本号。都是可以修改的。
java -Dserver.port=8888 -Dcsp.sentinel.dashboard.server=localhost:8888 -
Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.9.jar
在浏览器中输入地址:localhost:8888 即可出现下图画面

用户以及密码都是sentinel

5.3.4 修改"消费者项目",在⾥边加⼊有关控制台的配置
XML
spring:
cloud:
sentinel:
transport:
port: 8719 #跟控制台交流的端⼝,随意指定⼀个未使⽤的端⼝即可
dashboard: localhost:8888 # 指定控制台服务的地址
启动生产者和消费者项目,多访问几次,就会在Sentinel中看到

设置每秒最多访问2次


若1秒内多于2次,出现下图

6.案例
在controller类中写下述方法
java
@GetMapping("/c")
public JsonResult c() {
return ResultTool.success("This is SentinelConsumerController`c method!");
}
这样可以只启动消费者
6.1流控(线程数)


启动Jmeter

有3/7的几率失败
6.2让步
在controller类下写入d方法
java
@GetMapping("/d")
public JsonResult d() {
return ResultTool.success("This is SentinelConsumerController`d method!");
}

如果d的资源每秒访问数超过4次就会限流访问c

c不可以访问,d可以访问
6.3链路
链路:只记录指定链路上的流量(指定资源从⼊⼝资源进来的流量,如果达到阈值,就进⾏限流 ) , 可看作 api 级别的针对来源,链路的粒度更细,需要如下步骤才能实现
6.3.1修改yml文件
添加下述代码
java
web-context-unify: false
6.3.2在service中写方法
java
@SentinelResource("messageE")
public JsonResult e() {
return ResultTool.success("This is SentinelConsumerServiceImpl`e method!");
}
6.3.3写两个controller方法e1 e2
java
@GetMapping("/e1")
public JsonResult e1() {
return sentinelConsumerService.e();
}
@GetMapping("/e2")
public JsonResult e2() {
return sentinelConsumerService.e();
}

对e1限流,e2不限流
