日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > javascript >内容正文

javascript

aop统计请求数量_Spring-Boot+AOP+统计单次请求方法的执行次数和耗时情况-Go语言中文社区...

發(fā)布時(shí)間:2024/7/23 javascript 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 aop统计请求数量_Spring-Boot+AOP+统计单次请求方法的执行次数和耗时情况-Go语言中文社区... 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本篇結(jié)合aop(面向切面編程)的特性,對(duì)spring-boot項(xiàng)目下后端開(kāi)發(fā)人員所關(guān)心的java代碼的性能做了一次簡(jiǎn)單的統(tǒng)計(jì),比如,前端發(fā)了一個(gè)post請(qǐng)求(一連串?dāng)?shù)據(jù)的保存),到了后端,首先是指定Controller的某個(gè)方法做接收,本篇稱(chēng)之為接入點(diǎn)方法;其次就是以接入點(diǎn)方法為線(xiàn)索,繼續(xù)向下執(zhí)行剩余模塊(包)里面的方法,而這些方法就是我們所關(guān)心的數(shù)據(jù)業(yè)務(wù)的實(shí)現(xiàn)部分。

如果利用aop技術(shù)對(duì)這些方法進(jìn)行相應(yīng)的切點(diǎn)操作的話(huà),我們會(huì)清晰的看到,每個(gè)方法所在的包+類(lèi)名,每個(gè)方法的名稱(chēng),每個(gè)方法的參數(shù)類(lèi)型以及每個(gè)方法的執(zhí)行耗時(shí)情況等等,就如單元測(cè)試驗(yàn)證項(xiàng)目的可行性一樣,我們拿到了這些信息后,就可以利用給出的信息去定位優(yōu)化,雖然起決定性因素的還是整個(gè)項(xiàng)目的架構(gòu)和數(shù)據(jù)庫(kù)層面上的優(yōu)化,顯然本篇還是小試牛刀,就當(dāng)做是自我?jiàn)蕵?lè)吧。

一、Spring-Boot 添加 aop依賴(lài)

org.springframework.boot

spring-boot-starter-aop

二、Spring-Boot項(xiàng)目樹(shù)

(1)

(2)添加Controller級(jí)別的攔截器(Class)

ControllerInterceptor.java

package com.appleyk.interceptor;

import java.util.HashMap;

import java.util.Map;

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.AfterReturning;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

import org.aspectj.lang.annotation.Pointcut;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Component;

/**

* 攔截器

*/

@Aspect

@Component

public class ControllerInterceptor {

static Logger logger = LoggerFactory.getLogger(ControllerInterceptor.class);

//ThreadLocal 維護(hù)變量 避免同步

//ThreadLocal為每個(gè)使用該變量的線(xiàn)程提供獨(dú)立的變量副本,所以每一個(gè)線(xiàn)程都可以獨(dú)立地改變自己的副本,而不會(huì)影響其它線(xiàn)程所對(duì)應(yīng)的副本。

ThreadLocal startTime = new ThreadLocal<>();// 開(kāi)始時(shí)間

/**

* map1存放方法被調(diào)用的次數(shù)O

*/

ThreadLocal> map1 = new ThreadLocal<>();

/**

* map2存放方法總耗時(shí)

*/

ThreadLocal> map2 = new ThreadLocal<>();

/**

* 定義一個(gè)切入點(diǎn). 解釋下:

*

* ~ 第一個(gè) * 代表任意修飾符及任意返回值. ~ 第二個(gè) * 定義在web包或者子包 ~ 第三個(gè) * 任意方法 ~ .. 匹配任意數(shù)量的參數(shù).

*/

static final String pCutStr = "execution(* com.appleyk.*..*(..))";

@Pointcut(value = pCutStr)

public void logPointcut() {

}

@Around("logPointcut()")

public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {

//初始化 一次

if(map1.get() ==null ){

map1.set(new HashMap<>());

}

if(map2.get() == null){

map2.set(new HashMap<>());

}

long start = System.currentTimeMillis();

try {

Object result = joinPoint.proceed();

if(result==null){

//如果切到了 沒(méi)有返回類(lèi)型的void方法,這里直接返回

return null;

}

long end = System.currentTimeMillis();

logger.info("===================");

String tragetClassName = joinPoint.getSignature().getDeclaringTypeName();

String MethodName = joinPoint.getSignature().getName();

Object[] args = joinPoint.getArgs();// 參數(shù)

int argsSize = args.length;

String argsTypes = "";

String typeStr = joinPoint.getSignature().getDeclaringType().toString().split(" ")[0];

String returnType = joinPoint.getSignature().toString().split(" ")[0];

logger.info("類(lèi)/接口:" + tragetClassName + "(" + typeStr + ")");

logger.info("方法:" + MethodName);

logger.info("參數(shù)個(gè)數(shù):" + argsSize);

logger.info("返回類(lèi)型:" + returnType);

if (argsSize > 0) {

// 拿到參數(shù)的類(lèi)型

for (Object object : args) {

argsTypes += object.getClass().getTypeName().toString() + " ";

}

logger.info("參數(shù)類(lèi)型:" + argsTypes);

}

Long total = end - start;

logger.info("耗時(shí): " + total + " ms!");

if(map1.get().containsKey(MethodName)){

Long count = map1.get().get(MethodName);

map1.get().remove(MethodName);//先移除,在增加

map1.get().put(MethodName, count+1);

count = map2.get().get(MethodName);

map2.get().remove(MethodName);

map2.get().put(MethodName, count+total);

}else{

map1.get().put(MethodName, 1L);

map2.get().put(MethodName, total);

}

return result;

} catch (Throwable e) {

long end = System.currentTimeMillis();

logger.info("====around " + joinPoint + "tUse time : " + (end - start) + " ms with exception : "

+ e.getMessage());

throw e;

}

}

//對(duì)Controller下面的方法執(zhí)行前進(jìn)行切入,初始化開(kāi)始時(shí)間

@Before(value = "execution(* com.appleyk.controller.*.*(..))")

public void beforMehhod(JoinPoint jp) {

startTime.set(System.currentTimeMillis());

}

//對(duì)Controller下面的方法執(zhí)行后進(jìn)行切入,統(tǒng)計(jì)方法執(zhí)行的次數(shù)和耗時(shí)情況

//注意,這里的執(zhí)行方法統(tǒng)計(jì)的數(shù)據(jù)不止包含Controller下面的方法,也包括環(huán)繞切入的所有方法的統(tǒng)計(jì)信息

@AfterReturning(value = "execution(* com.appleyk.controller.*.*(..))")

public void afterMehhod(JoinPoint jp) {

long end = System.currentTimeMillis();

long total = end - startTime.get();

String methodName = jp.getSignature().getName();

logger.info("連接點(diǎn)方法為:" + methodName + ",執(zhí)行總耗時(shí)為:" +total+"ms");

//重新new一個(gè)map

Map map = new HashMap<>();

//從map2中將最后的 連接點(diǎn)方法給移除了,替換成最終的,避免連接點(diǎn)方法多次進(jìn)行疊加計(jì)算

//由于map2受ThreadLocal的保護(hù),這里不支持remove,因此,需要單開(kāi)一個(gè)map進(jìn)行數(shù)據(jù)交接

for(Map.Entry entry:map2.get().entrySet()){

if(entry.getKey().equals(methodName)){

map.put(methodName, total);

}else{

map.put(entry.getKey(), entry.getValue());

}

}

for (Map.Entry entry :map1.get().entrySet()) {

for(Map.Entry entry2 :map.entrySet()){

if(entry.getKey().equals(entry2.getKey())){

System.err.println(entry.getKey()+",被調(diào)用次數(shù):"+entry.getValue()+",綜合耗時(shí):"+entry2.getValue()+"ms");

}

}

}

}

}

(3)說(shuō)明

A.

B.

C.

D.

E.

D.其他見(jiàn)代碼注釋,還有一個(gè)地方需要注意

三、本次請(qǐng)求(保存players)測(cè)試的入口(Controller)

(1)對(duì)應(yīng)Controller

(2)再往下走一層,見(jiàn)證一下對(duì)應(yīng)的ServiceImpl的實(shí)現(xiàn)部分

四、請(qǐng)求測(cè)試

(1)數(shù)據(jù)準(zhǔn)備

(2)mysql數(shù)據(jù)庫(kù) 查看數(shù)據(jù)(暫時(shí)無(wú))

(3) 清空項(xiàng)目中的Console控制臺(tái)輸出信息

(4)Send 測(cè)試數(shù)據(jù)進(jìn)行 post請(qǐng)求

(5)查看項(xiàng)目控制臺(tái)輸出內(nèi)容

(6)post請(qǐng)求數(shù)據(jù)(存儲(chǔ))驗(yàn)證

(7)再來(lái)個(gè)大數(shù)據(jù)量的post數(shù)據(jù)請(qǐng)求(存儲(chǔ))

(8)本地日志查看

本篇涉及兩個(gè)知識(shí)點(diǎn),一個(gè)就是spring-boot日志的配置,一個(gè)就是aop的配置,前者在我的博文里有,后者的配置也很簡(jiǎn)單,總得來(lái)說(shuō),spring-boot作為spring的孩子,其在配置方面,真的是簡(jiǎn)化了不少,基本上就是零xml配置。

總結(jié)

以上是生活随笔為你收集整理的aop统计请求数量_Spring-Boot+AOP+统计单次请求方法的执行次数和耗时情况-Go语言中文社区...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。