如何优雅的实现接口限流?

news2024/11/20 7:19:20

首先限流,其实解决方案有很多,比如通过nginx配置,通过gateway网关进行限流,比如Spring Cloud GateWay整合熔断器实现限流

但是以上都是全局的,如何灵活的针对某些接口进行不同级别的限流呢?

方案一:令牌桶

如何需要用到这个方案需要先了解漏桶算法和令牌桶算法的区别

引入依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>18.0</version>
</dependency>

代码块

package com.sb.rateLimiter.service;
 
import com.google.common.util.concurrent.RateLimiter;
import org.springframework.stereotype.Service;
 
@Service
public class RateLimiterService {
    /**
     * 每秒只发出5个令牌
     */
    RateLimiter rateLimiter = RateLimiter.create(5.0);
 
    /**
     * 尝试获取令牌
     * @return
     */
    public boolean tryAcquire(){
        return rateLimiter.tryAcquire();
    }
}

自定义拦截器,在拦截器中实现限流

package com.sb.rateLimiter.interceptor;
import com.sb.rateLimiter.annotation.RateLimiterAnnotation;
import com.sb.rateLimiter.service.RateLimiterService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
@Component
public class RateLimiterInterceptor implements HandlerInterceptor {
    @Resource
    private RateLimiterService accessLimitService;
 
    private Logger logger = LoggerFactory.getLogger(RateLimiterInterceptor.class);
 
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
 
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        RateLimiterAnnotation rateLimiterAnnotation = handlerMethod.getMethod().getAnnotation(RateLimiterAnnotation.class);
        if(rateLimiterAnnotation == null) {
            return true;
        }
 
        if (!accessLimitService.tryAcquire()) {
            logger.info("限流中......");
            return false;
        }
        logger.info("请求成功");
        return true;
    }
 
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
 
    }
 
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
 
    }
}

实现 WebMvcConfigurer 添加自定义拦截器

package com.sb.rateLimiter.config;
import com.sb.rateLimiter.interceptor.RateLimiterInterceptor;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
 
@SpringBootConfiguration
public class WebConfiguration implements WebMvcConfigurer {
 
    @Resource
    private RateLimiterInterceptor rateLimiterInterceptor;
 
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(rateLimiterInterceptor).addPathPatterns("/**");
    }
}

自定义限流注解RateLimiterAnnotation

package com.sb.rateLimiter.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RateLimiterAnnotation {
    int rateLimiterNumber() default 1;
}

验证

package com.sb.rateLimiter.controller;
import com.sb.rateLimiter.annotation.RateLimiterAnnotation;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
@RequestMapping("/api")
public class RateLimiterController {
 
    @RateLimiterAnnotation
    @RequestMapping(value = "/rateLimiterTest", method = RequestMethod.GET)
    public void rateLimiterTest() throws Exception {
        //业务逻辑;
    }

​​​​​​在这里插入图片描述

方案二:RedissonUtils.rateLimiter

这个方案其实和上面差不多都是通过一个注解实现接口限流的,只不过相较于上面的业务处理更加完善

自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter {
	
	//限流key
	String key() default "rate_limit:";
 	//限流时间,单位秒
 	int time() default 60;
	//限流次数
	int count()default 100;
	//限流类型
	LimitType limitType() default  LimitType.DEFAULT
}

限流类型

public enum LimitType {

	// 默认策略全局限流
	DEFAULT,
	//根据请求者IP进行限流
	IP,
	//实例限流(集群多后端实例)
	CLUSTER

切面

@Befone("annotation(rateLimiter)")
public void doBefore(JoinPoint point, RateLimiter rateLimiter)
	//获取注解上的时间和计数
	int time = rateLimiter.time();
	int count = rateLimiter.count();
	//获取组合键
	String combineKey=this.getCombineKey(rateLimiter, point);
	try {
	//确定速率类型,这个枚举是redisson自带的枚举类
	RateType rateType = RateType.0VERALL;
	if(rateLimiter.LimiterType() == LimitType .CLUSTER){
		rateType  = RateType.PRE_CLIENT
	}
	// 调用RedissonUtils.rateLimiter方法进行限流处理
	Long number = RedissonUtils.rateLimiter(combineKey, rateType, count, time);
	if(number==-1){
		throw new RuntimeException("请求过于频繁~");
	};
	log.info("限制令牌 => {},剩余令牌 => {},緩存key =>'{}'"count,number,combineKey);
	}catch(Exception  e){
		throw new RnutimeException ("服务器限流异常,请稍后重试")
	}
}	
public String getCombineKey(RateLimiter rateLimiter, JoinPoint point){
	StringBuilder stringBuffer = new StringBuilder(rateLimiter.key());
	if(rateLimiter.limitType()== LimitType.IP){
		// 获取请求ip
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletRequest request =  attributes.getRequest();
		String clientIp =ServletUtil.getClientIP(request);
		stringBuffer.append(clientIP).append("-");
	}else if(rateLimiter.limitType() == LimitType.CLUSTER){
		//获取客户端实例id
		stringBuffer.append(RedissonUtils.getClient().getId()).append("-");
		MethodSignature signature =(MethodSignature)point.getSignature();
		Method method = signature.getMethod();
		Class<?>targetClass = method.getDeclaringClass();
		stringBuffer.append(targetClass.getName()).append(" ").append(method.getName());
		return stringBuffer.toString();
	}
}

RedissonUtils的限流方法


/**
 * redis 限流管理器
 * @author xxx
 * @version 1.0
 * @date xxx
 */

@Service
public class RedissonUtils{

    /**
     * 限流操作
     *
     * @param key 限流key
     * @param rateType 限流类型
     * @param rate 速率
     * @param rateInterval  速率间隔
     */
    public void rateLimiter(String key, Ratetype rateType,int ratel, int rateInterval) {
        // 创建限流器
        RRateLimiter rateLimiter = redisson.getRateLimiter(key);
        rateLimiter.trySetRate(rateType, rate,rateInterval,RateIntervalUtil.SECONDES);
        // 获取令牌
        if (rateLimiter.tryAcquire()){
            return rateLimiter.availablePermits();
        } else {
        	return  -1 ;
        }
    }
}

使用

@RestController
public class TestController {

	@RateLimiter(time=5,count =2)
	@GetMapping("/testLimit")
	public ResultV0<String> test(string name){
		return ResultV0.ok( data:"success");
	}	
}	

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1657200.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

西汉两个韩信,结局怎么如此相似

西汉军事家、“汉初三杰”韩信是家喻户晓的人物&#xff0c;同时期还有一个韩信&#xff0c;也是战功赫赫&#xff0c;也被封王&#xff0c;史书为了区别&#xff0c;在后一个韩信名字之间加上一个“王”&#xff0c;称为韩王信。韩信是个光芒万丈的人物&#xff0c;韩王信也是…

游戏专用设备指纹方案解析

如同人类拥有独一无二的指纹&#xff0c;设备也有设备的指纹&#xff0c;我们可以把设备指纹理解为设备的唯一识别码。 构建设备指纹需要采集设备硬件信息、软件信息、环境信息、网络信息等维度信息&#xff0c;进行加密/压缩&#xff0c;再通过算法处理&#xff0c;赋予设备唯…

YOLOv9中模块总结补充|SPPELAN

专栏相关代码&#xff1a;目前售价售价69.9&#xff0c;改进点80 专栏介绍&#xff1a;YOLOv9改进系列 | 包含深度学习最新创新&#xff0c;助力高效涨点&#xff01;&#xff01;&#xff01; 1. SPPELAN SPPELAN是YOLOv9作者在SPPF的基础上创新的模块&#xff08;增加了一次…

2024付费进群系统,源码及搭建变现视频课程(教程+源码)

前三节讲解搭建支付对接&#xff0c;后两节讲解一些引流变现的方法&#xff0c;还有一种变现就是帮人搭建这样的平台&#xff0c;因为全网都没有一套完整的视频教怎么搭建的&#xff0c;有也只是文字教程&#xff0c;一般新人根本看不懂&#xff0c;我视频实操演示&#xff0c;…

从“制造”到“智造”:“灯塔”经验助力中国制造业转型升级-转载

作者&#xff1a;Karel Eloot&#xff0c;侯文皓&#xff0c;Francisco Betti&#xff0c;Enno de Boer和Yves Giraud 作为中国实体经济的主体&#xff0c;制造业是推动中国经济发展乃至全球制造业持续增长的重要引擎。站在历史与未来交汇的新起点上&#xff0c;中国制造业将背…

马蹄集oj赛(双周赛第二十六次)

目录 斐波那契数列的组合 三国杀 数列分段 小码哥的跳棋游戏新编 能量供应 小码哥爱数字 最小串 小船过河 摘果子 泼墨淋漓 很重的枪 小码哥的布阵指挥 斐波那契数列的组合 #include<bits/stdc.h> using namespace std;// 斐波那契数列 1 1 2 3 5 8 13 21 34…

初始C++(二)

前言&#xff1a; C相对于C语言还有很多区别&#xff0c;接下来我们继续介绍 函数重载&#xff1a; 很好理解&#xff0c;就是Java中的函数重载。C加了函数的修饰&#xff0c;通过函数修饰规则去找。C语言是直接通过函数名查找&#xff0c;C是通过修饰后的函数名去查找。 引用…

蓝桥青少一月 STEMA-Python 测评第一题

第一题&#xff08;难度系数 2&#xff0c;18 个计分点&#xff09; (注.input()输入函数的括号中不允许添加任何信息) 编程实现&#xff1a; 给定一个正整数 N&#xff0c;输出 N 除以 3 的商。 输入描述&#xff1a;输入一个正整数 N 输出描述&#xff1a;输出 N 除以 3 的商…

Faststone Capture:高效屏幕捕获神器评测【AI写作】

首先&#xff0c;这篇文章是基于笔尖AI写作进行文章创作的&#xff0c;喜欢的宝子&#xff0c;也可以去体验下&#xff0c;解放双手&#xff0c;上班直接摸鱼~ 按照惯例&#xff0c;先介绍下这款笔尖AI写作&#xff0c;宝子也可以直接下滑跳过看正文~ 笔尖Ai写作&#xff1a;…

Cesium 展示——让加载的实体置于顶层,不被其他加载的元素所遮挡,包括地形,一键置顶

文章目录 需求分析需求 本案例中展示了加载了3DTiles后又在其表面添加了一个实体,实体被遮挡,随着视角变换,实体被展示出来,如下图所示 分析 disableDepthTestDistance:指定从相机到禁用深度测试的距离,由于深度测试的存在,我们的对象很多时候会被地形挡住,我们设置dis…

AI电视起风,三星电视打破“隔代飞跃”,在AI纪元再次领跑

作者 | 曾响铃 文 | 响铃说 要说什么是当下最热的话题&#xff0c;刚落下帷幕的北京车展一定是其中之一&#xff0c;除了各类让人眼花缭乱的新车&#xff0c;纷至沓来的各界行业大佬&#xff0c;也让车展话题度被不断拉高。在此之外&#xff0c;此次车展还刮起了一股“旋风”…

Orange3数据可视化(小提琴图)

小提琴图 小提琴图和箱线图类似&#xff0c;用来显示数据分布和概率密度。结合了箱线图和密度图的特征&#xff0c;用来显示数据的分布形状。 输入 数据: 输入数据集 输出 选中的数据: 从图中选中的实例 数据: 增加了一列&#xff0c;显示数据点是否被选中 …

DUX 主题 版本:8.2 WordPress主题优化版

主题下载地址&#xff1a;DUX 主题优化版.zip 支持夜间模式、快讯、专题、百度收录、人机验证、多级分类筛选&#xff0c;适用于垂直站点、科技博客、个人站&#xff0c;扁平化设计、简洁白色、超多功能配置、会员中心、直达链接、自动缩略图

如何从未入库的gerrit中撤销一个文件

用一个例子说明 比如有一个提交里面的default.xml的修改没有必要&#xff0c;需要从未入库的gerrit中移除 步骤如下&#xff1a; 1.做reset操作 git reset HEAD^ packages/SettingsProvider/res/values/defaults.xml 2.做checkout操作 git checkout packages/SettingsProv…

【算法系列】栈

目录 leetcode题目 一、删除字符串中的所有相邻重复项 二、比较含退格的字符串 三、基本计算器 II 四、字符串解码 五、验证栈序列 六、有效的括号 七、最小栈 八、逆波兰表达式求值 九、用栈实现队列 十、用队列实现栈 leetcode题目 一、删除字符串中的所有相邻重…

Mybatis存储数据将数据转为json

第一种方法 先创建一个表类型如下 创建一个项目&#xff0c;写一个接口 &#xff0c; 写一个JsonTypeHandler类继承BaseTypeHandler public class JsonTypeHandler<T> extends BaseTypeHandler<T> {private Class<T> clazz;//构造函数 --- >接收一个 Cl…

在数据分析中所需要运用到的概率论知识

数据分析 前言一、总体二、样本三、统计抽样抽取的基本准则 四、随机抽样抽签法随机数法 五、分层抽样六、整群抽样七、系统抽样八、统计参数常用的分布函数参数 九、样本统计量十、样本均值和样本方差十一、描述样本集中位置的统计量样本均值样本中位数样本众数 十二、描述样本…

计算机发展史故事【5】

电脑创世纪 全世界在隆隆的炮火声中迎来了1943 年。硝烟密布&#xff0c;战鼓催春&#xff0c;战争的迫切需要&#xff0c;像一只有力的巨手&#xff0c;为电脑的诞生扫清障碍&#xff0c;铺平道路。 4 月9 日&#xff0c;美国马里兰州阿贝丁&#xff0c;陆军军械部召集的一次会…

深度学习论文代码研读系列(1)Transformer:Attention Is All You Need

目录 摘要 1 引言 2背景 3模型架构 3.1编码器和解码器堆栈编码器&#xff1a; 3.2 Attention 3.2.1 Scaled Dot-Product Attention 3.2.2多头注意力 3.2.3注意力在我们的模型中的应用 3.3位置前馈网络 3.4嵌入和Softmax 3.5位置编码 4为什么自注意力 5 Training 6…

小红书释放被封手机号 无限注册

前几年抖音也可以释放被封手机号 那时候都不重视 导致现在被封手机号想释放 基本不可能的 或者就是最少几百块 有专业的人帮你通过某些信息差释放 本教程是拆解 小红书被封手机号怎么释放&#xff0c;从今年开始&#xff0c;被封的手机号无法注销了 所以很困扰 那么本教程来…