=========================生产者线程从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());
}
}