基于切面/快照/日志/操作/统计的通用服务

介绍

aop + 自定义注解,实现统一日志,统计,的功能。

期望能满足如下要求:

  • 高吞吐
  • 支持同步,异步两种模式,分别代表,允许丢数据,不允许丢数据

场景

  • 登陆登出、系统核心功能调用频率统计、系统运行关键数据收集等

思路

  • 需要一个自定义注解,一个aop,一个异步线程持续将数据写入到数据库中。当然也可以考虑同步的。

实现

方法上的调用代码

1
2
3
4
5
@AuditLog(operation = "动作标识",objectType="类型标识",type=async,module="模块标识",
objectName=@Value("args[]"),level="日志级别")
public boolean publishProblem(Object args) {
//dosomething
}

aop中的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Aspect
@Component
public class AuditLogInterceptor {

@Resource
private AuditLogger auditLogger;

private ExecutorService executors = new TraceThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());

public Object around(ProceedingJoinPoint pjp) throws Throwable {
Object o = pjp.proceed();
try {
Method method = Modifier.isInterface(pjp.getSignature().getDeclaringType().getModifiers()) ?
pjp.getTarget().getClass().getMethod(pjp.getSignature().getName(), ((MethodSignature) pjp.getSignature()).getMethod().getParameterTypes())
: ((MethodSignature) pjp.getSignature()).getMethod();
AuditLog auditLog = method.getAnnotation(AuditLog.class);
if (!SyswareUtil.isEmpty(auditLog)) {
executors.execute(new AuditLogInterceptor.AuditLogThread(auditLogger,
getAuditLog(auditLog, pjp.getArgs(), o)));
}
} catch (Exception e) {
throw new AuditLogException("获取实现方法发生错误:" + e.getMessage());
}
return o;
}

public class AuditLogThread implements Runnable {

private AuditLogger service;

private AuditLogObject instance;

public AuditLogThread(AuditLogger service, AuditLogObject instance) {
this.service = service;
this.instance = instance;
}

@Override
public void run(){
service.log(instance);
}
}

多线程参数传递问题
使用多线程时父线程中的上下文信息通常无法直接传递到子线程中去,容易造成程序bug。

  • method-1:每次都手动的将子线程需要用到的ThreadLocal数据传递到子线程中,这样子线程也能随时获取到线程上下文信息。
  • method-2:自定义一个ThreadPoolExecutor代替系统的ThreadPoolExecutor,每次用线程池提交线程任务时,线程池会自动将父线程的ThreadLocal自动传递到子线程中,避免每次手动传递ThreadLocal到子线程。
    method-2实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    public class TraceThreadPoolExecutor extends ThreadPoolExecutor {

    public TraceThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
    super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    public TraceThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
    super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
    }

    public TraceThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
    super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
    }

    public TraceThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
    super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }

    @Override
    public void execute(final Runnable command) {
    final Operator operator = OperationContext.getInstance().getOperator();
    final String userId = operator.getUserId();
    Runnable task = new Runnable() {
    @Override
    public void run() {
    OperationContextUtil.operatorContext(userId);
    try{
    command.run();
    } catch (Exception e){
    //dosomething
    } finally {
    OperationContext.getInstance().clear();
    }
    }
    };
    super.execute(task);
    }

考虑的点

  • 分布式部署,server端随时上下线会不会有影响,基本上常见的并发编程问题。
  • 更优雅的接入方式,提供一个jar包,方法上增加一个注解即可。
  • 与CAT等常用优秀开源产品的结合使用

总结

  • 保证效率和代码质量的情况下,根据自身项目去灵活实现。合适自己的才是最好的
  • 后续遇到其他的使用方案再进行补充
------本文结束感谢阅读------
显示评论
0%