counter计数器
一、為什么要做計(jì)數(shù)器:
在唯品會(huì)的工作的時(shí)候,為了更好的推動(dòng)系統(tǒng)向智慧供應(yīng)鏈發(fā)展,理性分析供應(yīng)商和商助對(duì)產(chǎn)品的使用頻率,追溯服務(wù)歷史調(diào)用情況,于是,開發(fā)了一款輕量級(jí)計(jì)數(shù)器,供各團(tuán)隊(duì)使用。
二、難點(diǎn):
1、調(diào)用量大
2、各團(tuán)隊(duì)需要保證極端情況下,計(jì)數(shù)器服務(wù)不可用時(shí),不影響各團(tuán)隊(duì)業(yè)務(wù)正常運(yùn)行
3、需要使用簡(jiǎn)單
4、個(gè)別需要統(tǒng)一業(yè)務(wù)日志
5、不能有額外的定時(shí)器一直輪詢
三、優(yōu)勢(shì):
1、簡(jiǎn)單? ?@LogCounter? 對(duì)應(yīng)的類或者方法上 即可開啟計(jì)數(shù)器功能
2、可配置? ? 日志記錄可選擇開啟和關(guān)閉、可選擇記錄到本地、可選擇記錄到遠(yuǎn)程服務(wù)器
3、統(tǒng)一日志? 開啟日志功能后,即可對(duì)方法的入?yún)⒑统鰠⑦M(jìn)行記錄
4、采用了線程池隔離
5、設(shè)計(jì)了漏斗策略
四、代碼:
? ? ??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;/*** 功 能:日志注解標(biāo)簽* 作 者:java瀟邦* 時(shí) 間:2018-05-19*/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface LogCounter {String counterName() default "";//計(jì)數(shù)器名稱boolean useLogFile() default true;//true時(shí)記錄log日志boolean useRemote() default true;//true時(shí)記錄counterboolean useLocal() default false;//true時(shí)開啟本地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瀟邦* 時(shí) 間: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;//切入點(diǎn)@Pointcut("@annotation(com.javaxiaobang.counter.aspect.log.LogCounter)")public void anyMethod() {}//方法執(zhí)行之前@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("{}{}方法的參數(shù)值:{}", 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);}}//方法執(zhí)行之后@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);}}//方法執(zhí)行之后異常日志@AfterThrowing(pointcut = "anyMethod()", throwing = "e")public void afterThrowing(JoinPoint joinPoint, Throwable e) throws CounterException {//不需要再記錄ServiceException,因?yàn)樵趕ervice異常切面中已經(jīng)記錄過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;/*** 功 能:遠(yuǎn)程計(jì)數(shù)器-調(diào)用遠(yuǎn)程服務(wù)保存數(shù)據(jù)* 作 者:java瀟邦* 時(shí) 間: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秒獲取鎖的時(shí)間private static long refreshTime = 30000L;//30秒刷新一次private static ConcurrentHashMap<String, AtomicLong> countMap = new ConcurrentHashMap<String, AtomicLong>(32);//并發(fā)計(jì)數(shù)private static ConcurrentHashMap<String, Long> timeMap = new ConcurrentHashMap<String, Long>(32);//并發(fā)計(jì)時(shí)private static long detailRefreshTime = 10 * 60 * 1000L;//默認(rèn)10分鐘刷新數(shù)據(jù)private static ConcurrentHashMap<String, AtomicLong> detailCountMap = new ConcurrentHashMap<String, AtomicLong>(32);//并發(fā)計(jì)數(shù)private static ConcurrentHashMap<String, Long> detailTimeMap = new ConcurrentHashMap<String, Long>(32);//并發(fā)計(jì)時(shí)// private CounterServiceHelper.CounterServiceClient client = new CounterServiceHelper.CounterServiceClient();//唯品會(huì)counter服務(wù)-對(duì)應(yīng)兩張表/*** 計(jì)數(shù)器邏輯處理*/public void execute(String counterName) {commonMethod("total", counterName, refreshTime, countMap, timeMap);//計(jì)數(shù)器頭表commonMethod("detail", counterName, detailRefreshTime, detailCountMap, detailTimeMap);//計(jì)數(shù)器明細(xì)表}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) {//原子計(jì)數(shù)器synchronized (lock) {//并發(fā)控制atomicLong = countMap.get(key);if (null == atomicLong) {countMap.put(key, new AtomicLong(1));timeMap.put(key, System.currentTimeMillis());return;}}}atomicLong.incrementAndGet();//原子計(jì)數(shù)器加1boolean flag = (System.currentTimeMillis() - timeMap.get(key) > refreshTime) && (atomicLong.get() > 0);//超過N秒 并且 次數(shù)大于0if (flag) {//滿足條件的漏斗if (lock.tryLock(tryLockTime, TimeUnit.SECONDS)) {try {flag = (System.currentTimeMillis() - timeMap.get(key) > refreshTime) && (atomicLong.get() > 0);//避免錯(cuò)誤更新if (flag) {long counter = countMap.get(key).get();timeMap.put(key, System.currentTimeMillis());//重新計(jì)時(shí)atomicLong.set(0);countMap.put(key, atomicLong);//重新計(jì)數(shù)if ("detail".equals(type)) {incrementDetailCounter(counterName, domain, counter, refreshTime);} else {incrementCounter(counterName, domain, counter);}}} finally {lock.unlock();}} else {logger.warn("2秒內(nèi)沒有獲取到鎖則放棄,下次再調(diào)用遠(yuǎn)程保存數(shù)據(jù)的服務(wù):{}", key);}}} catch (Exception e) {logger.debug("remote計(jì)數(shù)器異常:", e);}}/*** 保存到counter_info頭表*/public void incrementCounter(String counterName, String domain, long counter) {//調(diào)用遠(yuǎn)程方法 // 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明細(xì)表*/public void incrementDetailCounter(String counterName, String domain, long counter, long interval) {//調(diào)用遠(yuǎn)程方法 // 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;/*** 功 能:本地計(jì)數(shù)器-實(shí)現(xiàn)LogCounterDelayInterface接口-保存到本地?cái)?shù)據(jù)庫(kù)* 作 者:java瀟邦* 時(shí) 間: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秒獲取鎖的時(shí)間private static long refreshTime = 30000L;//30秒刷新一次private static ConcurrentHashMap<String, AtomicLong> countMap = new ConcurrentHashMap<String, AtomicLong>(32);//并發(fā)計(jì)數(shù)private static ConcurrentHashMap<String, Long> timeMap = new ConcurrentHashMap<String, Long>(32);//并發(fā)計(jì)時(shí)private static long detailRefreshTime = 10 * 60 * 1000L;//默認(rèn)10分鐘刷新數(shù)據(jù)private static ConcurrentHashMap<String, AtomicLong> detailCountMap = new ConcurrentHashMap<String, AtomicLong>(32);//并發(fā)計(jì)數(shù)private static ConcurrentHashMap<String, Long> detailTimeMap = new ConcurrentHashMap<String, Long>(32);//并發(fā)計(jì)時(shí)//懶加載@Autowired(required = false)private LogCounterDelayInterface logCounterDelayInterface;/*** 計(jì)數(shù)器邏輯處理*/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) {//原子計(jì)數(shù)器synchronized (lock) {//并發(fā)控制atomicLong = countMap.get(key);if (null == atomicLong) {countMap.put(key, new AtomicLong(1));timeMap.put(key, System.currentTimeMillis());return;}}}atomicLong.incrementAndGet();//原子計(jì)數(shù)器加1boolean flag = (System.currentTimeMillis() - timeMap.get(key) > refreshTime) && (atomicLong.get() > 0);//超過N秒 并且 次數(shù)大于0if (flag) {//滿足條件的漏斗if (lock.tryLock(tryLockTime, TimeUnit.SECONDS)) {try {flag = (System.currentTimeMillis() - timeMap.get(key) > refreshTime) && (atomicLong.get() > 0);//避免錯(cuò)誤更新if (flag) {long counter = countMap.get(key).get();timeMap.put(key, System.currentTimeMillis());//重新計(jì)時(shí)atomicLong.set(0);countMap.put(key, atomicLong);//重新計(jì)數(shù)if ("detail".equals(type)) {logCounterDelayInterface.incrementDetailCounter(counterName, domain, counter, refreshTime);} else {logCounterDelayInterface.incrementCounter(counterName, domain, counter);}}} finally {lock.unlock();}} else {logger.warn("2秒內(nèi)沒有獲取到鎖則放棄,下次再調(diào)用保存數(shù)據(jù)的服務(wù):{}", key);}}} catch (Exception e) {logger.info("local計(jì)數(shù)器異常:", 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;} }?
總結(jié)
以上是生活随笔為你收集整理的counter计数器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 车间能量看板设计需求,能给个思路吗
- 下一篇: 微信小程序 实现路线规划