依赖的jar包
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
=======================业务event推送到redis队列=========================
注解标识
@Override
@Action(value="",type = 1,eventType="payplan")
public int update(TXdAfterPayPlanDto plan) {
return this.sqlSession.update(String.format("%s.updateByCondition", nameSpace), plan);
}
注解类
package com.qb.loan.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;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Action {
String value();
int type();
String eventType(); //队列名称
}
切面类
package com.qb.loan.annotation;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.UUID;
import javax.annotation.PostConstruct;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import com.qb.loan.dto.TXdAfterPayPlanDto;
import com.qb.loan.service.impl.AsyncRecordService;
import com.zrj.platform.base.log.AppLogger;
/**
* 记录器
* @author qianll
*
*/
@Aspect
@Component
public class RecordAspect implements ApplicationContextAware{
private AppLogger logger = new AppLogger(RecordAspect.class);
private ApplicationContext applicationContext;
private Map<String,EventHandler> eventHandlerMap;
@Autowired
private AsyncRecordService asyncRecordService;
@Autowired
private AdviceService adviceService;
@Pointcut("@annotation(com.qb.loan.annotation.Action)")
public void annotationPointCut(){}
/**
* 监控的dao方法,有可能因为事物回滚,导致输出冗余日志
* @param joinPoint
*/
@After("annotationPointCut()")
public void after(JoinPoint joinPoint){
try{
record(joinPoint);
}catch(Exception e){
logger.error("",e);
}
}
@PostConstruct
public void init(){
eventHandlerMap = applicationContext.getBeansOfType(EventHandler.class);
}
public void record(JoinPoint joinPoint){
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
Action action = method.getAnnotation(Action.class);
EventHandler handler = eventHandlerMap.get(action.eventType());
String key = handler.getKey(action.type(), joinPoint.getArgs());
adviceService.processEvent(action.eventType(), key);
}
public void recordLog(JoinPoint joinPoint){
StringBuilder builder = new StringBuilder();
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
Action action = method.getAnnotation(Action.class);
if(action.type() == 1){
TXdAfterPayPlanDto dto = (TXdAfterPayPlanDto)joinPoint.getArgs()[0];
if(dto != null){
builder.append(dto.getLoanNumber()+" KKKK|| ");
}
}
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
builder.append(signature.getDeclaringTypeName() + "."
+ method.getName());
for (StackTraceElement e : stackTrace) {
if(e.getClassName().startsWith("com.qb.loan")&&!e.getClassName().contains("$$")&&!e.getClassName().contains("annotation")){
builder.append(" | ");
builder.append(e.getClassName() + "."
+ e.getMethodName());
}
}
asyncRecordService.asyncRecord(builder.toString());
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
}
EventHandler接口类
package com.qb.loan.annotation;
public interface EventHandler {
/**
* 通过类型和入参返回队列中的key
* @param type
* @param para
* @return
*/
String getKey(int type,Object[] para);
}
接口实现类
package com.qb.loan.annotation;
import java.util.UUID;
import org.springframework.stereotype.Service;
@Service("payplan")
public class PayplanEventHandler implements EventHandler{
@Override
public String getKey(int type, Object[] para) {
// TODO Auto-generated method stub
return UUID.randomUUID().toString();
}
}
接口实现类
package com.qb.loan.annotation;
import java.util.UUID;
import org.springframework.stereotype.Service;
@Service("withhold")
public class WithholdEventHandler implements EventHandler{
@Override
public String getKey(int type, Object[] para) {
// TODO Auto-generated method stub
return UUID.randomUUID().toString();
}
}
切面服务类
package com.qb.loan.annotation;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;
import com.qb.loan.facade.IUserFacade;
import com.zrj.platform.base.log.AppLogger;
@Service
public class AdviceService implements ApplicationContextAware{
private AppLogger logger = new AppLogger(AdviceService.class);
private ConcurrentHashMap<String, AtomicBoolean> flagMap = new ConcurrentHashMap<String, AtomicBoolean>();
private ConcurrentHashMap<String, AtomicInteger> errorMap = new ConcurrentHashMap<String, AtomicInteger>();
@Resource
private EventQueue eventQueue;
private ApplicationContext applicationContext;
@PostConstruct
public void init() {
Set<String> set = applicationContext.getBeansOfType(EventHandler.class).keySet();
for (String eventType : set) {
flagMap.put(eventType, new AtomicBoolean(true));
errorMap.put(eventType, new AtomicInteger(0));
}
}
public void processEvent(String eventType, String... args) {
boolean flag = flagMap.get(eventType).get();
Long num = Long.MAX_VALUE;
if (flag) {
try {
num = eventQueue.lpush(eventType, args);
// logger.doInfo(eventType + "发布事件:" + args, num);
if (num > 1024 * 2) {
flagMap.get(eventType).set(false);
// TODO 微信报警
logger.doError(eventType + "缓存队列已满!");
}
} catch (Exception e) {
logger.doError("", e);
// TODO 任务进入NOSQL
if (errorMap.get(eventType).addAndGet(1) > 30) {
errorMap.get(eventType).set(0);
// TODO 微信报警
logger.doError(eventType + "发布事件错误次数超标!");
}
}
} else {
num = eventQueue.llen(eventType);
if (num < 1024) {
flagMap.get(eventType).set(true);
}
// logger.doInfo(eventType + "缓存池已满,进入临时替补队列!", null);
// TODO 任务进入NOSQL
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
public static void main(String[] args) throws InterruptedException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "applicationContext.xml" });
context.start();
final AdviceService service = context.getBean(AdviceService.class);
EventQueue queue = context.getBean(EventQueue.class);
queue.del("payplan_processing");
queue.del("withhold_processing");
new Thread() {
@Override
public void run() {
for (int i = 0; i < 1003; i++) {
// service.processEvent("withhold",
// UUID.randomUUID().toString());
service.processEvent("withhold", i + "");
if (i % 1000 == 0) {
try {
Thread.sleep(Math.abs(new Random().nextInt(100)));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}.start();
new Thread() {
@Override
public void run() {
for (int i = 0; i < 997; i++) {
// service.processEvent("payplan",
// UUID.randomUUID().toString());
service.processEvent("payplan", i + "");
if (i % 1000 == 0) {
try {
Thread.sleep(Math.abs(new Random().nextInt(100)));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}.start();
new MyThread(queue).start();
// context.close();
}
}
class MyThread extends Thread {
EventQueue queue;
public MyThread(EventQueue queue) {
this.queue = queue;
}
IUserFacade userFacade;
@Override
public void run() {
while (true) {
try {
Thread.sleep(20000l);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("payplan:" + queue.llen("payplan"));
System.out.println("payplan_processing:"
+ queue.llen("payplan_processing"));
System.out.println("withhold:" + queue.llen("withhold"));
System.out.println("withhold_processing:"
+ queue.llen("withhold_processing"));
}
}
}
redis处理类
package com.qb.loan.annotation;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import com.qianbao.redis.service.RedisClient;
/**
* 封装了对redis队列的访问
* @author qianll
*
*/
@Service
public class EventQueue {
@Resource
private RedisClient redisClient;
@Value("${redis.client.namespace}")
private String namespace;
/**
* 弹出列表中的第一个元素,无则返回null
* @param queueName
* @return
*/
public String lpop(String queueName){
String result = null;
try {
result = redisClient.lpop(namespace, queueName);
if(result != null && result.equals("nil")){
result = null;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return result;
}
/**
* 从列表头插入元素,返回插入后列表中的元素数
* @param queueName
* @param elements
* @return
*/
public Long lpush(String queueName,String... elements){
Long result = 0l;
try {
result = redisClient.lpush(namespace, queueName, elements);
} catch (Exception e) {
throw new RuntimeException(e);
}
return result;
}
/**
* 从列表头开始删除前count个value值
* @param key
* @param count
* @param value
* @return 删除的元素个数
*/
public Long lrem(String key,long count,String value){
Long result = 0l;
try{
result = redisClient.lrem(namespace, key, count, value);
}catch (Exception e) {
throw new RuntimeException(e);
}
return result;
}
/**
* 获取列表中元素的个数,如果列表不存在,返回0.如果key存在但非列表类型,则返回错误
* @param key
* @return
*/
public Long llen(String key){
Long result = 0l;
try{
result = redisClient.llen(namespace, key);
}catch (Exception e) {
throw new RuntimeException(e);
}
return result;
}
/**
* 删除缓存key
* @param key
* @return
*/
public Long del(String key){
Long result = 0l;
try{
result = redisClient.del(namespace, key);
}catch (Exception e) {
throw new RuntimeException(e);
}
return result;
}
}