接口限流方案
由于业务应用系统的负载能力有限,为了防止非预期的请求对系统压力过大而拖垮业务应用系统,必须采取流量控制措施。
服务接口的流量控制策略:分流、降级、限流 熔断
分流:扩容机器、单元化通道
降级:关闭非核心接口,保证核心接口链路的正常运行
限流:业务系统限流、数据库限流
常见的限流算法有:令牌桶、漏桶、Redis计数器。
下面用图说话吧 令牌桶算法
谷歌的guava库已经帮我们实现好了 有兴趣的朋友可以去看看源码
@Component
public class GuavaLimitSimpalDemo {
private static final RateLimiter rateLimit = RateLimiter.create(10);
public boolean tryAcquire() {
return rateLimit.tryAcquire();
}
}
其实上面这段代码就可以作为一个简单的限流服务使用了。
在单进程服务中 我们就可以简单使用这个来实现某个接口的限流了
分布式系统中 可以借助redis来实现 不过稍微复杂 考虑的东西也很多。
那么我们可以自己写一个针对某个接口实现总访问次数的限制吗 不使用guava提供的包
当然可以 jdk中有个Semaphore类可以实现 同时限制线程数量访问。
还有种简单的基于某个接口基于总数量的次数访问限制 我们可以使用简单的思路 不一定要用算法来解决 下面给出一个例子(这种再装饰下完全可以用于OA,ERP这些企业内部系统的),复杂,对数据要求精准,要求并发等这个做不到哦 ,也不合适哦,当然,这也是基于单进程的。
直接上代码:
/**
* 定时扫描servletContext里的limitcount 可以对其重置 修改等
*/
@Configuration
@EnableScheduling
public class TimerService implements ServletContextAware{
private ServletContext servletContext;
//这里可以设置每日0:00 点进行次数重置 下面这只是演示
@Scheduled(initialDelay = 1000,fixedRate = 2000)
public void timerToNow() {
int count = (int) servletContext.getAttribute("limit");
servletContext.setAttribute("limit","自定义总次数");
System.out.println(" count limit:" + count);
System.out.println("now time:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext=servletContext;
}
}
@Component
@Slf4j
public class LimitInit implements ServletContextInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
log.info("limitcount:" + count);
servletContext.setAttribute("limit", count);
}
@Value("${limitCount}")
public int count;
}
@RestController
@RequestMapping("/demo")
public class TestController {
private TimerService timerService;
@GetMapping("/test")
public String demo1(HttpServletRequest request) {
synchronized (this) {
System.out.println(" do somethng ");
ServletContext servletContext = request.getServletContext();
int limit = (int) servletContext.getAttribute("limit");
if (limit <= 0) {
throw new RuntimeException("次数限制");
}
servletContext.setAttribute("limit", limit-1);
int newlimit = (int) servletContext.getAttribute("limit");
System.out.println(" newlimit:" + newlimit);
}
return "test limit success ";
}
}
是不是我们也可以简单的实现呢
我不建议这样写哈 为了与业务代码分开 降低系统的耦合性
可以使用写在拦截器里 或者使用aop实现
通过上面三个类 我们就可以使用测试啦
上面提到了用redis实现分布式环境下的限流
限流嘛 无非是从次数 和速度上限制
redis 里有个数据结构hash集 可以把令牌放里面 设置过期时间嘛,扯的话有点多。。。不细说了。
对了 nginx也可以限流哦 也推荐使用。

浙公网安备 33010602011771号