最近遇到个问题,一个简单的spring mvc的项目,但是经常被刷子刷,恶意暴力破解通信协议,不断尝试登陆系统,为了方便实时统计请求量和请求返回码的分布情况。最简单的逻辑:不断有人错误登陆,当次数多了,说明有问题了,有人在恶意刷我们的系统。
那么这个问题的一个重要环节就是统计所有请求的返回码,方便离线大数据的同学分析,怎么记录返回码呢?总不能每次都定义一个log,在每个return里进行log.info吧。
returnMap.put("result", "500"); returnMap.put("msg", "数据库查询出错"); errorLogger.error("500,数据库查询出错", e); return returnMap;
这种方式太恶心了,冗余代码一坨一坨的。
目前本人想到的方式是通过自定义注解和aop来进行统计return的返回码。比如:一天有20%的请求成功,80%是失败,这样也方便我们分析。
做法如下:
自定义注解:在需要统计的方法上加这个统计注解
package com.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; /** * @author */ @Target({ ElementType.PARAMETER, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ControllerMetric { String description() default ""; AspectReturnTypeEnum type() default AspectReturnTypeEnum.String; String returnCode() default "result"; }
注解返回值的类型Enum:
package com.annotation; /** * @author * </br> * 用于在controller层拦截的时候定义的返回类型 * 在ControllerAspect里会对不同的返回类型做不同 的处理。 */ public enum AspectReturnTypeEnum { Map, String, JSON, XML }
定义的AOP切面:
package com.aop; import java.util.Map; import javax.servlet.http.HttpServletRequest; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import com.annotation.ControllerMetric; /** * @author */ @Aspect @Component public class ControllerAspect { private static final Logger logger = LoggerFactory.getLogger(ControllerAspect.class); @Pointcut("@annotation( com.annotation.ControllerMetric)") public void controllerAspect() { } @Around("controllerAspect()&&@annotation(log)") public Object doCountMetric(ProceedingJoinPoint pjp, ControllerMetric log) throws Throwable { logger.debug("enter doCountMetric ProceedingJoinPoint " + log.type() + "," + log.returnCode()); Object[] args = pjp.getArgs(); HttpServletRequest req = (HttpServletRequest) args[0]; System.out.println("The request uri for this ProceedingJoinPoint is : " + req.getRequestURI()); Object object = pjp.proceed(); switch (log.type()) { case Map: Map ret = (java.util.Map) object; System.out.println(ret); break; default: } logger.debug("leave doCountMetric ProceedingJoinPoint"); return object; } }
在Controller中用法
@Controller @RequestMapping("/sdk/mobService/device") /** * 可信设备相关的controller * @author liuxiaoming * */ public class EquipmentController { private static Logger mobLog = LoggerFactory.getLogger("mobLog"); private static Logger errorLogger = LoggerFactory.getLogger(EquipmentController.class); @Autowired EquipmentServiceImpl equipmentServiceImpl; @Autowired MobUtil mobUtil; @RequestMapping("/bd") @ResponseBody @ControllerMetric public Map<String, String> bdDevice(HttpServletRequest request, HttpServletResponse response) { Map<String, String> returnMap = new HashMap<String, String>(); return returnMap }
通过这种形式,所有的返回码都会被拦截,然后可以用一个日志去记录,我这里是直接输出了。
时间: 2024-10-27 06:04:31