好的,我们开始今天的讲座,主题是“JAVA 服务间调用限流:结合 Sentinel + Nacos 实现动态限流规则”。
在微服务架构中,服务间的调用非常频繁,如果不加以限制,高并发的请求可能会导致服务雪崩,影响整个系统的稳定性。限流就是一种有效的保护机制,它可以限制服务的请求速率,防止过载。今天我们将深入探讨如何使用 Sentinel 结合 Nacos 来实现动态的限流规则。
1. 为什么需要限流?
在深入技术细节之前,我们先来明确一下为什么需要限流。想象一下,你正在举办一个免费的演唱会,如果没有任何入场限制,所有人都想挤进场地,最终的结果很可能是踩踏事故。在微服务架构中,如果某个服务突然面临大量的请求,它可能会因为资源耗尽而崩溃,进而影响依赖它的其他服务,最终导致整个系统瘫痪。
限流就像演唱会的入场券,它控制了进入系统的请求数量,确保服务在可承受的范围内运行。它可以防止恶意攻击、应对突发流量,保证服务的可用性和稳定性。
2. Sentinel 简介
Sentinel 是阿里巴巴开源的一款流量控制、熔断降级框架。它以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来保障服务的稳定性。
Sentinel 的核心概念包括:
- 资源 (Resource): Sentinel 保护的目标,可以是任何东西,例如一个服务、一个方法、甚至是一段代码。
- 规则 (Rule): 定义如何保护资源。Sentinel 提供了多种规则类型,包括流量控制规则、熔断降级规则、系统保护规则等。
- Slot Chain: Sentinel 的核心处理链路,负责执行各种规则。
3. Nacos 简介
Nacos 是阿里巴巴开源的配置中心和服务注册发现组件。它可以帮助我们管理服务的配置信息、服务注册信息等,并提供动态配置更新的能力。
在限流场景中,我们可以使用 Nacos 来存储 Sentinel 的限流规则,并实现动态更新。当我们需要调整限流策略时,只需要修改 Nacos 中的配置,Sentinel 就会自动加载新的规则,无需重启服务。
4. Sentinel + Nacos 实现动态限流的步骤
下面我们将详细介绍如何使用 Sentinel 结合 Nacos 来实现动态限流。
4.1. 项目准备
我们需要一个简单的 Spring Boot 项目,包含两个服务:服务 A 和 服务 B。服务 A 调用服务 B。
- 服务 A (Consumer): 发起调用的服务,模拟用户请求。
- 服务 B (Provider): 提供服务的服务,需要进行限流保护。
首先,创建两个 Spring Boot 项目,分别命名为 service-a 和 service-b。
4.2. 添加依赖
在 service-b (Provider) 的 pom.xml 文件中添加以下依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
同样,在 service-a (Consumer) 的 pom.xml 文件中添加以下依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
说明:
spring-cloud-starter-alibaba-sentinel: Sentinel 的 Spring Cloud Starter,用于集成 Sentinel。spring-cloud-starter-alibaba-nacos-config: Nacos 配置中心的 Spring Cloud Starter,用于从 Nacos 加载配置。spring-cloud-starter-alibaba-nacos-discovery: Nacos 服务发现的 Spring Cloud Starter,用于服务注册与发现。spring-boot-starter-web: Spring Web 的 Starter,用于构建 RESTful API。spring-cloud-starter-openfeign: Feign Client 的 Starter,用于服务间的调用 (服务A)。
4.3. 配置 Nacos
启动 Nacos 服务。你可以在 Nacos 官网下载并安装 Nacos,或者使用 Docker 启动 Nacos。
在 service-b 和 service-a 的 application.properties 或 application.yml 文件中添加 Nacos 配置:
# application.yml (service-b)
spring:
application:
name: service-b
cloud:
nacos:
config:
server-addr: localhost:8848 # Nacos 服务器地址
file-extension: yaml
discovery:
server-addr: localhost:8848
server:
port: 8082 # 服务B的端口
# application.yml (service-a)
spring:
application:
name: service-a
cloud:
nacos:
config:
server-addr: localhost:8848 # Nacos 服务器地址
file-extension: yaml
discovery:
server-addr: localhost:8848
server:
port: 8081 # 服务A的端口
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
4.4. 配置 Sentinel
在 Nacos 中创建名为 service-b.sentinel.flow 的 Data ID,Group 默认为 DEFAULT_GROUP,内容为 Sentinel 的流量控制规则。 文件类型选择 YAML。
# service-b.sentinel.flow
- resource: /hello # 受保护的资源
limitApp: default # 限制的应用,default 表示不区分调用来源
grade: 1 # 限流的维度,1 表示 QPS
count: 10 # QPS 的阈值,每秒最多允许 10 个请求
controlBehavior: 0 # 流控效果,0 表示直接拒绝
说明:
resource: 需要进行限流的资源名称,这里我们设置为/hello。limitApp: 限制的应用,default表示不区分调用来源,对所有请求都进行限流。grade: 限流的维度,1表示按照 QPS (每秒请求数) 进行限流。count: 限流的阈值,这里设置为10,表示每秒最多允许 10 个请求通过。controlBehavior: 流控效果,0表示直接拒绝超出阈值的请求。
4.5. Provider (service-b) 代码实现
在 service-b 中,创建一个 Controller,并添加 Sentinel 的注解:
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
@SentinelResource("hello") // 定义资源名称
public String hello() {
return "Hello from service-b!";
}
}
说明:
@SentinelResource("hello"): 使用 Sentinel 的@SentinelResource注解来定义一个资源,resource属性指定资源名称,与 Nacos 中配置的resource保持一致。blockHandler: 可选属性,指定当请求被限流时执行的方法。fallback: 可选属性,指定当方法抛出异常时执行的方法。
4.6. Consumer (service-a) 代码实现
在 service-a 中,我们需要使用 Feign Client 来调用 service-b。
首先,创建一个 Feign Client 接口:
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "service-b") // 指定调用的服务名称
public interface ServiceBClient {
@GetMapping("/hello") // 指定调用的 API 路径
String hello();
}
然后,在 service-a 的主类上添加 @EnableFeignClients 注解,启用 Feign Client:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class ServiceAApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceAApplication.class, args);
}
}
最后,创建一个 Controller,使用 Feign Client 调用 service-b:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
private ServiceBClient serviceBClient;
@GetMapping("/call-hello")
public String callHello() {
return serviceBClient.hello();
}
}
4.7. 启动服务并测试
依次启动 Nacos、service-b 和 service-a。
访问 service-a 的 /call-hello 接口,例如:http://localhost:8081/call-hello。
在短时间内快速刷新页面,或者使用工具(如 ab 命令)模拟高并发请求。你会发现,当请求超过 service-b 配置的 QPS 阈值 (10) 时,Sentinel 会拒绝超出阈值的请求,service-a 会收到错误信息。
4.8. 动态修改限流规则
修改 Nacos 中 service-b.sentinel.flow 的内容,例如将 count 修改为 20:
# service-b.sentinel.flow
- resource: /hello # 受保护的资源
limitApp: default # 限制的应用,default 表示不区分调用来源
grade: 1 # 限流的维度,1 表示 QPS
count: 20 # QPS 的阈值,每秒最多允许 20 个请求
controlBehavior: 0 # 流控效果,0 表示直接拒绝
稍等片刻,Sentinel 会自动加载新的规则。再次访问 service-a 的 /call-hello 接口,你会发现 service-b 的 QPS 阈值已经变为 20。
5. 更多配置和高级用法
- 流控效果 (controlBehavior):
0: 快速失败,直接拒绝超出阈值的请求。1: 匀速排队,让请求以均匀的速度通过,适用于应对突发流量。2: 预热,在一段时间内逐渐增加请求的通过速率,防止系统被瞬间的流量冲击。
- 熔断降级规则: 当服务出现故障时,自动熔断,防止故障蔓延。
- 系统保护规则: 从系统负载的角度进行保护,例如限制 CPU 使用率、Load、RT 等。
- 自定义 BlockException 处理: 通过实现
BlockExceptionHandler接口,可以自定义当请求被限流或降级时的处理逻辑。 - 热点参数限流: 针对热点参数进行限流,例如限制某个用户的访问频率。
6. 代码示例
下面是一个更完整的 service-b 的代码示例,包含了自定义 BlockException 处理:
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
@SentinelResource(value = "hello", blockHandler = "handleHelloBlock")
public String hello() {
return "Hello from service-b!";
}
// 处理 BlockException 的方法
public String handleHelloBlock(BlockException ex) {
return "Service is busy, please try again later.";
}
}
在这个示例中,我们使用 blockHandler 属性指定了当请求被限流时执行的方法 handleHelloBlock。
7. 总结
我们学习了如何使用 Sentinel 结合 Nacos 实现动态限流。通过 Nacos,我们可以动态地修改限流规则,而无需重启服务。Sentinel 提供了多种限流策略和高级功能,可以满足各种复杂的限流需求。希望这次讲座能帮助你更好地理解和使用 Sentinel,保护你的微服务系统。
服务保护,Sentinel + Nacos 实现流量控制。
灵活配置,动态调整限流规则。
确保稳定,防止服务雪崩。