@SuppressWarnings({ "rawtypes", "unchecked" })
@ManagedResource(objectName = QueuesHolder.QUEUEHOLDER_MBEAN_NAME, description = "Queues Holder Bean")
public class QueuesHolder {
/**
* QueueManager注册的名称.
*/
public static final String QUEUEHOLDER_MBEAN_NAME = "Core:type=QueueManagement,name=queueHolder";
// private static Log logger = LogFactory.getLog(QueuesHolder.class);//初始化需要,勿删
private static ConcurrentMap<String, BlockingQueue> queueMap = new MapMaker().concurrencyLevel(32).makeMap();// 消息队列
/**
* 根据提供的queueName 入队列
*
* @param queueName
* @param object
* @return
*/
public static void add(String queueName, int queueSize, Object object) {
BlockingQueue bq = getQueue(queueName, queueSize);
try {
bq.add(object); // bq.offer(object);(这里也可以使用offer,offer不会抛出异常)
LogUtil.debug("put Log to queue【{}】,msg={}", queueName, object);
} catch (IllegalStateException e) {
LogUtil.error("加入队列失败:队列【{}】size【{}】满了!object=", queueName, queueSize, object, e);
} catch (Exception e) {
LogUtil.error("加入队列失败:队列【{}】size【{}】逻辑异常!object={}", queueName, queueSize, object, e);
}
}
/**
* 根据queueName获得消息队列的静态函数. 如消息队列还不存在, 会自动进行创建.
*/
public static <T> BlockingQueue<T> getQueue(String queueName, int queueSize) {
BlockingQueue queue = queueMap.get(queueName);
if (queue == null) {
queue = new LinkedBlockingQueue(queueSize);
queueMap.put(queueName, queue);
}
return queue;
}
/**
* 根据queueName获得消息队列中未处理消息的数量,支持基于JMX查询.
*/
@ManagedOperation(description = "Get message count in queue")
@ManagedOperationParameters({ @ManagedOperationParameter(name = "queueName", description = "Queue name") })
public static int getQueueLength(String queueName, int queueSize) {
return getQueue(queueName, queueSize).size();
}
/**
* add 增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常 remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常 element
* 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常 offer 添加一个元素并返回true 如果队列已满,则返回false poll 移除并返问队列头部的元素
* 如果队列为空,则返回null peek 返回队列头部的元素 如果队列为空,则返回null put 添加一个元素 如果队列满,则阻塞 take 移除并返回队列头部的元素 如果队列为空,则阻塞
*/
}
消费:
@SuppressWarnings("rawtypes")
public abstract class QueueConsumer implements Runnable {
protected Logger logger = LoggerFactory.getLogger(getClass());
protected String queueName = "log";//任务所消费的队列名称
protected int queueSize = Integer.MAX_VALUE;//每个队列的最大长度, 默认为Integer最大值, 设置时不改变已创建队列的最大长度.
protected int batchSize = 10;//批量读取事件数量, 默认为10.
protected int shutdownTimeout = Integer.MAX_VALUE;
protected BlockingQueue queue;
protected ListeningExecutorService executor;
/**
* 启动并执行队列
*/
public void start() {
logger.debug("启动批量入库任务并执行队列 queueName[{}] queueSize[{}]",queueName,queueSize);
try {
queue = QueuesHolder.getQueue(queueName, queueSize);
executor = MoreExecutors.listeningDecorator(new ThreadPoolExecutor(2,4,10,TimeUnit.MINUTES,new ArrayBlockingQueue<Runnable>(10),new CustomizableThreadFactory("Queue Consumer-" + queueName)));
executor.execute(this);
logger.debug("启动批量入库任务成功");
} catch (Exception e) {
logger.error("批量入库任务队列消费初始化失败!",e);
}
}
/**
* 停止任务
*/
@PreDestroy
public void stop() throws IOException {
try {
ThreadUtils.normalShutdown(executor, shutdownTimeout, TimeUnit.MILLISECONDS);
} finally {
// TODO
}
}
/**
* 初始化
*/
protected abstract void init();
/**
* 设置任务所消费的队列名称.默认为log
*/
public void setQueueName(String queueName) {
this.queueName = queueName;
}
/**
* 设置每个队列的最大长度. 默认为Integer最大值, 设置时不改变已创建队列的最大长度.
*/
public void setQueueSize(int queueSize) {
this.queueSize = queueSize;
}
/**
* 批量读取事件数量. 默认为10.
*/
public void setBatchSize(int batchSize) {
this.batchSize = batchSize;
}
/**
* 停止任务时最多等待的时间, 单位为毫秒.
*/
public void setShutdownTimeout(int shutdownTimeout) {
this.shutdownTimeout = shutdownTimeout;
}
}
public abstract class BlockingConsumer extends QueueConsumer {
/**
* 线程执行函数,阻塞获取消息并调用processMessage()进行处理.
*/
public void run() {
Object message = null;
// 循环阻塞获取消息直到线程被中断.
try {
logger.debug("BlockingConsumer threadName:{},status={}", Thread.currentThread().getName(), Thread.currentThread().isInterrupted());
while (!Thread.currentThread().isInterrupted()) {
message = queue.take();
processMessage(message);
}
} catch (InterruptedException e) {
logger.error("消费线程阻塞被中断");
} finally {
// 退出线程前调用清理函数.
clean();
}
}
/**
* 消息处理
*/
protected abstract void processMessage(Object message);
/**
* 退出清理
*/
protected abstract void clean();
}
具体消费消费类:日志批量入库(日志添加进队列另外写一个就行)
@Component("accessLogWriter")
public class AccessLogWriter extends BlockingConsumer {
protected List<AccessLogDTO> eventsBuffer;
@Autowired
private SSCommonDAO ssCommonDAO;
@Value("${queue.queueName.accessLog}")
private String queueNameP; // 队列名
@Value("${queue.queueSize.accessLog}")
private int queueSizeP; // 队列大小 100
@Value("${queue.batchSize.accessLog}")
private int batchSizeP;// 批量读取事件数量 10
@Override
@PostConstruct
public void init(){
setQueueName(queueNameP);
setQueueSize(queueSizeP);
setBatchSize(batchSizeP);
eventsBuffer = Collections.synchronizedList(new ArrayList<AccessLogDTO>(batchSizeP));
start();
}
/**
* 消息处理函数,将消息放入buffer,当buffer达到batchSize时执行批量更新函数.
*/
@Override
protected void processMessage(Object message) {
AccessLogDTO event = (AccessLogDTO) message;
eventsBuffer.add(event);
LogUtil.debug("has event[size:{}]: {}", eventsBuffer.size(), event);
// 已到达BufferSize则执行批量插入操作
if (eventsBuffer.size() >= batchSize) {
insertBatch();
}
if (queue.size()==0&&eventsBuffer.size()>0){
insertBatch();
}
}
/**
* 将Buffer中的事件列表批量插入数据库.
*/
private void insertBatch() {
LogUtil.debug("达到上限{},执行批量入库操作", eventsBuffer.size());
try {
ssCommonDAO.insert(LoginInterceptorUtil.getAccessLogBatchSql(), eventsBuffer);
} catch (Exception e) {
LogUtil.error("批量提交任务时发生错误.", e);
} finally {
// 清除已完成的Buffer
eventsBuffer.clear();
}
}
/**
* 退出清理函数,完成buffer中未完成的消息.
*/
@Override
protected void clean() {
if (!eventsBuffer.isEmpty()) {
insertBatch();
}
LogUtil.debug("cleaned task {}", this);
}
public List<AccessLogDTO> getEventsBuffer() {
return eventsBuffer;
}
}