自定义注解 封装

如果需要让接口实现限流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. Travel(最短路)

    Travel The country frog lives in has nn towns which are conveniently numbered by 1,2,…,n1,2,…,n. Amo ...

  2. eclipse远程debug Java程序

    使用Eclipse JPDA远程调试Java程序 本文将介绍使用Eclipse JPDA,在Eclipse的开发环境下对远程运行的Java程序进行调试操作. 请按以下步骤进行(本人已经在Eclipse ...

  3. Introspection in Python How to spy on your Python objects Guide to Python introspection

    Guide to Python introspection https://www.ibm.com/developerworks/library/l-pyint/ Guide to Python in ...

  4. 2014-08-28——移动端,触摸事件 touchstart、touchmove、touchend、touchcancel

    1.Touch事件简介在移动终端上的web页面触屏时会产生ontouchstart.ontouchmove.ontouchend.ontouchcancel 事件,分别对应了触屏开始.拖拽及完成触屏事 ...

  5. 如何使用 stl 进行排列组合?

    #include <iostream> #include <vector> #include <algorithm> //从 indexs 集合中选择 num 个元 ...

  6. Vue中watch的简单应用

    Vue.js 有一个方法 watch,它可以用来监测Vue实例上的数据变动. 如果对应一个对象,键是观察表达式,值是对应回调,值也可以是方法名,或者是对象,包含选项. 下面写两个demo,参考demo ...

  7. random生成六位随机数验证码

    需求: 生成一个六位随机验证码 # 傻瓜式纯数字实现import randomran = random.randint(100000, 999999) # ran = random.randrange ...

  8. Python如何实现单例模式?其他23种设计模式python如何实现?

    #使用__metaclass__(元类)的高级python用法 class Singleton2(type): def __init__(cls, name, bases, dict): super( ...

  9. matlab学习笔记之基础知识(一)

    一.两种特殊数据类型 1.元胞数组   元胞数组是MATLAB的一种特殊数据类型,可以将元胞数组看做一种无所不包的通用矩阵,或者叫做广义矩阵.组成元胞数组的元素可以是任何一种数据类型的常数或者常量,每 ...

  10. C# 函数2

    //读写INI     public class GF_INI     {         [DllImport("kernel32")]         private stat ...