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

=========================生产者线程从redis queue中读取消息,加入ringBuffer=================

=========================消息者线程从ringBuffer中消费消息,交由具体的eventHandler处理=========

事件类

package com.qb.loan.disruptor;

public class Event {
	//事件类型
	private String eventType;
	//事件key
	private String key;
	//事件value
	private Object value;
	public String getEventType() {
		return eventType;
	}
	public void setEventType(String type) {
		this.eventType = type;
	}
	public String getKey() {
		return key;
	}
	public void setKey(String key) {
		this.key = key;
	}
	public Object getValue() {
		return value;
	}
	public void setValue(Object value) {
		this.value = value;
	}
	@Override
	public String toString() {
		return "Event [eventType=" + eventType + ", key=" + key + ", value="
				+ value + "]";
	}
	
	
}

事件工厂类

package com.qb.loan.disruptor;

import com.lmax.disruptor.EventFactory;
/**
 * 事件工厂
 * @author qianll
 *
 */
public class DefaultEventFactory implements EventFactory<Event>{

	@Override
	public Event newInstance() {
		return new Event();
	}

}

disruptor核心组件类

package com.qb.loan.disruptor;

import java.lang.Thread.UncaughtExceptionHandler;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.EventTranslator;
import com.lmax.disruptor.ExceptionHandler;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.WorkHandler;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import com.qb.loan.disruptor.exception.ConsumerFailException;
import com.qb.loan.disruptor.exception.ConsumerSuccessException;
import com.zrj.platform.base.log.AppLogger;

/**
 * disruptor核心组件类
 * @author qianll
 *
 */
@Service
@Lazy(false)
public class DisruptorInitialization implements ApplicationContextAware{
	
	protected AppLogger logger = new AppLogger(DisruptorInitialization.class);
	
	@Resource
	private EventQueue eventQueue;
	
	private Map<String,EventHandler> eventHandlerMap;
	
	private Disruptor<Event> disruptor;
	
	private ApplicationContext applicationContext;
	
	private RingBuffer<Event> ringBuffer;
	
	private List<EventPublishThread> list = new ArrayList<EventPublishThread>();

	@PostConstruct
	public void init(){ 
		eventHandlerMap = applicationContext.getBeansOfType(EventHandler.class);
		logger.doInfo("获取事件处理机,个数为:", eventHandlerMap.size());
		disruptor = new Disruptor<Event>(new DefaultEventFactory(),1024,Executors.newFixedThreadPool(eventHandlerMap.size()), ProducerType.MULTI,new BlockingWaitStrategy());
		//异常处理
		disruptor.handleExceptionsWith(new ExceptionHandler(){

			@Override
			public void handleEventException(Throwable ex, long sequence,
					Object event) {				
				logger.doError(event.toString(),ex);
				if(ex instanceof ConsumerFailException){
					logger.doError("",ex);
					//TODO 记录到NO-SQL数据库中 判断是否需求重新消费
				}
				if(ex instanceof ConsumerSuccessException){
					logger.doError("",ex);
					//TODO 记录到NO-SQL数据库中 判断是否需求从执行队列中删除
				}
			}

			@Override
			public void handleOnStartException(Throwable ex) {
				logger.doError("",ex);
			}

			@Override
			public void handleOnShutdownException(Throwable ex) {
				logger.doError("",ex);
			}
			
		});
		//消费者
		@SuppressWarnings("unchecked")
		WorkHandler<Event>[] array = new WorkHandler[eventHandlerMap.size()];
		for(int i=0; i<eventHandlerMap.size();i++){
			array[i] = new WorkHandler<Event>(){
				@Override
				public void onEvent(Event event) throws Exception {
					String type = event.getEventType();
					eventHandlerMap.get(type).onEvent(event);
				}				
			};
		}
		disruptor.handleEventsWithWorkerPool(array);
		ringBuffer = disruptor.start();
		//生产者
		for(String eventType : eventHandlerMap.keySet()){
			EventPublishThread thread = new EventPublishThread(ringBuffer,eventQueue,eventType);
			thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler(){
				@Override
				public void uncaughtException(Thread t, Throwable e) {
					logger.doError(t.getName(),e);					
				}				
			});
			thread.start();
			logger.doInfo("生产者线程启动!", null);
			list.add(thread);
		}
	}
	
	@PreDestroy
	public void destroy(){
		for(EventPublishThread thread:list){
			thread.setRunning(false);
		}
		if(disruptor != null){
			disruptor.shutdown();
		}
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.applicationContext = applicationContext;
	}
	
	private class EventPublishThread extends Thread{

		private RingBuffer<Event> ringBuffer;
		
		private EventQueue eventQueue;
		
		private String eventType;
		
		private boolean running = true;
		
		private EventPublishThread(RingBuffer<Event> ringBuffer,EventQueue eventQueue,String eventType){
			this.ringBuffer = ringBuffer;
			this.eventQueue = eventQueue;
			this.eventType = eventType;
		}
		
		@Override
		public void run() {
			LoadingCache<Long,AtomicInteger> counter =
					CacheBuilder.newBuilder()
					.expireAfterAccess(2,TimeUnit.MINUTES)
					.build(new CacheLoader<Long,AtomicInteger>(){
						@Override
						public AtomicInteger load(Long minute) throws Exception {
							return new AtomicInteger(0);
						}						
					});
			while(running){
				String failKey = null;
				try{
					final String key = eventQueue.rpop(eventType);
//					logger.doInfo(eventType+"从队列中获取元素:", key);
					if(key != null){
						failKey = key;
						eventQueue.lpush(eventType+"_processing", key);
						ringBuffer.publishEvent(new EventTranslator<Event>(){
							@Override
							public void translateTo(Event event,long sequence) {
								event.setEventType(eventType);
								event.setKey(key);
							}							
						});
//						logger.doInfo(eventType+"向disruptor发布事件:", key);
						
					}else{
						//释放cpu,避免长期占用cpu,导致负载增加
						Thread.sleep(2000l);
					}
				}catch(Exception e){
					if(failKey != null){
						//TODO 记录到NO-SQL数据库中 判断是否需求重新消费
					}
					logger.doError(eventType,e);
					//限流处理,防止日志雪崩
					long currentMinutes = System.currentTimeMillis()/1000/60;
					try{
						//TODO微信报警
						if(counter.get(currentMinutes).incrementAndGet() > 10){
							logger.doError(eventType+"生产消息错误超标!");
							Thread.sleep(1000*60*5);
						}
					}catch(Exception ex){
						logger.doError(eventType,ex);
					}
				}
			}			
		}

		public void setRunning(boolean running) {
			this.running = running;
		}

		
	}
	
}

EventHandler接口类

package com.qb.loan.disruptor;
/**
 * 事件处理器
 * @author qianll
 *
 */
public interface EventHandler {
	void onEvent(Event event);
}

抽象事件处理类

package com.qb.loan.disruptor;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import javax.annotation.Resource;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.qb.loan.disruptor.exception.ConsumerFailException;
import com.qb.loan.disruptor.exception.ConsumerSuccessException;
import com.zrj.platform.base.log.AppLogger;

public abstract class AbstractEventHandler implements EventHandler {
	
	@Resource
	protected EventQueue eventQueue;
	
	private AppLogger logger = new AppLogger(AbstractEventHandler.class);
	
	LoadingCache<Long,AtomicInteger> counter =
			CacheBuilder.newBuilder()
			.expireAfterAccess(2,TimeUnit.MINUTES)
			.build(new CacheLoader<Long,AtomicInteger>(){
				@Override
				public AtomicInteger load(Long minute) throws Exception {
					return new AtomicInteger(0);
				}						
			});

	@Override
	public void onEvent(Event event) {
		try{
			processEvent(event);
		}catch(Exception e){
			logger.doError("",e);
			long currentMinutes = System.currentTimeMillis()/1000/60;
			try{
				if(counter.get(currentMinutes).incrementAndGet() > 10){
					logger.doError("消费事件错误次数超标!");
					//TODO 微信报警
				}
			}catch(Exception ex){
				ex.printStackTrace();
			}
			fail(event,e);
		}
		try{
			success(event);
		}catch(Exception e){
			throw new ConsumerSuccessException(e);
		}
	}
	
	abstract void processEvent(Event event);
	
	protected void success(Event event){
		long l = eventQueue.lrem(event.getEventType()+"_processing", 1, event.getKey());
//		logger.doInfo(event.getEventType()+"消息成功:"+event.getKey(),null);
	}
	
	protected void fail(Event event,Exception e){
		throw new ConsumerFailException(e);
	}

}

事件处理实现类

package com.qb.loan.disruptor;

import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.zrj.platform.base.log.AppLogger;

/**
 * 还款计划事件处理器
 * @author qianll
 *
 */
@Service("payplan")
public class PayplanEventHandler extends AbstractEventHandler {

	private AppLogger logger = new AppLogger(PayplanEventHandler.class);
	
	private AtomicInteger count = new AtomicInteger(0);
	
	@Override
	void processEvent(Event event) {
		// TODO 根据事件key查询相关信息
		// TODO ES中重建索引 or 其他触发动作
		if(count.incrementAndGet()%1000 ==0){
			logger.doInfo(event.getEventType()+"已消费"+count.get()+"个消息。", new Date());
		}
		if(count.get()>10000000){
			count.set(0);
		}
	}

}

事件处理实现类

package com.qb.loan.disruptor;

import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;

import org.springframework.stereotype.Service;

import com.zrj.platform.base.log.AppLogger;

/**
 * 代扣事件处理器
 * @author qianll
 *
 */
@Service("withhold")
public class WithholdEventHandler extends AbstractEventHandler {
	
	private AppLogger logger = new AppLogger(WithholdEventHandler.class);
	
	private AtomicInteger count = new AtomicInteger(0);

	@Override
	void processEvent(Event event) {
		// TODO 根据事件key查询相关信息
		// TODO ES中重建索引 or 其他触发动作
		if(count.incrementAndGet()%1000 ==0){
			logger.doInfo(event.getEventType()+"已消费"+count.get()+"个消息。", new Date());
		}
		if(count.get()>10000000){
			count.set(0);
		}
	}



}

redis处理类

package com.qb.loan.disruptor;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Value;
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 rpop(String queueName){
		String result = null;
		try {
			result = redisClient.rpop(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;
	 }
	 /**
	  * 删除缓存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;
	 }
}

异常处理类

package com.qb.loan.disruptor.exception;

public class ConsumerFailException extends RuntimeException{
	
	private static final long serialVersionUID = 1L;
	
	public ConsumerFailException(){}

	public ConsumerFailException(String message){
		super(message);
	}
	
	public ConsumerFailException(Exception ex){
		super(ex.getMessage());
	}
}
package com.qb.loan.disruptor.exception;

public class ConsumerSuccessException extends RuntimeException{

	private static final long serialVersionUID = 1L;
	
	public ConsumerSuccessException(String message){
		super(message);
	}
	
	public ConsumerSuccessException(Exception ex){
		super(ex.getMessage());
	}

}


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

猜你喜欢

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