自定义注解 封装

如果需要让接口实现限流RateLimiter使用

网关:一般拦截所有的接口 实现限流 秒杀 抢购 或者大流量的接口才会实现限流。灵活

不是所有接口都需要限流  秒杀等接口需要限流

设计: 加注解的才可以实现限流

注解形式而不是网关形式 只有需要限流的才加这个注解

传统的方式整合RateLimiter有很大缺点:代码重复量特别大,而且本身不支持注解方式

限流代码可以写在网关,相当于针对所有接口实现限流,维护性不强

不是所有的接口都需要限流 一般限流主要针对大流量,比如秒杀抢购

分析案例:

定义一个自定义注解

Spring Boot整合 spring aop

使用环绕通知

判断请求方法上是否有 注解

如果有  使用反射获取注解方法上的参数

调用原生RateLImiter方法创建令牌

如果获取令牌超时  直接调用服务降级(自己定义)

如果能够获取令牌 直接进入实际请求方法

本案例没有用到 扫包   直接请求过来走方法的

首先自定义注解:

引入maven依赖:

<!-- springboot 整合AOP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>pom 

pom:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.toov5</groupId>
<artifactId>springboot-guava</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>25.1-jre</version>
</dependency>
<!-- springboot 整合AOP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
</project>

  

封装注解:

package com.toov5.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExtRateLimiter {
//以秒为单位 固定的速录往桶中添加
double permitsPerSecond(); //在规定的时间内,如果没有获取到令牌的话,直接走降级处理
long timeout();
}

aop封装:

package com.toov5.aop;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.aspectj.lang.ProceedingJoinPoint;
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.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import com.google.common.util.concurrent.RateLimiter;
import com.toov5.annotation.ExtRateLimiter; //aop环绕通知 判断拦截所有springmvc请求,判断请求方法上是否存在ExtRateLimiter @Aspect //aop两种方式 注解 和 xml方式
@Component
public class RateLimiterAop {
// 存放接口是否已经存在
private static ConcurrentHashMap<String, RateLimiter> rateLimiterMap = new ConcurrentHashMap<String, RateLimiter>(); //定义切入点 拦截
@Pointcut("execution(public * com.toov5.controller.*.*(..))") //所有类 所有方法 任意参数
public void rlAop() {
} //使用aop环绕通知判断拦截所有springmvc请求,判断方法上是否存在ExRanteLimiter
@Around("rlAop()")
public Object doBefore(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//1、判断请求方法上是否存在@RxtRateLimiter注解
//2、如果请求方法上存在此注解@RxtRateLimiter注解 比如加上了RequestMapping表示请求方法
Method sinatureMethod = getSinatureMethod(proceedingJoinPoint);
if (sinatureMethod == null) {
//直接报错
return null;
} //3、使用Java的反射机制获取拦截方法上自定义注解的参数
ExtRateLimiter extRateLimiter = sinatureMethod.getDeclaredAnnotation(ExtRateLimiter.class);
if (extRateLimiter==null) { //方法上没有注解
//直接放行代码 进入实际请求方法中
proceedingJoinPoint.proceed();
}
//4、调用原生RateLimiter创建令牌
double permitsPerSecond = extRateLimiter.permitsPerSecond(); //获取参数
long timeout = extRateLimiter.timeout();
//调用原生的RateLimiter创建令牌 保证每个请求对应的是单例的RateLimiter 一个请求一个RateLimiter 使用hashMap
RateLimiter.create(permitsPerSecond);
String requestURI = getRequestUrl();
RateLimiter rateLimiter = null;
if (rateLimiterMap.containsKey(requestURI)) {
//如果检测到
rateLimiter = rateLimiterMap.get(requestURI);
}else {
//如果没有 则添加
rateLimiter = RateLimiter.create(permitsPerSecond);
rateLimiterMap.put(requestURI, rateLimiter);
} //5、获取桶中的令牌,如果没有有效期获取到令牌,直接调用降级方法。
boolean tryAcquire = rateLimiter.tryAcquire(timeout,TimeUnit.MILLISECONDS);
if (!tryAcquire) {
//服务降级
fallback();
return null;
}
//6、否则 直接进入到实际请求方法中 return proceedingJoinPoint.proceed();
}
private void fallback() throws IOException {
//在aop编程中获取响应
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletResponse response = attributes.getResponse();
//防止乱码
response.setHeader("Content-type", "text/html;charset=UTF-8");
PrintWriter writer = response.getWriter();
try {
writer.println("亲,别抢了");
} catch (Exception e) {
writer.close();
} }
private String getRequestUrl() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
return request.getRequestURI();
}
private Method getSinatureMethod(ProceedingJoinPoint proceedingJoinPoint) {
//获取到目标代理对象
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
//获取当前aop拦截的方法
Method method = signature.getMethod();
return method;
}
}

controller

package com.toov5.controller;

import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import com.google.common.util.concurrent.RateLimiter;
import com.toov5.annotation.ExtRateLimiter;
import com.toov5.service.OrderService; @RestController
public class IndexController {
@Autowired
private OrderService orderService;
//create方法中传入一个参数 以秒为单位固定的速率值 1r/s 往桶中存入一个令牌
RateLimiter rateLimiter = RateLimiter.create(1); //独立线程!它自己是个线程 //相当于接口每秒只能接受一个客户端请求
@RequestMapping("/addOrder")
public String addOrder() {
//限流放在网关 获取到当前 客户端从桶中获取对应的令牌 结果表示从桶中拿到令牌等待时间
//如果获取不到令牌 就一直等待
double acquire = rateLimiter.acquire();
System.out.println("从桶中获取令牌等待时间"+acquire);
boolean tryAcquire=rateLimiter.tryAcquire(500,TimeUnit.MILLISECONDS); //如果在500sms没有获取到令牌 直接走降级
if (!tryAcquire) {
System.out.println("别抢了,等等吧!");
return "别抢了,等等吧!";
}
//业务逻辑处理
boolean addOrderResult = orderService.addOrder();
if (addOrderResult) {
System.out.println("恭喜抢购成功!等待时间");
return "恭喜抢购成功!";
} return "抢购失败!";
}
//以每秒1个的速度往桶中添加令牌
@RequestMapping("/findIndex")
@ExtRateLimiter(permitsPerSecond=1.0,timeout=500)
public void findIndex() {
System.out.println("findIndex"+System.currentTimeMillis());
} }

service

package com.toov5.service;

import org.springframework.stereotype.Service;

@Service
public class OrderService { public boolean addOrder() {
System.out.println("db...正在操作订单表数据库");
return true;
}
}

启动类

package com.toov5;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args); }
}

疯狂点击:

手写RateLimiter的更多相关文章

  1. 【Win 10 应用开发】手写识别

    记得前面(忘了是哪天写的,反正是前些天,请用力点击这里观看)老周讲了一个14393新增的控件,可以很轻松地结合InkCanvas来完成涂鸦.其实,InkCanvas除了涂鸦外,另一个大用途是墨迹识别, ...

  2. JS / Egret 单笔手写识别、手势识别

    UnistrokeRecognizer 单笔手写识别.手势识别 UnistrokeRecognizer : https://github.com/RichLiu1023/UnistrokeRecogn ...

  3. 如何用卷积神经网络CNN识别手写数字集?

    前几天用CNN识别手写数字集,后来看到kaggle上有一个比赛是识别手写数字集的,已经进行了一年多了,目前有1179个有效提交,最高的是100%,我做了一下,用keras做的,一开始用最简单的MLP, ...

  4. 【转】机器学习教程 十四-利用tensorflow做手写数字识别

    模式识别领域应用机器学习的场景非常多,手写识别就是其中一种,最简单的数字识别是一个多类分类问题,我们借这个多类分类问题来介绍一下google最新开源的tensorflow框架,后面深度学习的内容都会基 ...

  5. caffe_手写数字识别Lenet模型理解

    这两天看了Lenet的模型理解,很简单的手写数字CNN网络,90年代美国用它来识别钞票,准确率还是很高的,所以它也是一个很经典的模型.而且学习这个模型也有助于我们理解更大的网络比如Imagenet等等 ...

  6. 使用神经网络来识别手写数字【译】(三)- 用Python代码实现

    实现我们分类数字的网络 好,让我们使用随机梯度下降和 MNIST训练数据来写一个程序来学习怎样识别手写数字. 我们用Python (2.7) 来实现.只有 74 行代码!我们需要的第一个东西是 MNI ...

  7. 手写原生ajax

    关于手写原生ajax重要不重要,各位道友自己揣摩吧, 本着学习才能进步,分享大家共同受益,自己也在自己博客里写一下 function createXMLHTTPRequest() { //1.创建XM ...

  8. springmvc 动态代理 JDK实现与模拟JDK纯手写实现。

    首先明白 动态代理和静态代理的区别: 静态代理:①持有被代理类的引用  ② 代理类一开始就被加载到内存中了(非常重要) 动态代理:JDK中的动态代理中的代理类是动态生成的.并且生成的动态代理类为$Pr ...

  9. 为sproto手写了一个python parser

    这是sproto系列文章的第三篇,可以参考前面的<为sproto添加python绑定>.<为python-sproto添加map支持>. sproto是云风设计的序列化协议,用 ...

随机推荐

  1. MySQL的limit子句

    1.理解: limit用来取结果集中的固定几条记录 2.参数: limit offset,pagesize offset:偏移量,为0时,可以省略 pagesize:每页显示的行数,通常是固定的 0表 ...

  2. 如何修改vs2005/vs2010的tfs的登录名和密码 .

    如何修改vs2005/vs2010的tfs的登录名和密码 . 连接TFS时,如果本机保存了用户的网络密码,不会出现用户名和密码的输入框,若要更换TFS的用户名和密码,需按以下步骤操作: 控制面板--- ...

  3. EasyDSS流媒体服务器软件(支持RTMP/HLS/HTTP-FLV/视频点播/视频直播)-正式环境安装部署攻略

    EasyDSS流媒体服务器软件,提供一站式的转码.点播.直播.时移回放服务,极大地简化了开发和集成的工作. 其中,点播功能主要包含:上传.转码.分发.直播功能,主要包含:直播.录像, 直播支持RTMP ...

  4. UML初览(转)

    原文:UML初览 本章使用一个简单的例子对UML中所使用的概念和视图进行初览.本章的目的是要将高层UML概念组织成一系列较小的视图和图表来可视化说明这些概念,说明如何用各种不同的概念来描述一个系统以及 ...

  5. Json模块的详细介绍(序列化)

    什么叫序列化——将原本的字典.列表等内容转换成一个字符串的过程就叫做序列化. 比如,我们在python代码中计算的一个数据需要给另外一段程序使用,那我们怎么给? 现在我们能想到的方法就是存在文件里,然 ...

  6. 从1到N中1的个数

    示例1,2...9,10,11中有四个1 int getNumber(int n) { int count = 0; int factor = 1; int low = 0; int cur = 0; ...

  7. 剑指offer 面试46题

    面试46题: 题目:把数字翻译成字符串 题:给定一个数字,我们按照如下规则把它翻译为字符串:0翻译成“a”,1翻译成“b”,……,11翻译成“1”,……,25翻译成“z”.一个数字可能有多个翻译.例如 ...

  8. 调用settings.py的配置信息作为全局使用

    项目中一些比较零散的信息可以保存在数据库,也可以保存在settings.py里面   并且这些变量也可以像引用数据里面的数据使用,     可以把信息保存在settings.py里面,也可以保存在数据 ...

  9. Python基础(17)_面向对象程序设计(抽象类、继承原理、封装、多态,绑定方法)

    一.抽象类 抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化 1.在python中实现抽象类 import abc #利用abc模块实现抽象类 class All_file(metacl ...

  10. Python3 进程 线程 同步锁 线程死锁和递归锁

    进程是最小的资源单位,线程是最小的执行单位 一.进程 进程:就是一个程序在一个数据集上的一次动态执行过程. 进程由三部分组成: 1.程序:我们编写的程序用来描述进程要完成哪些功能以及如何完成 2.数据 ...