基于RateLimiter+Aop+自定义注解实现QPS限流

news2024/11/29 7:43:05

QPS秒级限流

      • 一、Aop
      • 二、自定义注解
      • 三、测试类
      • 实例

QPS简介:
QPS(Query Per Second),QPS 其实是衡量吞吐量(Throughput)的一个常用指标,就是说服务器在一秒的时间内处理了多少个请求 —— 我们通常是指 HTTP 请求,显然数字越大代表服务器的负荷越高、处理能力越强。作为参考,一个有着简单业务逻辑(包括数据库访问)的程序在单核心运行时可以提供 50 - 100 左右的 QPS,即每秒可以处理 50 - 100 个请求。

pom.xml:

		<!--   RateLimiter  -->
		<dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>30.0-android</version>
            <scope>compile</scope>
        </dependency>
        <!--   AOP   -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
        </dependency>
        <!--   访问http-API   -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpcore</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>

一、Aop

package cn.edu.shanghaitech.kysjutil.aop;

import cn.edu.shanghaitech.kysjutil.qps.QPS;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ResponseBody;

import java.lang.reflect.Method;

@Slf4j
@Aspect
@Component
public class RateLimitAop {
	// RateLimiter限流桶 默认值10
    private RateLimiter rateLimiter = RateLimiter.create(10);

    /**
     * 定义切点
     * 1、通过扫包切入
     * 2、带有指定注解切入
     */
    @Pointcut("@annotation(cn.edu.shanghaitech.kysjutil.qps.QPS)")
    public void pointcut() {
    }
	// 环绕通知
    @ResponseBody
    @Around(value = "pointcut()")
    public Object aroundNotice(ProceedingJoinPoint pjp) throws Throwable {
        log.info("拦截到了{}方法...", pjp.getSignature().getName());
        Signature signature = pjp.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        //获取目标方法
        Method targetMethod = methodSignature.getMethod();
        if (targetMethod.isAnnotationPresent(QPS.class)) {
            //获取目标方法的@QPS注解
            QPS qps = targetMethod.getAnnotation(QPS.class);
            // 判断注解设置值是否大于限流桶的默认值
            long limitNum = Long.parseLong(qps.limitNum());
            if(limitNum > Math.round(rateLimiter.getRate())){
                rateLimiter.setRate(limitNum);
            }
            if (!rateLimiter.tryAcquire()){
                log.error("访问频繁...请稍后重试!!!");
                return "访问频繁...请稍后重试!!!";
            }
        }
        log.info("执行成功!!!...做一些业务处理");
        return pjp.proceed();
    }

}

二、自定义注解

@Documented 注解
功能:指明修饰的注解,可以被例如javadoc此类的工具文档化,只负责标记,没有成员取值。

@Retention 注解
功能:指明修饰的注解的生存周期,即会保留到哪个阶段。
RetentionPolicy的取值包含以下三种:
SOURCE:源码级别保留,编译后即丢弃。
CLASS: 编译级别保留,编译后的class文件中存在,在jvm运行时丢弃,这是默认值。
RUNTIME: 运行级别保留,编译后的class文件中存在,在jvm运行时保留,可以被反射调用。

@Target 注解
功能:指明了修饰的这个注解的使用范围,即被描述的注解可以用在哪里。
ElementType的取值包含以下几种:
TYPE: 类,接口或者枚举
FIELD: 域,包含枚举常量
METHOD: 方法
PARAMETER: 参数
CONSTRUCTOR: 构造方法
LOCAL_VARIABLE: 局部变量
ANNOTATION_TYPE: 注解类型
PACKAGE: 包

package cn.edu.shanghaitech.kysjutil.qps;

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface QPS {
    /**
     * 每秒向桶中放入令牌的数量 默认最大即不做限流
     * @return
     */
    String limitNum() default "0";
    /**
     * 获取令牌的等待时间 默认0
     * @return
     */
    int timeOut() default 0;

    /**
     * 超时时间单位
     * @return
     */
    TimeUnit timeOutUnit() default TimeUnit.SECONDS;
}

三、测试类

package cn.edu.shanghaitech.kysjutil.test;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.util.concurrent.*;
public class Test {
    static int num = 0;    //访问失败次数
    static int fwnum = 0;  // 总访问次数
    int ThreadNum = 60;   //定义线程数
    //    static List<Map<String,Object>> infences = new ArrayList<>();

    //发令枪
    CountDownLatch countDownLatch = new CountDownLatch(ThreadNum);

    public void runThread() {
        //定义线程池
        ExecutorService executorService = Executors.newFixedThreadPool(ThreadNum);
        for (int i = 0; i < ThreadNum; i++) {
            executorService.submit(buildThread());
        }
    }

    public Thread buildThread() {
        //创建线程
        Thread thread = new Thread(new Runnable() {
            public void run() {
                synchronized (countDownLatch) {  //这一步不知道有没有必要,但是我还是加了
                    //发令枪减1
                    countDownLatch.countDown();
                }

                try {
                    System.out.println("线程:" + Thread.currentThread().getName() + "准备");
                    //线程阻塞
                    countDownLatch.await();
                    //这一步是调用线上的接口,发送HTTP请求    127.0.0.1可以换成你的ip地址     post是我的请求方式
                    Object appectContext = appectContext("http://localhost:8099/wuzhi/getOrder?Num=123456", "post");
                    fwnum++;       //访问的总次数
                    if (appectContext == null) {
                        num++;     //访问失败的次数
                    }
                    System.out.println("接受的值" + appectContext);
                    System.out.println("错误次数" + num);
                    System.out.println("总次数" + fwnum);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });

        return thread;
    }

    public static void main(String[] args) {
        Test h = new Test();
        h.runThread();
    }


    public Object appectContext(String url, String states) {
        // 配置请求信息(请求时间)
        RequestConfig rc = RequestConfig.custom().setSocketTimeout(5000)
                .setConnectTimeout(5000).build();
        // 获取使用DefaultHttpClient对象
        CloseableHttpClient httpclient = HttpClients.createDefault();
        // 返回结果
        String result = null;
        if (url != null) {   //请求路径不是null
            if (states.equals("post")) {   //post请求
                //System.out.println("post请求");
                HttpPost httpPost = new HttpPost(url);    //这些post请求和get请求我就不多说了   json格式的参数自己在网上搜一搜
                httpPost.setConfig(rc);
                try {
                    CloseableHttpResponse response = httpclient.execute(httpPost);
                    HttpEntity entity = response.getEntity();
                    result = EntityUtils.toString(entity, "UTF-8");
                    return result;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else if (states.equals("get")) {    //get请求
                //System.out.println("get请求");
                HttpGet httpGet = new HttpGet(url);
                httpGet.setConfig(rc);
                try {
                    CloseableHttpResponse response = httpclient.execute(httpGet);
                    return response;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else {
                System.out.println("请求失败");
            }
        } else {     //传的路径是null
            System.out.println("路径是null");
        }
        return null;
    }
}

实例

API加入@QPS注解设置limitNum次数,通过测试类进行访问调试就好了。
在这里插入图片描述
打印访问API返回结果。
在这里插入图片描述

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

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

相关文章

控制流分析之构建支配树

控制流分析之构建支配树引言1 分析有向图2 构建支配树2.1 求最小半支配点2.2 求最近支配点引言 如上一个带有起始入口点的有向图为例&#xff0c;从A到Q的必经结点有A、L、M、Q&#xff0c;我们称其为Q的支配点&#xff0c;其中M为Q的最近支配点。我们将每个结点的最近支配结点…

分享124个PHP源码,总有一款适合您

PHP源码 分享124个PHP源码&#xff0c;总有一款适合您 源码下载 链接&#xff1a;https://pan.baidu.com/s/1AIktEQ0-cPRoSSAw_eA2Lw?pwdfb9m 提取码&#xff1a;fb9m 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0…

2022 Apache APISIX 年度记忆

时光已逝&#xff0c;转眼 2022 年已然落下了帷幕。Apache APISIX 社区在众多开源爱好者和开发者的陪伴下&#xff0c;又一起走过了新的四季。 在过去的一年中&#xff0c;我们的开源项目和社区都取得了许多成就&#xff0c;并得到了来自全球范围的广泛关注和支持。2022 年&am…

数据库管理与表文件管理

数据库管理&#xff1a; 1.MySql服务器管理数据库位置&#xff1a;C:\ProgramData\MySQL\MySQL Server 5.5\data 2.查看所有的数据库名 show databases; mysql> show databases;&#xff08;查看有哪些数据库 这属于MySql的命令&#xff0c;不属于SQL语句&…

硅基仿生业务全面 Serverless 容器化,14万+问答库助力糖尿病科普

作者&#xff1a;宁佑章&#xff08;硅基仿生科技&#xff09;、元毅&#xff08;阿里云容器服务&#xff09; “使用阿里云容器服务 Knative&#xff0c;解决了开发迭代慢的问题&#xff0c;加速了深度学习模型的性能提升&#xff1b;同时提供了弹性可伸缩的资源配置&#xf…

关于Linux宝塔面板nginx配置关于php项目的跨域请求

1、网上有好多跨域请求的案例。最多的如下&#xff1a; 在项目下的配置文件中添加&#xff1a; #add_header Access-Control-Allow-Origin * always; #add_header Access-Control-Allow-Credentials true; #add_header Access-Control-Allow-Methods GET,POST,OPTIONS…

新库上线 | CnOpenData中诚信绿金ESG评级数据

中诚信绿金ESG评级数据 一、数据简介 在碳达峰、碳中和的时代浪潮下&#xff0c;以环境、社会、公司治理为核心的ESG投资理念迅速成为发现资本市场投资机遇、规避投资风险的利器。中诚信绿金在多年信用评级经验的基础上通过建立契合国内政策趋势、信息披露现状、行业发展情况…

applicationContext讲解

applicationContext讲解 生成bean 豆子&#xff0c;bean工厂&#xff0c;对象的工厂 id是对象名&#xff0c;豆子的名字&#xff0c;全项目唯一&#xff0c;class是类全路径名 bean标签的底层实现是无参构造方法 <bean id"book1" class"com.nz.entity.Book&q…

【BFS 广度优先搜索】详解感染橘子最短时间问题

一、题目描述 在给定的 m x n 网格 grid 中&#xff0c;每个单元格可以有以下三个值之一&#xff1a; 值 0 代表空单元格&#xff1b;值 1 代表新鲜橘子&#xff1b;值 2 代表腐烂的橘子。 每分钟&#xff0c;腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。 返回 直…

nginx在linux上的部署

第一步、虚拟机安装新建虚拟机&#xff08;创建的时候记得打开网络连接&#xff0c;使用nat转换的方式&#xff09;linux配置上网第一种方式&#xff1a;自动获取&#xff0c;将ipv4设置为dhcp的自动获取。但是这样每次获取的ip可能不一样。使用xshell连接的时候可能需要修改。…

第四十一讲:神州防火墙透明模式的初始配置

现在要求把防火墙接入到现有的局域网中&#xff0c;尽量不改变已有的LAN架构和IP 地址分配&#xff0c;还能对内网的流量管理进行管理&#xff0c;起到对内网的安全保护作用。防火墙接入要求&#xff0c;应该选择防火墙的工作模式为透明模式。透明模式对原有网络的介入最少&…

把PBP、前端对接、钉钉集成玩透的考试系统

文/刘俊良 周亮 年佳斌 编辑/杜逸敏 一、项目背景 客户需要开发一套企业内部的考试系统&#xff0c;用于企业内部规章制度的学习及考核。系统的主要功能包括&#xff1a; 每个员工都可以进行每日答题与专项答题。根据答题结果&#xff0c;归类不同的题库以供员工查询学习。…

进击的 AI 生成,创造性的新世界!

2022年&#xff0c;AI艺术生成文本生成图像的AI绘画生成器如雨后春笋般涌现&#xff0c;以一幅幅“不明觉厉”的AI作品进入大众视野。从2月Disco Diffusion爆火&#xff0c;仅两个月后OpenAI发布DALL-E 2&#xff0c;谷歌和Meta紧随其后宣布了各自的AI”画家“Imagen和Make-A-S…

尚医通-排班规则接口-排班详情接口-前端整合(二十五)

目录&#xff1a; &#xff08;1&#xff09;医院排班-排班规则接口 &#xff08;2&#xff09;医院排班-排班规则-前端整合 &#xff08;3&#xff09;医院排班-排班详情接口 &#xff08;4&#xff09;医院排班-排班详情前端整合 &#xff08;1&#xff09;医院排班-排班…

一文分析Linux虚拟化KVM-Qemu分析之内存虚拟化

说明&#xff1a; KVM版本&#xff1a;5.9.1QEMU版本&#xff1a;5.0.0工具&#xff1a;Source Insight 3.5&#xff0c; Visio 1. 概述 深入分析Linux虚拟化KVM-Qemu之ARMv8虚拟化文中描述过内存虚拟化大体框架&#xff0c;再来回顾一下&#xff1a; 非虚拟化下的内存的访问…

剑指 Offer 07. 重建二叉树

剑指 Offer 07. 重建二叉树 一、题目 输入某二叉树的前序遍历和中序遍历的结果&#xff0c;请构建该二叉树并返回其根节点。 假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 Input: preorder [3,9,20,15,7], inorder [9,3,15,20,7] Output: [3,9,20,null,null,1…

ansible第三天作业

1.挂载本地光盘到/mnt 2.配置yum源仓库文件通过多种方式实现 仓库1 &#xff1a; Name: RH294_Base Description&#xff1a; RH294 base software Base urt: file:///mnt/BaseOS 不需要验证钦件包 GPG 签名 启用此软件仓库 仓库 2: Name: RH294_Stream Description …

QGIS编译---QGIS3.22.4 + Qt5.15.3 + VS2019 ---64位版本

0 编译结果 先放上编译结果&#xff1a; 图1 QGIS3.22 启动界面 图2 QGIS3.22 操作界面 1 前言 因一些主观、客观原因&#xff0c;一年多没更新博客了&#xff0c;提笔继续。 这是笔者编译的第三个版本QGIS&#xff0c;本次编译原因有四&#xff1a; &#xff08;1&#xff…

05-微服务调用组件FeignDubbo实战

JAVA 项目中如何实现接口调用 1&#xff09;Httpclient HttpClient 是 Apache Jakarta Common 下的子项目&#xff0c;用来提供高效的、最新的、功能丰富的支持 Http 协议的客户端编程工具包&#xff0c;并且它支持 HTTP 协议最新版本和建议。HttpClient 相比传统 JDK 自带的UR…

Neo4j图数据库实现节点批量删除

1 前言 1-1 简介 由于对图数据库需要经常维护&#xff0c;图数据库建设初期&#xff0c;需要经常对数据写入删除等操作。 1-2 任务背景 再将1100万数据写入Neo4j后&#xff0c;由于需要对每个实体的label做精细化处理&#xff0c;之前写入的时候每个实体的label全部都为‘Comm…