counter计数器
一、为什么要做计数器:
在唯品会的工作的时候,为了更好的推动系统向智慧供应链发展,理性分析供应商和商助对产品的使用频率,追溯服务历史调用情况,于是,开发了一款轻量级计数器,供各团队使用。
二、难点:
1、调用量大
2、各团队需要保证极端情况下,计数器服务不可用时,不影响各团队业务正常运行
3、需要使用简单
4、个别需要统一业务日志
5、不能有额外的定时器一直轮询
三、优势:
1、简单 @LogCounter 对应的类或者方法上 即可开启计数器功能
2、可配置 日志记录可选择开启和关闭、可选择记录到本地、可选择记录到远程服务器
3、统一日志 开启日志功能后,即可对方法的入参和出参进行记录
4、采用了线程池隔离
5、设计了漏斗策略
四、代码:
https://gitee.com/sunrisexq/counter
package com.javaxiaobang.counter.aspect.log;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;/*** 功 能:日志注解标签* 作 者:java潇邦* 时 间:2018-05-19*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface LogCounter {String counterName() default "";//计数器名称boolean useLogFile() default true;//true时记录log日志boolean useRemote() default true;//true时记录counterboolean useLocal() default false;//true时开启本地counter}
package com.javaxiaobang.counter.aspect.log;import com.alibaba.fastjson.JSON;
import com.javaxiaobang.counter.exception.CounterException;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 功 能:日志AOP* 作 者:java潇邦* 时 间:2018-05-19*/
@Order
@Aspect
@Component("commonLogCounterAop")
public class LogCounterAop {private final Logger logger = LoggerFactory.getLogger(this.getClass());private static ExecutorService pool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());@Autowiredprivate LogCounterLocalBusiness logCounterLocalBusiness;@Autowiredprivate LogCounterRemoteBusiness logCounterRemoteBusiness;//切入点@Pointcut("@annotation(com.javaxiaobang.counter.aspect.log.LogCounter)")public void anyMethod() {}//方法执行之前@Before("anyMethod()")public void before(final JoinPoint joinPoint) {try {Signature signature = joinPoint.getSignature();MethodSignature methodSignature = (MethodSignature) signature;Method method = methodSignature.getMethod();if (method != null && method.isAnnotationPresent(LogCounter.class)) {String methodName = method.getName();final LogCounter aspect = method.getAnnotation(LogCounter.class);if (aspect.useLogFile()) {logger.info("{}{}方法的参数值:{}", aspect.counterName(), methodName, JSON.toJSONString(joinPoint.getArgs()));}final String counterName = StringUtils.isBlank(aspect.counterName()) ? methodName : aspect.counterName();if (aspect.useLocal()) {pool.submit(new Runnable() {@Overridepublic void run() {logCounterLocalBusiness.execute(counterName);}});}if (aspect.useRemote()) {pool.submit(new Runnable() {@Overridepublic void run() {logCounterRemoteBusiness.execute(counterName);}});}}} catch (Exception e) {logger.debug("before切面异常:", e);}}//方法执行之后@AfterReturning(pointcut = "anyMethod()", returning = "retVal")public void after(JoinPoint joinPoint, Object retVal) {try {Signature signature = joinPoint.getSignature();MethodSignature methodSignature = (MethodSignature) signature;Method method = methodSignature.getMethod();if (method != null) {String methodName = method.getName();LogCounter aspect = method.getAnnotation(LogCounter.class);if (aspect != null && aspect.useLogFile()) {logger.info("{}{}方法的返回值:{}", aspect.counterName(), methodName, JSON.toJSONString(retVal));}}} catch (Exception e) {logger.debug("after切面异常:", e);}}//方法执行之后异常日志@AfterThrowing(pointcut = "anyMethod()", throwing = "e")public void afterThrowing(JoinPoint joinPoint, Throwable e) throws CounterException {//不需要再记录ServiceException,因为在service异常切面中已经记录过if (e instanceof CounterException) {throw (CounterException) e;} else {Signature signature = joinPoint.getSignature();MethodSignature methodSignature = (MethodSignature) signature;Method method = methodSignature.getMethod();if (method != null) {String methodName = method.getName();LogCounter aspect = method.getAnnotation(LogCounter.class);logger.warn("{}{}方法异常:", aspect.counterName(), methodName, e);throw new CounterException();}}}}
package com.javaxiaobang.counter.aspect.log;import com.javaxiaobang.counter.util.LogCounterUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;/*** 功 能:远程计数器-调用远程服务保存数据* 作 者:java潇邦* 时 间:2018-05-19*/
@Component("logCounterRemoteBusiness")
public class LogCounterRemoteBusiness {private final Logger logger = LoggerFactory.getLogger(this.getClass());private static ReentrantLock lock = new ReentrantLock();private static String domain = LogCounterUtil.getDomain();private static long tryLockTime = 2;//尝试2秒获取锁的时间private static long refreshTime = 30000L;//30秒刷新一次private static ConcurrentHashMap<String, AtomicLong> countMap = new ConcurrentHashMap<String, AtomicLong>(32);//并发计数private static ConcurrentHashMap<String, Long> timeMap = new ConcurrentHashMap<String, Long>(32);//并发计时private static long detailRefreshTime = 10 * 60 * 1000L;//默认10分钟刷新数据private static ConcurrentHashMap<String, AtomicLong> detailCountMap = new ConcurrentHashMap<String, AtomicLong>(32);//并发计数private static ConcurrentHashMap<String, Long> detailTimeMap = new ConcurrentHashMap<String, Long>(32);//并发计时// private CounterServiceHelper.CounterServiceClient client = new CounterServiceHelper.CounterServiceClient();//唯品会counter服务-对应两张表/*** 计数器逻辑处理*/public void execute(String counterName) {commonMethod("total", counterName, refreshTime, countMap, timeMap);//计数器头表commonMethod("detail", counterName, detailRefreshTime, detailCountMap, detailTimeMap);//计数器明细表}private void commonMethod(String type, String counterName, long refreshTime, ConcurrentHashMap<String, AtomicLong> countMap, ConcurrentHashMap<String, Long> timeMap) {try {String key = counterName + domain;//唯一keyAtomicLong atomicLong = countMap.get(key);if (null == atomicLong) {//原子计数器synchronized (lock) {//并发控制atomicLong = countMap.get(key);if (null == atomicLong) {countMap.put(key, new AtomicLong(1));timeMap.put(key, System.currentTimeMillis());return;}}}atomicLong.incrementAndGet();//原子计数器加1boolean flag = (System.currentTimeMillis() - timeMap.get(key) > refreshTime) && (atomicLong.get() > 0);//超过N秒 并且 次数大于0if (flag) {//满足条件的漏斗if (lock.tryLock(tryLockTime, TimeUnit.SECONDS)) {try {flag = (System.currentTimeMillis() - timeMap.get(key) > refreshTime) && (atomicLong.get() > 0);//避免错误更新if (flag) {long counter = countMap.get(key).get();timeMap.put(key, System.currentTimeMillis());//重新计时atomicLong.set(0);countMap.put(key, atomicLong);//重新计数if ("detail".equals(type)) {incrementDetailCounter(counterName, domain, counter, refreshTime);} else {incrementCounter(counterName, domain, counter);}}} finally {lock.unlock();}} else {logger.warn("2秒内没有获取到锁则放弃,下次再调用远程保存数据的服务:{}", key);}}} catch (Exception e) {logger.debug("remote计数器异常:", e);}}/*** 保存到counter_info头表*/public void incrementCounter(String counterName, String domain, long counter) {//调用远程方法
// try {
// CounterInfo counterInfo = new CounterInfo();
// counterInfo.setCounterName(counterName);
// counterInfo.setDomain(domain);
// counterInfo.setCounter(counter);
// client.incrementCounter(counterInfo);
// } catch (Exception e) {
// logger.debug("incrementCounter:", e);
// }}/*** 保存到counter_detail明细表*/public void incrementDetailCounter(String counterName, String domain, long counter, long interval) {//调用远程方法
// try {
// CounterInfo counterInfo = new CounterInfo();
// counterInfo.setCounterName(counterName);
// counterInfo.setDomain(domain);
// counterInfo.setCounter(counter);
// counterInfo.setInterval(interval);
// client.incrementDetailCounter(counterInfo);
// } catch (Exception e) {
// logger.debug("incrementDetailCounter:", e);
// }}public static long getRefreshTime() {return refreshTime;}public static void setRefreshTime(long refreshTime) {LogCounterRemoteBusiness.refreshTime = refreshTime;}public static long getDetailRefreshTime() {return detailRefreshTime;}public static void setDetailRefreshTime(long detailRefreshTime) {LogCounterRemoteBusiness.detailRefreshTime = detailRefreshTime;}public static void setDomain(String domain) {LogCounterRemoteBusiness.domain = domain;}public static String getDomain() {return domain;}}
package com.javaxiaobang.counter.aspect.log;import com.javaxiaobang.counter.aspect.delay.LogCounterDelayInterface;
import com.javaxiaobang.counter.util.LogCounterUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;/*** 功 能:本地计数器-实现LogCounterDelayInterface接口-保存到本地数据库* 作 者:java潇邦* 时 间:2018-05-19*/
@Component("logCounterLocalBusiness")
public class LogCounterLocalBusiness {private final Logger logger = LoggerFactory.getLogger(this.getClass());private static ReentrantLock lock = new ReentrantLock();private static String domain = LogCounterUtil.getDomain();private static long tryLockTime = 2;//尝试2秒获取锁的时间private static long refreshTime = 30000L;//30秒刷新一次private static ConcurrentHashMap<String, AtomicLong> countMap = new ConcurrentHashMap<String, AtomicLong>(32);//并发计数private static ConcurrentHashMap<String, Long> timeMap = new ConcurrentHashMap<String, Long>(32);//并发计时private static long detailRefreshTime = 10 * 60 * 1000L;//默认10分钟刷新数据private static ConcurrentHashMap<String, AtomicLong> detailCountMap = new ConcurrentHashMap<String, AtomicLong>(32);//并发计数private static ConcurrentHashMap<String, Long> detailTimeMap = new ConcurrentHashMap<String, Long>(32);//并发计时//懒加载@Autowired(required = false)private LogCounterDelayInterface logCounterDelayInterface;/*** 计数器逻辑处理*/public void execute(String counterName) {commonMethod("total", counterName, refreshTime, countMap, timeMap);commonMethod("detail", counterName, detailRefreshTime, detailCountMap, detailTimeMap);}private void commonMethod(String type, String counterName, long refreshTime, ConcurrentHashMap<String, AtomicLong> countMap, ConcurrentHashMap<String, Long> timeMap) {try {String key = counterName + domain;//唯一keyAtomicLong atomicLong = countMap.get(key);if (null == atomicLong) {//原子计数器synchronized (lock) {//并发控制atomicLong = countMap.get(key);if (null == atomicLong) {countMap.put(key, new AtomicLong(1));timeMap.put(key, System.currentTimeMillis());return;}}}atomicLong.incrementAndGet();//原子计数器加1boolean flag = (System.currentTimeMillis() - timeMap.get(key) > refreshTime) && (atomicLong.get() > 0);//超过N秒 并且 次数大于0if (flag) {//满足条件的漏斗if (lock.tryLock(tryLockTime, TimeUnit.SECONDS)) {try {flag = (System.currentTimeMillis() - timeMap.get(key) > refreshTime) && (atomicLong.get() > 0);//避免错误更新if (flag) {long counter = countMap.get(key).get();timeMap.put(key, System.currentTimeMillis());//重新计时atomicLong.set(0);countMap.put(key, atomicLong);//重新计数if ("detail".equals(type)) {logCounterDelayInterface.incrementDetailCounter(counterName, domain, counter, refreshTime);} else {logCounterDelayInterface.incrementCounter(counterName, domain, counter);}}} finally {lock.unlock();}} else {logger.warn("2秒内没有获取到锁则放弃,下次再调用保存数据的服务:{}", key);}}} catch (Exception e) {logger.info("local计数器异常:", e);}}public static void setDomain(String domain) {LogCounterLocalBusiness.domain = domain;}public static String getDomain() {return domain;}public static long getRefreshTime() {return refreshTime;}public static void setRefreshTime(long refreshTime) {LogCounterLocalBusiness.refreshTime = refreshTime;}public static long getDetailRefreshTime() {return detailRefreshTime;}public static void setDetailRefreshTime(long detailRefreshTime) {LogCounterLocalBusiness.detailRefreshTime = detailRefreshTime;}
}
counter计数器相关推荐
- MapReducer Counter计数器的使用,Combiner ,Partitioner,Sort,Grop的使用,
一:Counter计数器的使用 hadoop计数器:可以让开发人员以全局的视角来审查程序的运行情况以及各项指标,及时做出错误诊断并进行相应处理. 内置计数器(MapReduce相关.文件系统相关和作业 ...
- counter 计数器
包含了两个属性和一个方法: 1. counter-reset 2. counter-increment 3. counter()/counters() counter-reset(主要作用就是给计数器 ...
- CSS counter计数器(content目录序号自动递增)详解
一.挖坟不可耻 说到报数,是否想起了教官的嘹亮口号:"生信4班,立正,稍息,开始1,2,3,4报数!" 其中有这么几个关键点: 1. 班级命名.总不能六脉神剑一指,你,侬,汝来称呼 ...
- Python标准库collections库:超好用的counter计数器,不接受反驳!
collections是python的标准库,它提供了一些解决特定问题的容器,也就是说有些问题虽然可以用list,tuple,dict解决,但解决起来太麻烦,而这些问题又经常遇到,所以他们就把这些问题 ...
- Python里最好用的counter计数器,不接受反驳!
collections是python的标准库,它提供了一些解决特定问题的容器,也就是说有些问题虽然可以用list,tuple,dict解决,但解决起来太麻烦,而这些问题又经常遇到,所以他们就把这些问题 ...
- 计数器:counter
组成:2属性,1方法 属性1: counter-reset 命名 属性2: counter-increment 启动/自增 方法 : counter()/counters() 调用方法 1.计数器 命 ...
- Python——计数器(Counter)
计数器(Counter) 编写一个对输入的字符串,进行计数的程序. 计数器(Counter) 计数器是一个无序容器,用于记录各种值出现的次数.它采用键值对的形式存储,要记录的值作为key,这个值出现的 ...
- Python之计数器 (Counter)
任务描述 本关任务:编写一个对输入字符串的字符进行计数的程序. 计数器(Counter) 计数器是一个无序容器,用于记录各种值出现的次数.它采用键值对的形式存储,要记录的值作为key,这个值出现的次数 ...
- python counter函数定义_分享几个自己常用的Python高级函数
哈喽大家好我是蚂蚁,今天给大家分享几个我自己常用的Python相对高级点的函数,这些函数在特定的场景下能节省大量的代码. 简单列举一下我想要介绍的几个函数: counter:计数器 defaultdi ...
- python有序队列_【python】collections模块(有序字典,计数器,双向队列)
collections模块基本介绍 我们都知道,Python拥有一些内置的数据类型,比如str, int, list, tuple, dict等, collections模块在这些内置数据类型的基础上 ...
最新文章
- elasticsearch: 权威指南_你还不会Elasticsearch的CUD?
- 截取列表前面100行_python列表
- 有理贝塞尔曲线(Rational Bezier Curves)
- java下拉框查询_[Java教程]jQuery实现联动下拉列表查询框
- oracle v$sql last_load_time,Oracle 等待事件V$视图
- spring客户端resttemplate/feign/httpclient调研
- 代码单元测试:gtest
- 图片加载框架Picasso - 源码分析
- 函数yield报错ValueError: too many values to unpack (expected 2)
- Yii2框架之旅(六)
- js中WINDOW对象
- 这样的书 我改怎么起名呢?
- 对于拼接进去的html原来绑定的jq事件失效
- 【风电功率预测】基于matlab粒子群算法优化LSTM风电功率预测【含Matlab源码 941期】
- Datalogic得利捷引领工业4.0时代 携SG4 FIELDBUS安全光幕亮相两大国际工业展
- jquery提交表单错误
- 2013中国电商盘点回顾
- c语言 switch案例,c语言switch case语句使用例子
- http的CA证书安装(也就是https)
- ExcelVBA之InputBox函数