高吞吐高性能异步消息处理系统(一)

依赖的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;
	 }
}

发布了177 篇原创文章 · 获赞 14 · 访问量 47万+

猜你喜欢

转载自blog.csdn.net/qian_348840260/article/details/80655308