很早就对task,queue有兴趣,今天总结一下,做个笔记。
一、对于多任务异步的项目中,task的作用很普遍,最近学习,和小试牛刀了一下,有一些感悟,做个笔记。
二、使用spring task,配置如下:
<bean id="orderTimeoutService"
class="com.haohao.order.service.order.impl.OrderTimeoutServiceImpl"
init-method="init" destroy-method="destroy">
<property name="handlers">
<map key-type="com.haohao.order.common.enums.TimeoutTypeEnum">
<entry key="ORDER" value-ref="orderTimeoutHandler" />
<entry key="REFUND" value-ref="refundTimeoutHandler" />
map>
property>
<property name="loadTimeoutTask" value="${timeout.task.is.load}"/>
bean>
<bean id="orderTimeoutHandler"
class="com.haohao.order.service.timeout.handler.OrderTimeoutHandler">
<property name="timeoutActions">
<map>
<entry key="wait_buyer_pay_deposit" value-ref="closeOrderAction" />
<entry key="wait_buyer_pay_all" value-ref="closeOrderAction" />
<entry key="wait_seller_send" value-ref="sellerSendGoodsAction" />
<entry key="wait_buyer_receive" value-ref="receiveGoodsAction" />
map>
property>
bean>
<bean id="refundTimeoutHandler"
class="com.haohao.order.service.timeout.handler.RefundTimeoutHandler">
<property name="timeoutActions">
<map>
<entry key="wait_seller_agree" value-ref="confirmRefundProtocolAction" />
<entry key="wait_buyer_modify" value-ref="closeRefundAction" />
<entry key="wait_buyer_send" value-ref="closeRefundAction" />
<entry key="wait_seller_receive" value-ref="refundAction" />
<entry key="wait_customer_service" value-ref="refundCustomerServiceAction" />
map>
property>
bean>
<task:scheduled-tasks scheduler="myScheduler">
<task:scheduled ref="orderTimeoutService" method="loadRecentlyTimeoutInfo" cron="${load.order.timeout.period}" />
task:scheduled-tasks>
<task:scheduler id="myScheduler" pool-size="5" />
Java代码:
/**
*
*/
package com.haohao.order.service.order.impl;
import java.util.List;
import java.util.Map;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.haohao.order.common.enums.TimeoutTypeEnum;
import com.haohao.order.common.util.BeanUtils;
import com.haohao.order.dal.timeout.OrderTimeoutInfoDAO;
import com.haohao.order.dal.timeout.OrderTimeoutInfoDO;
import com.haohao.order.service.order.OrderTimeoutService;
import com.haohao.order.service.timeout.handler.TimeoutHandler;
import com.haohao.order.service.timeout.model.TimeoutModel;
/**
* 功能描述:
*
* @author mandy.hu
*/
public class OrderTimeoutServiceImpl implements OrderTimeoutService {
private static final Logger logger = LoggerFactory.getLogger(OrderTimeoutServiceImpl.class);
@Resource
private OrderTimeoutInfoDAO orderTimeoutInfoDAO;
private DelayQueue timeoutQueue;
@Autowired(required = false)
private ExecutorService executorService;
private Map handlers;
private transient boolean running;
private boolean isLoadTimeoutTask;
protected void init() {
if(isLoadTimeoutTask) {
logger.info("初始化超时队列");
running = true;
//初始化一个阻塞队列
timeoutQueue = new DelayQueue();
startHandler();
}
}
//这个方法就是用来处理业务的,意思是查询超时的订单,并放在DelayQueue中。
@Override
public void loadRecentlyTimeoutInfo() {
if(isLoadTimeoutTask) {
logger.info("加载即将超时的信息...");
List orderTimeoutInfoDOs = orderTimeoutInfoDAO.queryRecentlyOrderTimeoutInfos();
logger.info("加载即将超时的信息...loadRecentlyTimeoutInfo orderTimeoutInfoDOs:{}",orderTimeoutInfoDOs);
List timeoutModels = BeanUtils.convertList(orderTimeoutInfoDOs, TimeoutModel.class);
if (CollectionUtils.isNotEmpty(timeoutModels)) {
timeoutQueue.addAll(timeoutModels);
}
logger.info("加载即将超时的信息...loadRecentlyTimeoutInfo timeoutQueue:{}",timeoutQueue);
}
}
@PreDestroy
protected void destroy() {
running = false;
}
private void startHandler() {
logger.info("启动超时监听线程");
new Thread(new Runnable() {
@Override
public void run() {
while (running) {
try {
//阻塞队列执行take()方法,删除队列顶部的一个对象,并返回删除的对象。这个方法是阻塞方法,如果队列中没有对象,这个线程将被阻塞,知道队列中有对象。
TimeoutModel timeoutModel = timeoutQueue.take();
//对象交给ExecutorService处理,ExecutorService是一个异步处理机制,相当于一个线程池,意思就把这个take()的对象委托给ExecutorService处理。
getExecutorService().execute(new TimeoutHandlerTask(timeoutModel));
} catch (InterruptedException e) {
logger.warn("处理超时失败", e);
e.printStackTrace();
} catch (Throwable e) {
e.printStackTrace();
}
}
}
}).start();
}
class TimeoutHandlerTask implements Runnable {
private TimeoutModel timeoutModel;
public TimeoutHandlerTask(TimeoutModel timeoutModel) {
this.timeoutModel = timeoutModel;
}
@Override
public void run() {
TimeoutHandler handler = handlers.get(timeoutModel.getTimeoutType());
if (handler != null) {
handler.onTimeout(timeoutModel);
}
}
}
public ExecutorService getExecutorService() {
if (executorService == null) {
//得到executorService 对象,其中Runtime.getRuntime()相当于new一个对象,、、Executors.newFixedThreadPool(xxx)最大执行多少个线程的executorService 。
executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()*2);
}
return executorService;
}
public void setExecutorService(ExecutorService executorService) {
this.executorService = executorService;
}
public Map getHandlers() {
return handlers;
}
public void setHandlers(Map handlers) {
this.handlers = handlers;
}
public void setLoadTimeoutTask(boolean isLoadTimeoutTask) {
this.isLoadTimeoutTask = isLoadTimeoutTask;
}
}
三:上面注释已经很清楚了,为了防止以后不认识自己写注释,在这里再系统的说一下流程。
<task:scheduled-tasks scheduler="myScheduler">
<task:scheduled ref="orderTimeoutService" method="loadRecentlyTimeoutInfo" cron="${load.order.timeout.period}" />
task:scheduled-tasks>
<task:scheduler id="myScheduler" pool-size="5" />
其中ref=”orderTimeoutService”是要执行的类,method=”loadRecentlyTimeoutInfo”是要执行这个类中的方法,cron=”${load.order.timeout.period}” 是执行规则多长时间执行,什么周期执行,都可以灵活设置。
<bean id="orderTimeoutService"
class="com.haohao.order.service.order.impl.OrderTimeoutServiceImpl"
init-method="init" destroy-method="destroy">
<property name="handlers">
<map key-type="com.haohao.order.common.enums.TimeoutTypeEnum">
<entry key="ORDER" value-ref="orderTimeoutHandler" />
<entry key="REFUND" value-ref="refundTimeoutHandler" />
map>
property>
<property name="loadTimeoutTask" value="${timeout.task.is.load}"/>
bean>
<bean id="orderTimeoutHandler"
class="com.haohao.order.service.timeout.handler.OrderTimeoutHandler">
<property name="timeoutActions">
<map>
<entry key="wait_buyer_pay_deposit" value-ref="closeOrderAction" />
<entry key="wait_buyer_pay_all" value-ref="closeOrderAction" />
<entry key="wait_seller_send" value-ref="sellerSendGoodsAction" />
<entry key="wait_buyer_receive" value-ref="receiveGoodsAction" />
map>
property>
bean>
<bean id="refundTimeoutHandler"
class="com.haohao.order.service.timeout.handler.RefundTimeoutHandler">
<property name="timeoutActions">
<map>
<entry key="wait_seller_agree" value-ref="confirmRefundProtocolAction" />
<entry key="wait_buyer_modify" value-ref="closeRefundAction" />
<entry key="wait_buyer_send" value-ref="closeRefundAction" />
<entry key="wait_seller_receive" value-ref="refundAction" />
<entry key="wait_customer_service" value-ref="refundCustomerServiceAction" />
map>
property>
bean>
OrderTimeoutServiceImpl类中有两个方法init-method=”init” destroy-method=”destroy”,一个是这个类加载时就会执行的,另一个是这个类销毁时执行的,init-method方法做一些初始化操作。在这个类中有一个属性,是一个map,名字是handlers,handlers中有初始化了两个对象,本文重点不是这里,就不多赘述了
protected void init() {
if(isLoadTimeoutTask) {
logger.info("初始化超时队列");
running = true;
timeoutQueue = new DelayQueue();
startHandler();
}
}
这个方法很简单就是new一个阻塞队列DelayQueue。之后执行startHandler()方法,
private void startHandler() {
logger.info("启动超时监听线程");
new Thread(new Runnable() {
@Override
public void run() {
while (running) {
try {
TimeoutModel timeoutModel = timeoutQueue.take();
getExecutorService().execute(new TimeoutHandlerTask(timeoutModel));
} catch (InterruptedException e) {
logger.warn("处理超时失败", e);
e.printStackTrace();
} catch (Throwable e) {
e.printStackTrace();
}
}
}
}).start();
}
这个方法目的是开起监听,利用的是DelayQueue阻塞队列的特性,方法中调用take()方法,这个方法是阻塞的方法,意思是移除队列中顶部的对象,并返回这个对象,如果队列是空,就会一直等待,等待队列中有对象,线程也处于阻塞的状态,同事就起到了监听的作用。getExecutorService().execute(new TimeoutHandlerTask(timeoutModel))这个方法是把timeoutModel 对象委托给ExecutorService()处理,这边好处理下一个任务。
public ExecutorService getExecutorService() {
if (executorService == null) {
executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()*2);
}
return executorService;
}
这个就是得到ExecutorService对象, ExecutorService是一个异步处理机制,Executors.newFixedThreadPool(Num);最大能处理Num个线程的ExecutorService。调用ExecutorService的execute()方法就是委托了。
public void loadRecentlyTimeoutInfo() {
if(isLoadTimeoutTask) {
logger.info("加载即将超时的信息...");
List orderTimeoutInfoDOs = orderTimeoutInfoDAO.queryRecentlyOrderTimeoutInfos();
logger.info("加载即将超时的信息...loadRecentlyTimeoutInfo orderTimeoutInfoDOs:{}",orderTimeoutInfoDOs);
List timeoutModels = BeanUtils.convertList(orderTimeoutInfoDOs, TimeoutModel.class);
if (CollectionUtils.isNotEmpty(timeoutModels)) {
timeoutQueue.addAll(timeoutModels);
}
logger.info("加载即将超时的信息...loadRecentlyTimeoutInfo timeoutQueue:{}",timeoutQueue);
}
}
这个方法就是在spring task中配置要执行的方法,刚开始的时候一直在想在哪里有数据放在了DelayQueue中,就是这里,将想要监听的数据放在DelayQueue中,在startHandler()方法中就能监听到,这是task就是一个闭环了。