一、为什么要做计数器:

在唯品会的工作的时候,为了更好的推动系统向智慧供应链发展,理性分析供应商和商助对产品的使用频率,追溯服务历史调用情况,于是,开发了一款轻量级计数器,供各团队使用。

二、难点:

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计数器相关推荐

  1. MapReducer Counter计数器的使用,Combiner ,Partitioner,Sort,Grop的使用,

    一:Counter计数器的使用 hadoop计数器:可以让开发人员以全局的视角来审查程序的运行情况以及各项指标,及时做出错误诊断并进行相应处理. 内置计数器(MapReduce相关.文件系统相关和作业 ...

  2. counter 计数器

    包含了两个属性和一个方法: 1. counter-reset 2. counter-increment 3. counter()/counters() counter-reset(主要作用就是给计数器 ...

  3. CSS counter计数器(content目录序号自动递增)详解

    一.挖坟不可耻 说到报数,是否想起了教官的嘹亮口号:"生信4班,立正,稍息,开始1,2,3,4报数!" 其中有这么几个关键点: 1. 班级命名.总不能六脉神剑一指,你,侬,汝来称呼 ...

  4. Python标准库collections库:超好用的counter计数器,不接受反驳!

    collections是python的标准库,它提供了一些解决特定问题的容器,也就是说有些问题虽然可以用list,tuple,dict解决,但解决起来太麻烦,而这些问题又经常遇到,所以他们就把这些问题 ...

  5. Python里最好用的counter计数器,不接受反驳!

    collections是python的标准库,它提供了一些解决特定问题的容器,也就是说有些问题虽然可以用list,tuple,dict解决,但解决起来太麻烦,而这些问题又经常遇到,所以他们就把这些问题 ...

  6. 计数器:counter

    组成:2属性,1方法 属性1: counter-reset 命名 属性2: counter-increment 启动/自增 方法 : counter()/counters() 调用方法 1.计数器 命 ...

  7. Python——计数器(Counter)

    计数器(Counter) 编写一个对输入的字符串,进行计数的程序. 计数器(Counter) 计数器是一个无序容器,用于记录各种值出现的次数.它采用键值对的形式存储,要记录的值作为key,这个值出现的 ...

  8. Python之计数器 (Counter)

    任务描述 本关任务:编写一个对输入字符串的字符进行计数的程序. 计数器(Counter) 计数器是一个无序容器,用于记录各种值出现的次数.它采用键值对的形式存储,要记录的值作为key,这个值出现的次数 ...

  9. python counter函数定义_分享几个自己常用的Python高级函数

    哈喽大家好我是蚂蚁,今天给大家分享几个我自己常用的Python相对高级点的函数,这些函数在特定的场景下能节省大量的代码. 简单列举一下我想要介绍的几个函数: counter:计数器 defaultdi ...

  10. python有序队列_【python】collections模块(有序字典,计数器,双向队列)

    collections模块基本介绍 我们都知道,Python拥有一些内置的数据类型,比如str, int, list, tuple, dict等, collections模块在这些内置数据类型的基础上 ...

最新文章

  1. elasticsearch: 权威指南_你还不会Elasticsearch的CUD?
  2. 截取列表前面100行_python列表
  3. 有理贝塞尔曲线(Rational Bezier Curves)
  4. java下拉框查询_[Java教程]jQuery实现联动下拉列表查询框
  5. oracle v$sql last_load_time,Oracle 等待事件V$视图
  6. spring客户端resttemplate/feign/httpclient调研
  7. 代码单元测试:gtest
  8. 图片加载框架Picasso - 源码分析
  9. 函数yield报错ValueError: too many values to unpack (expected 2)
  10. Yii2框架之旅(六)
  11. js中WINDOW对象
  12. 这样的书 我改怎么起名呢?
  13. 对于拼接进去的html原来绑定的jq事件失效
  14. 【风电功率预测】基于matlab粒子群算法优化LSTM风电功率预测【含Matlab源码 941期】
  15. Datalogic得利捷引领工业4.0时代 携SG4 FIELDBUS安全光幕亮相两大国际工业展
  16. jquery提交表单错误
  17. 2013中国电商盘点回顾
  18. c语言 switch案例,c语言switch case语句使用例子
  19. http的CA证书安装(也就是https)
  20. ExcelVBA之InputBox函数

热门文章

  1. 一次旅行:汕尾-汕头-梅州
  2. C++新特性28_线程同步问题的产生原因(高级语言转为低级语言执行,时间片交替运行多线程中代码,代码切换过程中出现的问题)
  3. Mac系统 QuickTime 屏幕录制
  4. Liu C-2021-1: Nontrivial Gates FET
  5. 免费云服务器获取方法 云服务器购买
  6. ubuntu中firfox插件xmarks的同步问题
  7. ffmpeg 抽取视频原始编码数据(mpeg4)方法
  8. 【优化模型】推销员问题模型
  9. 【生物信息】影像组学入门实践成长营(14天)
  10. Unity:骨骼动画