业务场景:
订单支付成功时,订单信息需要给银行系统同步,由于不能保证银行返回信息的时间,所以使用队列进行同步,并且用spring的定时任务执行。
需求:
订单支付成功时立即执行一次同步任务,如果失败,则没半小时执行一次同步任务,直至同步成功。
问题:
spring定时任务定为半小时,订单成功支付时,无法立即执行任务,只能等0-30分钟后执行同步任务,所以需要做一个功能,在订单支付成功的时候,调用一次同步任务。
解决方案:
在服务启动时,初始化一个A线程,该线程每隔N秒扫描一次任务集合,集合中存放着某个任务,扫描到就执行任务。然后在订单支付成功时,在将订单放入队列后,接着将订单放入任务集合,由A线程扫描并执行。
支付成功后代码:
BizLogger.info("订单支付成功!!!"); Singleton.put(SpdbRateTask.class);
Singleton:
import java.util.concurrent.ConcurrentHashMap; public class Singleton { private static ConcurrentHashMap<String,Class> map = new ConcurrentHashMap<String,Class>(); public static ConcurrentHashMap<String,Class> getMap(){ return map; } public static void put(Class clazz){ map.put(clazz.getCanonicalName(), clazz); } }
初始化线程任务:
import java.util.Iterator; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import org.springframework.web.context.support.WebApplicationContextUtils; import com.jniu.app.task.ExcutTask; import com.jniu.app.util.Singleton; import com.jniu.bas.context.Context; import com.jniu.bas.log4j.logger.BizLogger; public class ProjectInit implements ServletContextListener { private ConcurrentHashMap<String, Class> map = Singleton.getMap(); /** * 初始化学校缓存 */ @Override public void contextInitialized(final ServletContextEvent sce) { // 初始化当前上下文 Context ctx = Context.createContext("currentContext", null); Context.pushCurrentContext(ctx); TimerTask task = new TimerTask() { @Override public void run() { Iterator<Map.Entry<String, Class>> it = map.entrySet().iterator(); ExcutTask t = null; try { Thread.sleep(3000);//任务不多,且不是很频繁,队列使用的是redis的list,为了防止定时任务执行时redis中还没有数据,这里延迟三秒 while (it.hasNext()) { Map.Entry<String, Class> entry = it.next(); BizLogger.info("项目任务"+entry.getValue().getCanonicalName()+"执行开始"); t = ((ExcutTask) WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext()).getBean(entry.getValue())); t.a(); BizLogger.info("项目任务"+t.getClass().getCanonicalName()+"执行完成"); it.remove(); } } catch (Exception e) { e.printStackTrace(); BizLogger.info("项目任务"+t.getClass().getCanonicalName()+"执行失败"); } } }; Timer timer = new Timer(); long delay = 0; long intevalPeriod = 10 * 1000;//每十秒检查一遍任务集合 timer.scheduleAtFixedRate(task, delay, intevalPeriod); } /** * 销毁工作 */ @Override public void contextDestroyed(ServletContextEvent sce) { // TODO Auto-generated method stub } }
spring定时任务:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import com.jniu.app.constans.Constans; import com.jniu.app.service.SpdbOrderService; import com.jniu.bas.log4j.logger.BizLogger; import com.jniu.bas.result.ResultBean; @Service public class SpdbRateTask implements ExcutTask{ @Autowired RedisTemplate<String, Object> redisTemplate; @Autowired SpdbOrderService spdbOrderService; public void a(){ String t = null; ResultBean resultBean = null; try { long size = redisTemplate.opsForList().size(Constans.SPDB_RATE_QUEUE); if(size>0){ for(int i=0;i<size;i++){ t = (String)redisTemplate.opsForList().rightPop(Constans.SPDB_RATE_QUEUE); if(t!=null){ resultBean = spdbOrderService.transDetailObtain(t); if(!resultBean.isSuccess()){ redisTemplate.opsForList().leftPush(Constans.SPDB_RATE_QUEUE, t); } } } } } catch (Exception e) { e.printStackTrace(); if(t!=null) redisTemplate.opsForList().leftPush(Constans.SPDB_RATE_QUEUE, t); BizLogger.info(resultBean.getMessage()); } } @Override public String toString() { return "SpdbRateTask-----name"; } }