自定义注解 封装

如果需要让接口实现限流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. Android初体验-D3

    1. UI界面布局. (即可用XML控制布局也可采用Java代码布局,不过在实际应用中是两者混合控制UI界面,为什么呢,因为XML适用于固定的不易改变的组件布局,Java程序控制常变的组件...其控制 ...

  2. Python 内置函数、作用域、闭包、递归

    一.内置函数如何使用 help()一下: 如想看min()咋用?在shell中:help(min) 二.部分内置函数 (一).排序:sorted() li = [(1, 2, 3, 4), (7, 8 ...

  3. 【BZOJ3158】千钧一发 最小割

    [BZOJ3158]千钧一发 Description Input 第一行一个正整数N. 第二行共包括N个正整数,第 个正整数表示Ai. 第三行共包括N个正整数,第 个正整数表示Bi. Output 共 ...

  4. 爬虫入门【8】Python连接MongoDB的用法简介

    MongoDB的连接和数据存取 MongoDB是一种跨平台,面向文档的NoSQL数据库,提供高性能,高可用性并且易于扩展. 包含数据库,集合,文档等几个重要概念. 我们在这里不介绍MongoDB的特点 ...

  5. 合并子目录(hash)

    题目2 : 合并子目录 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi的电脑的文件系统中一共有N个文件,例如: /hihocoder/offer22/soluti ...

  6. (四)DIH导入结构化数据

    (四)DIH导入结构化数据 目前大多数的应用程序将数据存储在关系数据库(如oracle.sql server .mysql等).xml文件中.对这样的数据进行搜索是很常见的应用.所谓的DataImpo ...

  7. 记录--Gson、json转实体类、类转json

    需要导入Gson jar包 最近在做一个java web service项目,需要用到jason,本人对java不是特别精通,于是开始搜索一些java平台的json类库. 发现了google的gson ...

  8. Python3.6全栈开发实例[002]

    2.判断用户传入的对象(字符串.列表.元组)长度是否大于5. li = [11,22,33,44,55,66,77,88,99,000,111,222] def func2(lst): if len( ...

  9. matlab学习笔记之求解线性规划问题和二次型问题

    一.线性规划问题 已知目标函数和约束条件均为线性函数,求目标函数的最小值(最优值)问题. 1.求解方式:用linprog函数求解 2.linprog函数使用形式: x=linprog(f,A,b)  ...

  10. (4.7)怎么捕获和记录SQL Server中发生的死锁?

    转自:https://blog.csdn.net/c_enhui/article/details/19498327 怎么捕获和记录SQL Server中发生的死锁? 关键词:死锁记录,死锁捕获 sql ...