当前位置:网站首页>AOP implementation enterprise API access interface monitoring (via Google Guava cache data)
AOP implementation enterprise API access interface monitoring (via Google Guava cache data)
2022-07-29 15:03:00 【A programmer who loves sports】
Developed enterprise function modules,分享给大家参考,If you see the shortcomings of my implementation or have your own opinions, please share them in the comment area٩꒰▽ ꒱۶⁼³₌₃ go learn
文章目录
前言
需求:
1、Records the number of times the interface access succeeds and fails
2、Record the time spent on the success and failure of interface access
3、Access timestamp records,以5分钟为一个节点,例如:13:14The points are recorded as13:10分
4、公开一个接口,返回Google Guava缓存的数据,以JSON的格式
5、It is still available under high concurrency,保证线程安全
The above are the specific needs,At present, this requirement only stores specific interface information,In the future, monitoring and early warning may be implemented:The number of interface failures in the current node exceeds the threshold、If the time taken to call the interface exceeds the threshold, an alert needs to be sent(邮箱通知等等)
一、AOP的基本知识
1、什么是AOP?
AOP(Aspect-Oriented Programming,面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性.
2、有哪些AOP的概念?
Spring AOP是基于动态代理的,如果要代理的对象实现了某个接口,那么Spring AOP就会使用JDK动态代理去创建代理对象;而对于没有实现接口的对象,就无法使用JDK动态代理,转而使用CGlib动态代理生成一个被代理对象的子类来作为代理.
3、AOPcontains several concepts
1)、Jointpoint(连接点):Concrete facets of abstract concepts,Can be in the field、方法上,SpringThe specific form of expression isPointCut(切入点),Only works on methods.
2)、Advice(通知): The specific operation performed at the connection point,How to enhance processing,分为前置、后置、异常、最终、Surrounding five situations.
3)、目标对象:被AOP框架进行增强处理的对象,Also known as an enhanced object.
4)、AOP代理:AOP框架创建的对象,简单的说,代理就是对目标对象的加强.Spring中的AOP代理可以是JDK动态代理,也可以是CGLIB代理.
5)、Weaving(织入):将增强处理添加到目标对象中,The process of creating an enhanced object
总结为一句话就是:在目标对象(target object)的某些方法(jointpoint)Add different kinds of actions(通知、Enhanced handling),Finally by some means(weaving、织入操作)Implements a new proxy target object.
4、AOP 有哪些应用场景?
举几个例子:
- 记录日志(Log after the method is called)
- 监控性能(统计方法运行时间)
- 权限控制(Check for permission before calling the method)
- 事务管理(调用方法前开启事务,调用方法后提交关闭事务 )
- 缓存优化(第一次调用查询数据库,将查询结果放入内存对象, 第二次调用,直接从内存对象返回,不需要查询数据库 )
5、有哪些AOP Advice通知的类型?
特定 JoinPoint 处的 Aspect 所采取的动作称为 Advice.Spring AOP 使用一个 Advice 作为拦截器,在 JoinPoint “周围”维护一系列的拦截器.
- 前置通知(Before advice) : 这些类型的 Advice 在 joinpoint 方法之前执行,并使用 @Before 注解标记进行配置.
- 后置通知(After advice) :这些类型的 Advice 在连接点方法之后执行,无论方法退出是正常还是异常返回,并使用 @After 注解标记进行配置.
- 返回后通知(After return advice) :这些类型的 Advice 在连接点方法正常执行后执行,并使用@AfterReturning 注解标记进行配置.
- 环绕通知(Around advice) :这些类型的 Advice 在连接点之前和之后执行,并使用 @Around 注解标记进行配置.
- 抛出异常后通知(After throwing advice) :仅在 joinpoint 方法通过抛出异常退出并使用 @AfterThrowing 注解标记配置时执行.
二、Google Guava是什么?
This is a tool class,很好用,Boss let me study hard,Because he also used this to cache when he was working in Ali before.
Google Guava 官网: https://guava.dev/
Google Guava 中文教程: https://wizardforcel.gitbooks.io/guava-tutorial/content/
Just look at the documentation,in conjunction with the Internetdemo,Basic use is fine.
三、功能实现
1.引入依赖
<!-- AOP -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20140107</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2、Write a return content entity
MonitoringVO.java:
/**
* @author 一个爱运动的程序员
*/
@Data
public class MonitoringVO {
/**
* 请求时间
*/
private String time;
/**
* 接口请求的URL
*/
private String apiURL;
/**
* The number of times the interface request was successful
*/
private Long successfulNum;
/**
* The number of times the interface request failed
*/
private Long failuresNum;
/**
* Cumulative time spent on successful interface requests
*/
private Long successfulTime;
/**
* Cumulative time spent for interface request failures
*/
private Long failuresTime;
}
3、编写AOP
ApiVisitHistory.java:
/**
* APIAccess historical statistics
*
* @author 一个爱运动的程序员
*/
@Component
@Aspect
public class ApiVisitHistory {
private Logger log = LoggerFactory.getLogger(ApiVisitHistory.class);
ThreadLocal<Long> startTime = new ThreadLocal<>();
/**
* 定义切面
* - 此处代表com.example.demo.monitoring.controllerAll interfaces under the package will be counted
*/
@Pointcut("execution(* com.example.demo.monitoring.controller..*.*(..))")
public void pointCut() {
}
/**
* 在接口原有的方法执行前,将会首先执行此处的代码
*/
@Before("pointCut()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
startTime.set(System.currentTimeMillis());
//获取传入目标方法的参数
log.info("类名:{}", joinPoint.getSignature().getDeclaringType().getSimpleName());
log.info("方法名:{}", joinPoint.getSignature().getName());
}
/**
* 只有正常返回才会执行此方法
* 如果程序执行失败,则不执行此方法
*/
@Async
@AfterReturning(returning = "returnVal", pointcut = "pointCut()")
public void doAfterReturning(JoinPoint joinPoint, Object returnVal) throws ExecutionException {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String key = AtomicCounter.getTimeStamp() + "--" + AtomicCounter.getApiURL(request);
// 成功计数+1
AtomicCounter.increaseSucceed(AtomicCounter.getCountObject(key));
// 耗时计算
Long succeedTime = System.currentTimeMillis() - startTime.get();
log.info("接口访问成功,URI:[{}], 耗费时间:[{}] ms", request.getRequestURI(), succeedTime);
// 存储APIInterface access information
AtomicCounter.setSuccessfulTime(key, succeedTime);
}
/**
* 当接口报错时执行此方法
*/
@AfterThrowing(pointcut = "pointCut()")
public void doAfterThrowing(JoinPoint joinPoint) throws ExecutionException {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String key = AtomicCounter.getTimeStamp() + "--" + AtomicCounter.getApiURL(request);
// 失败计数+1
AtomicCounter.increaseFail(AtomicCounter.getCountObject(key));
// 耗时计算
Long failTime = System.currentTimeMillis() - startTime.get();
log.info("接口访问失败,URI:[{}], 耗费时间:[{}] ms", request.getRequestURI(), failTime);
AtomicCounter.setMultiMap(key, failTime);
}
}
4、Write storage utility classes
AtomicCounter java:
/**
* 记录API接口访问成功/Failed count and time
*
* @author 一个爱运动的程序员
*/
public class AtomicCounter {
/**
* Guava Cache 缓存API访问记录
*/
private static Cache<String, ConcurrentHashMap<AtomicCounter, MonitoringVO>> cache = CacheBuilder.newBuilder().expireAfterWrite(60 * 10, TimeUnit.SECONDS).build();
/**
* Called count object
*
* @param key
* @return
*/
public static ConcurrentHashMap<AtomicCounter, MonitoringVO> getCountObject(String key) {
ConcurrentHashMap<AtomicCounter, MonitoringVO> ifPresent = cache.getIfPresent(key);
if (ifPresent == null || ifPresent.isEmpty()) {
ifPresent = new ConcurrentHashMap<>();
AtomicCounter atomicCounter = new AtomicCounter();
MonitoringVO monitoringVO = new MonitoringVO();
String[] split = key.split("--");
monitoringVO.setTime(split[0]);
monitoringVO.setApiURL(split[1]);
monitoringVO.setSuccessfulNum(0L);
monitoringVO.setFailuresNum(0L);
monitoringVO.setSuccessfulTime(0L);
monitoringVO.setFailuresTime(0L);
ifPresent.put(atomicCounter, monitoringVO);
cache.put(key, ifPresent);
}
return ifPresent;
}
/**
* Increase the number of times the access interface is called successfully
* @param concurrentHashMap
* @return
*/
public static void increaseSucceed(ConcurrentHashMap<AtomicCounter, MonitoringVO> concurrentHashMap) {
MonitoringVO monitoringVO = null;
for (MonitoringVO m : concurrentHashMap.values()) monitoringVO = m;
Lock lock = new ReentrantLock();
try {
lock.lock();
monitoringVO.setSuccessfulNum(monitoringVO.getSuccessfulNum() + 1);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* The elapsed time for a successful update call
* @param key
* @param successTime
* @throws ExecutionException
*/
public static void setSuccessfulTime(String key, Long successTime) {
ConcurrentHashMap<AtomicCounter, MonitoringVO> map = getCountObject(key);
MonitoringVO monitoringVO = null;
for (MonitoringVO m : map.values()) monitoringVO = m;
Lock lock = new ReentrantLock();
try {
lock.lock();
monitoringVO.setSuccessfulTime(monitoringVO.getSuccessfulTime() + successTime);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* Increase the number of times the access interface invocation fails
* @param concurrentHashMap
* @return
*/
public static void increaseFail(ConcurrentHashMap<AtomicCounter, MonitoringVO> concurrentHashMap) {
MonitoringVO monitoringVO = null;
for (MonitoringVO m : concurrentHashMap.values()) monitoringVO = m;
Lock lock = new ReentrantLock();
try {
lock.lock();
monitoringVO.setFailuresNum(monitoringVO.getFailuresNum() + 1);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
*The elapsed time for the update call to fail
* @param key
* @param failTime
*/
public static void setMultiMap(String key, Long failTime) {
ConcurrentHashMap<AtomicCounter, MonitoringVO> map = getCountObject(key);
MonitoringVO monitoringVO = null;
for (MonitoringVO m : map.values()) monitoringVO = m;
Lock lock = new ReentrantLock();
try {
lock.lock();
monitoringVO.setFailuresTime(monitoringVO.getFailuresTime() + failTime);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* 获取时间戳,并以5Minutes are nodes
* @return
*/
public static String getTimeStamp() {
// 获取时间戳
Long timeStamp = System.currentTimeMillis();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH");
SimpleDateFormat sdfMinute = new SimpleDateFormat("mm");
// 时间戳转换成时间
String sd = sdf.format(new Date(Long.parseLong(String.valueOf(timeStamp))));
// 时间戳转换成时间
String sdMinute = sdfMinute.format(new Date(Long.parseLong(String.valueOf(timeStamp))));
sd += ":" + (Integer.valueOf(sdMinute) - Integer.valueOf(sdMinute) % 5);
return sd;
}
/**
* 获取API的接口URL
* @param request
* @return
*/
public static String getApiURL(HttpServletRequest request) {
// 请求路径
String requestURI = request.getRequestURI();
return requestURI;
}
/**
* 获取API监控信息
*
* @return
*/
public static CopyOnWriteArrayList<MonitoringVO> getCacheMap() {
ConcurrentMap<String, ConcurrentHashMap<AtomicCounter, MonitoringVO>> stringConcurrentHashMapConcurrentMap = cache.asMap();
CopyOnWriteArrayList<MonitoringVO> list = new CopyOnWriteArrayList<>();
for (ConcurrentHashMap<AtomicCounter, MonitoringVO> c : stringConcurrentHashMapConcurrentMap.values())
for (MonitoringVO m : c.values()) list.add(m);
return list;
}
}
5、编写测试类
HelloController.java:
@RestController
@RequestMapping("/aop")
public class HelloController {
static int r = 0;
@GetMapping("success")
public String success() {
return "调用成功";
}
@GetMapping("fail")
public String fail() {
if (r == 3) {
r++;
return "调用错误接口,成功一次";
} else {
r++;
int i = 1 / 0;
return "The test is wrongAOP方法";
}
}
@GetMapping("cache")
public String get() {
CopyOnWriteArrayList<MonitoringVO> list = AtomicCounter.getCacheMap();
return JSONObject.valueToString(list);
}
}
项目目录:
运行效果:
总结
以上就是今天要分享的内容,在线程安全方面,Google Guava CacheCaches are inherently thread-safe,Therefore, it is necessary to pay attention to the thread safety of the operation process,本可以使用AtomicIntegerA thread-safe integer data type,But because the storage is too cumbersome and difficult to handle,Finally used in processing operationslock来保证线程安全.今天的分享就到这里,给个三连呗,创作不易(づ。◕ᴗᴗ◕。)づ
附加功能
需求:Can be customized to enhance the interface,例如:Provide monitoring functions to the interface in the form of annotations
代码如下:
MonitoringAnnotation .java:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MonitoringAnnotation {
}
ApiVisitHistory.java:
@Pointcut("@annotation(com.example.demo.monitoring.annotation.MonitoringAnnotation)")
修改路径即可:
Just add this annotation to the interface method or class we need to monitor:
@MonitoringAnnotation
源码地址
https://github.com/XIN007-C/api-monitoring/tree/main/aopdemo
如果对大家有帮助,给个start吧 ψ(*`ー´)ψ
边栏推荐
- 即时通讯-改变社交与工作状态的新型软件
- 兆骑科创海外高层次人才引进平台,企业项目对接,赛事活动路演
- AQS源码阅读与强软弱虚4种引用以及ThreadLocal原理与源码
- 请写出用Graphics2D类的新方法画一个图形的步骤(表格如何画斜线)
- WOLFLAB一方老师带你解读虚拟云网络《VMware NSX-T卷2》-1
- EA&UML日拱一卒-活动图::Variable Actions(续)
- Shared memory - shmget filling holes
- The reason for Apple's official price reduction has been found, and it is also facing declining sales and even inventory problems
- Nacos基础教程
- 【IIC通信】Chap.2 (I2C)IIC协议的特点;为什么IIC需要开漏输出、上拉电阻?
猜你喜欢

AOP实现企业级API访问接口监控(通过Google Guava缓存数据)

ArcGIS Pro与ArcGis区别

国产手机将用户变成它们的广告肉鸡,难怪消费者都买iPhone了

Introduction to several methods of making custom welcome interface on Weiluntong touch screen

这 6 款在线 PDF 转换工具,得试

正斜杠 “/” 与反斜杠 “\”辨析

打卡广汽本田喜悦安全驾驶中心,体验最刁钻的场地训练

Offensive EA&UML day arch - activity diagram: : Variable Actions (continue)

Linux installation of MySQL (super detailed)

Why does APP use the JSON protocol to interact with the server: serialization related knowledge
随机推荐
面对互联网的裁员潮,我们该何去何从?
兆骑科创海外高层次人才引进平台,企业项目对接,赛事活动路演
暴力递归到动态规划 02 (绝顶聪明的人的纸牌游戏)
xss内容总结
Numpy
Programmers are a group with a high incidence of occupational diseases. Don’t be naive to think that it’s just as simple as being bald.
深陷盈利困境,“寒冬”中也要二次递表,北森上市迫切
深开鸿:万物智联的大江上,升起一轮开源鸿蒙月
正则、grep/egrep、sed、awk
QT通过UDP分包传输大图像(测试可传6M)
C语言 4:汇编语言指令介绍
使用Xshell和Xftp7跑学校服务器记录
字典树笔记(自用)
极市直播丨严彬-Unicorn:走向目标跟踪的大一统(ECCV2022 Oral)
一篇适合新手的深度学习综述!
立足本土,链接全球 | 施耐德电气“工业SI同盟”携手伙伴共赴未来工业
部门例会上做测试分享,不知道分享什么内容?
数字孪生万物可视 |联接现实世界与数字空间
广州消防:高温天气火灾频发 消防安全不容忽视
你会看 MySQL 的执行计划(EXPLAIN)吗?