网站首页 > java教程 正文
在分布式系统和高并发场景中,限流是保证系统稳定性和高可用性的关键技术之一。本期咱们一起探讨Sentinel和Spring Cloud Gateway的限流实现差异,并通过代码示例帮助你更好地理解和应用这些技术。
三大经典限流算法Java实现
1. 滑动时间窗口算法实现
public class SlidingWindow {
private final long windowSizeInMs;
private final int maxRequests;
private final LinkedList<Long> requests = new LinkedList<>();
public SlidingWindow(long windowSizeInMs, int maxRequests) {
this.windowSizeInMs = windowSizeInMs;
this.maxRequests = maxRequests;
}
public synchronized boolean allowRequest() {
long currentTime = System.currentTimeMillis();
// 移除过期请求
while (!requests.isEmpty() &&
requests.getFirst() < currentTime - windowSizeInMs) {
requests.removeFirst();
}
if (requests.size() < maxRequests) {
requests.addLast(currentTime);
return true;
}
return false;
}
}
// 使用示例
public class SlidingWindowExample {
public static void main(String[] args) {
// 限制1秒内最多10个请求
SlidingWindow window = new SlidingWindow(1000, 10);
for (int i = 0; i < 15; i++) {
if (window.allowRequest()) {
System.out.println("请求 " + (i + 1) + ": 允许");
} else {
System.out.println("请求 " + (i + 1) + ": 被限流");
}
try { Thread.sleep(100); } catch (InterruptedException e) {}
}
}
}
2. 令牌桶算法实现
public class TokenBucket {
private final int capacity;
private final double refillRate; // 令牌/毫秒
private double tokens;
private long lastRefillTime;
public TokenBucket(int capacity, int refillTokensPerSecond) {
this.capacity = capacity;
this.refillRate = refillTokensPerSecond / 1000.0;
this.tokens = capacity;
this.lastRefillTime = System.currentTimeMillis();
}
public synchronized boolean allowRequest() {
refillTokens();
if (tokens >= 1) {
tokens -= 1;
return true;
}
return false;
}
private void refillTokens() {
long currentTime = System.currentTimeMillis();
double timePassed = currentTime - lastRefillTime;
tokens = Math.min(capacity, tokens + timePassed * refillRate);
lastRefillTime = currentTime;
}
}
// 使用示例
public class TokenBucketExample {
public static void main(String[] args) {
// 容量10个令牌,每秒补充5个
TokenBucket bucket = new TokenBucket(10, 5);
for (int i = 0; i < 20; i++) {
if (bucket.allowRequest()) {
System.out.println("请求 " + (i + 1) + ": 允许");
} else {
System.out.println("请求 " + (i + 1) + ": 被限流");
}
try { Thread.sleep(100); } catch (InterruptedException e) {}
}
}
}
Sentinel 限流实战
3. Sentinel 基础使用示例
// 添加Maven依赖
// <dependency>
// <groupId>com.alibaba.csp</groupId>
// <artifactId>sentinel-core</artifactId>
// <version>1.8.6</version>
// </dependency>
public class SentinelExample {
// 定义资源
private static final String RESOURCE_NAME = "orderApi";
static {
// 配置流控规则:QPS不超过10
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource(RESOURCE_NAME);
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(10);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
public void createOrder() {
Entry entry = null;
try {
entry = SphU.entry(RESOURCE_NAME);
// 执行业务逻辑
System.out.println("创建订单成功");
} catch (BlockException e) {
// 被限流
System.out.println("创建订单被限流");
} finally {
if (entry != null) {
entry.exit();
}
}
}
public static void main(String[] args) throws InterruptedException {
SentinelExample example = new SentinelExample();
// 模拟高并发请求
for (int i = 0; i < 100; i++) {
new Thread(() -> {
example.createOrder();
}).start();
Thread.sleep(10);
}
}
}
4. Sentinel 热点参数限流
public class SentinelParamFlowExample {
private static final String RESOURCE_NAME = "queryUserInfo";
static {
// 热点参数限流规则
ParamFlowRule rule = new ParamFlowRule(RESOURCE_NAME)
.setParamIdx(0) // 第一个参数为热点参数
.setGrade(RuleConstant.FLOW_GRADE_QPS)
.setCount(5); // 每个参数值每秒最多5次
ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
}
public String queryUserInfo(Long userId) {
Entry entry = null;
try {
// 使用热点参数
entry = SphU.entry(RESOURCE_NAME, EntryType.IN, 1, userId);
return "用户信息: " + userId;
} catch (BlockException e) {
return "请求过于频繁,请稍后重试";
} finally {
if (entry != null) {
entry.exit();
}
}
}
}
Spring Cloud Gateway 限流实战
5. Gateway + Redis 限流配置
# application.yml
spring:
redis:
host: localhost
port: 6379
cloud:
gateway:
routes:
- id: user-service
uri: http://localhost:8080
predicates:
- Path=/user/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10 # 每秒令牌数
redis-rate-limiter.burstCapacity: 20 # 令牌桶容量
key-resolver: "#{@userKeyResolver}"
6. Gateway 限流Java配置
@Configuration
public class RateLimitConfig {
@Bean
public KeyResolver userKeyResolver() {
return exchange -> {
// 根据用户ID限流
String userId = exchange.getRequest()
.getQueryParams()
.getFirst("userId");
return Mono.just(Objects.requireNonNullElse(userId, "anonymous"));
};
}
@Bean
public RedisRateLimiter redisRateLimiter() {
return new RedisRateLimiter(10, 20);
}
}
// 自定义限流处理器
@Component
public class CustomRateLimiter implements RateLimiter {
@Override
public Mono<Response> isAllowed(String routeId, String id) {
// 自定义限流逻辑
return Mono.just(new Response(true, -1));
}
public static class Response {
private final boolean allowed;
private final long tokensRemaining;
public Response(boolean allowed, long tokensRemaining) {
this.allowed = allowed;
this.tokensRemaining = tokensRemaining;
}
}
}
性能测试对比
7. 限流算法性能测试
public class RateLimiterBenchmark {
public static void main(String[] args) {
testSlidingWindow();
testTokenBucket();
testSentinel();
}
private static void testSlidingWindow() {
SlidingWindow limiter = new SlidingWindow(1000, 1000);
long start = System.currentTimeMillis();
int count = 0;
for (int i = 0; i < 1000000; i++) {
if (limiter.allowRequest()) {
count++;
}
}
long time = System.currentTimeMillis() - start;
System.out.println("滑动窗口: " + time + "ms, 通过请求: " + count);
}
private static void testTokenBucket() {
TokenBucket limiter = new TokenBucket(1000, 1000);
long start = System.currentTimeMillis();
int count = 0;
for (int i = 0; i < 1000000; i++) {
if (limiter.allowRequest()) {
count++;
}
}
long time = System.currentTimeMillis() - start;
System.out.println("令牌桶: " + time + "ms, 通过请求: " + count);
}
private static void testSentinel() {
// 初始化Sentinel规则
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("test");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(1000);
rules.add(rule);
FlowRuleManager.loadRules(rules);
long start = System.currentTimeMillis();
int count = 0;
for (int i = 0; i < 1000000; i++) {
Entry entry = null;
try {
entry = SphU.entry("test");
count++;
} catch (BlockException e) {
// 被限流
} finally {
if (entry != null) {
entry.exit();
}
}
}
long time = System.currentTimeMillis() - start;
System.out.println("Sentinel: " + time + "ms, 通过请求: " + count);
}
}
实际应用建议
- 网关层限流:使用Gateway的令牌桶算法,适合处理突发流量
- 服务层限流:使用Sentinel的滑动窗口,实现更精细的控制
- 热点数据保护:使用Sentinel的热点参数限流,保护关键资源
通过以上代码示例,咱们可以看到不同限流算法的具体实现方式和应用场景。在实际项目中:
- 滑动时间窗口适合需要精确控制QPS的场景
- 令牌桶算法适合允许突发流量的场景
- 漏桶算法适合需要平滑输出流量的场景
Sentinel和Gateway分别针对不同的使用场景提供了优秀的限流解决方案。小编认为理解它们的底层原理和实现方式,能够帮助咱在实际项目中做出更合理的技术选型。
猜你喜欢
- 2025-09-19 常见的几种简单限流算法_常见的几种简单限流算法有哪些
- 2025-09-19 常用限流算法与Guava RateLimiter源码解析
- 2025-09-19 如何实现一个简单可行的限流方法,保障电商系统的稳定性
- 2025-09-19 常见的4种限流算法与实现_限流方法原理
- 2025-09-19 API能力开放平台设计思路剖析_开放api技术
- 2025-09-19 Sentinel 是什么?它是如何实现限流的?
- 2025-09-19 在Spring Boot集成Bucket4j实现接口限流操作
- 2025-09-19 Spring Boot3 中实现单节点限流操作全解析
- 2025-09-19 一文搞懂!7种常见的限流方式!_限流的几种方式
- 2025-09-19 Java API Gateway动态限流,流量控制还能这么玩?一线实战全解析!
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)