java分布式应用限流实现
题记
众所周知,现今分布式限流工具有许多,对于不同应用框架的支持也不尽相同,选择适合当前项目架构的限流技术才是最合适的。
分布式
什么是分布式
分布式系统(distributed system
)是建立在网络之上的软件系统。正是因为软件的特性,所以分布式系统具有高度的内聚性和透明性。因此,网络和分布式系统之间的区别更多的在于高层软件(特别是操作系统),而不是硬件。
分布式系统优点
提高系统并发量和吞吐率,通俗点讲,就是单台机器与多台机器同时工作的区别。
分布式核心问题
并发导致的系统问题,著名的CAP
理论就是对分布式系统问题的诠释:一致性、可用性、分区容错性
数据一致性解决方案
最简单,最常见的方法就是实现分布式锁,如:采用数据库实现,采用Zookeeper
实现,采用Redis
实现等。
以上归纳的方法属于分布式并发问题的解决方案,本文主要对高并发场景的限流进行优化解决
限流
java
并发限流
Java并发工具类(四)Semaphore
Java并发工具类(三)Exchanger
Java并发工具类(二)CyclicBarrier
Java并发工具类(一)CountDownLatch
java
限流针对的是单机场景下对于接口执行的限制,可以满足指定的场景。但是用于分布式架构的系统中不合时宜。
分布式应用限流
实现分布式限流的思路有很多种
基于Redis
的限流
Redis
的setnx
的操作
固定时间窗口大小,不会有滑动窗口的效果,除非按照N:N
,即为N个单位时间对应N
个key
,动态实现计数。
Redis
的数据结构zset
public boolean limitZset(){
int currentTime = Helper.getCurrentTime();
int intervalTime = 10 * 1000;
System.err.println("开始时间:" + currentTime);
if(redisTemplate.hasKey("limit_key")) {
Integer count = redisTemplate.opsForZSet().rangeByScore("limit_key", currentTime - intervalTime, currentTime).size(); // intervalTime是限流的时间
System.out.println(count);
if (count != null && count > 5) {
return false;
}
}
redisTemplate.opsForZSet().add("limit_key",UUID.randomUUID().toString(),currentTime);
System.err.println("结束时间:" + Helper.getCurrentTime());
return true;
}
Redis
的令牌桶算法
public boolean limitLeakyBuckets (){
Object result = redisTemplate.opsForList().leftPop("limit_leaky_buckets");
if(result == null){
return false;
}
return true;
}
@Scheduled(fixedDelay = 10000,initialDelay = 0)
public void setToken(){
redisTemplate.opsForList().rightPush("limit_leaky_buckets",UUID.randomUUID().toString());
}
}
基于Hystrix
的限流
Netflix
公司在分布式微服务架构的践行下,将其保护服务的稳定性而设计的客户端熔断和断路器的解决方案,称之为Hystrix
。
Future
限流实现
public class FutureDemo {
private static final Random random = new Random();
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<String> future = executorService.submit(()->{
int nextInt = random.nextInt(200);
System.out.printf("Thread : %s,睡眠时间: %s ms \n", Thread.currentThread().getName(),nextInt);
try {
Thread.sleep(nextInt);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
executorService.shutdown();
}
return "hello world";
});
try {
future.get(100, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
System.out.println("服务熔断保护");
e.printStackTrace();
}
}
}
Hystrix
实现
@RestController
public class HelloWorldController {
private final Random random = new Random();
@GetMapping("/hello")
@HystrixCommand(fallbackMethod = "errorOfHello",
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "100")
}
)
public String hello() throws InterruptedException {
int nextInt = random.nextInt(200);
System.out.printf("Thread : %s,睡眠时间: %s ms \n", Thread.currentThread().getName(), nextInt);
Thread.sleep(nextInt);
return "hello world";
}
@GetMapping("/hello2")
public String hello2() throws InterruptedException {
return new HystrixCommendDemo().execute();
}
public String errorOfHello() {
return "error of hello world";
}
}
基于Sentinel
的限流
Sentinel
是阿里中间件团队开源的,面向分布式服务架构的轻量级高可用流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助用户保护服务的稳定性。
public static void main(String[] args) {
initFlowRules(); //初始化规则
while(true){
Entry entry=null;
try{
entry= SphU.entry(resource); //准入判断
System.out.println("Hello Word");
}catch (BlockException e){//异常处理
e.printStackTrace();
}finally {
if(entry!=null){
entry.exit();// 资源释放
}
}
}
}
Sentinel
对比 Hystrix
对比内容 | Sentinel | Hystrix |
---|---|---|
隔离策略 | 信号量隔离 | 线程池隔离/信号量隔离 |
熔断降级策略 | 基于响应时间或失败比率 | 基于失败比率 |
实时指标实现 | 滑动窗口 | 滑动窗口(基于 RxJava) |
规则配置 | 支持多种数据源 | 支持多种数据源 |
扩展性 | 多个扩展点 | 插件的形式 |
基于注解的支持 | 支持 | 支持 |
限流 | 基于 QPS,支持基于调用关系的限流 | 不支持 |
流量整形 | 支持慢启动、匀速器模式 | 不支持 |
系统负载保护 | 支持 | 不支持 |
控制台 | 开箱即用,可配置规则、查看秒级监控、机器发现等 | 不完善 |
常见框架的适配 | Servlet、Spring Cloud、Dubbo、gRPC 等 | Servlet、Spring Cloud Netflix |