机票预定系统类图_电商系统延时任务机制源码分享
需求分析:
在javashop電商系統中,各種促銷活動都有開始時間和結束時間,想要讓一個活動在預定的時間開始或結束,使用定時任務輪詢,存在耗性能并且不能在準確的時間點開始或結束的缺點,為了可以在指定的時間執行,要求使用延時任務
思路:
延時任務:指定某日期執行某自定義任務
思路為采用Rabbitmq中的死信轉移隊列的技術點實現。
第一步向一個隊列(具有xxxx屬性)發送消息,這個隊列的消息可以指定失效時間
當失效發生時rabbbitmq會將此消息轉移到另外的一個普通對列中,此時立刻被消費了,以此實現任務的延遲執行。
AMQP 延時任務核心類圖
TimeTrigger 觸發器接口,對外提供定義延遲任務的接口,調用者直接面向此接口。
目前只實現了基于RabbitMq的實現,如果有其他延時任務實現(如基于redis),面向此接口開發即可,定義新增、編輯、刪除任務操作。
RabbitmqTimeTrigger
基于rabbitmq延時任務實現
TimeTriggerConfig,rabbitmq配置
TimeTriggerMsg,rabbitmq延時任務消息
執行器類圖
TimeTriggerConsumer 延時任務消費者,負責延時任務的調用
TimeTriggerExecuter 延時任務執行器接口,自定義延時任務需要實現此接口
PintuanTimeTriggerExecuter 以拼團業務為例,延時任務執行的實現。
新增任務時序圖
步驟說明:
1、新增延時任務,指定延時任務所需的參數(執行器beanName,執行器參數,執行日期,執行任務標識KEY)
2、rabbitmq發送消息,將執行器以及參數封裝
3、寫入redis,標識任務需要執行
4、mq監聽 指定時間任務
5、消費者獲取redis的任務標識
7、進行標識判斷,如果判斷無效,則不執行任務,return
8、如果任務標識有效,則通過springbean容器獲取執行器,執行execute方法
編輯任務流程圖
步驟說明:
1、編輯延時任務,指定延時任務所需的參數(執行器,執行器參數,執行日期,執行任務標識KEY)
2、刪除redis中的任務標識,代表任務取消
3、rabbitmq發送消息,將執行器以及參數封裝
4、寫入redis,標識任務需要執行
5、mq監聽 指定時間任務
7、消費者獲取redis的任務標識
8、進行標識判斷,如果判斷無效,則不執行任務,return
9、如果任務標識有效,則通過springbean容器獲取執行器,執行execute方法
刪除任務流程圖
步驟說明:
1、刪除延時任務,參數(執行任務標識KEY)
2、刪除redis中的任務標識,代表任務取消
源碼
TimeTriggerConsumer 延時任務消費者,負責延時任務的調用
package com.enation.app.javashop.framework.trigger.rabbitmq;import com.enation.app.javashop.framework.cache.Cache;import com.enation.app.javashop.framework.trigger.Interface.TimeTrigger;import com.enation.app.javashop.framework.trigger.rabbitmq.model.TimeTriggerMsg;import com.enation.app.javashop.framework.trigger.util.RabbitmqTriggerUtil;import com.enation.app.javashop.framework.util.DateUtil;import com.enation.app.javashop.framework.util.StringUtil;import com.enation.app.javashop.framework.logs.Logger;import com.enation.app.javashop.framework.logs.LoggerFactory;import org.springframework.amqp.rabbit.core.RabbitTemplate;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;/** * 延時任務生產 rabbitmq實現 * @Description: 原理:利用amqp的死信隊列的超時屬性,將超時的任務轉到普通隊列交給消費者執行。* 添加任務,將任務執行標識、beanid、執行時間,hash值存入redis,標識任務需要執行
* 添加刪除,刪除redis中的任務標識,消費者執行時獲取不到 redis中的標識,則不會執行延時任務 *
*/@Componentpublic class RabbitmqTimeTrigger implements TimeTrigger { /** * 引入rabbit的操作模板 */ @Autowired private RabbitTemplate rabbitTemplate; @Autowired private Cache cache; private final Logger logger = LoggerFactory.getLogger(getClass()); /** * 添加延時任務 * * @param executerName 執行器 * @param param 執行參數 * @param triggerTime 執行時間 * @param uniqueKey 如果是一個 需要有 修改/取消 延時任務功能的延時任務,
* 請填寫此參數,作為后續刪除,修改做為唯一憑證
* 建議參數為:PINTUAZN_{ACTIVITY_ID} 例如 pintuan_123
* 業務內全局唯一 */ @Override public void add(String executerName, Object param, Long triggerTime, String uniqueKey) { if (StringUtil.isEmpty(uniqueKey)) { uniqueKey = StringUtil.getRandStr(10); } //標識任務需要執行 cache.put(RabbitmqTriggerUtil.generate(executerName, triggerTime, uniqueKey), 1); TimeTriggerMsg timeTriggerMsg = new TimeTriggerMsg(executerName, param, triggerTime, uniqueKey); logger.debug("定時執行在【" + DateUtil.toString(triggerTime, "yyyy-MM-dd HH:mm:ss") + "】,消費【" + param.toString() + "】"); rabbitTemplate.convertAndSend(TimeTriggerConfig.DELAYED_EXCHANGE_XDELAY, TimeTriggerConfig.DELAY_ROUTING_KEY_XDELAY, timeTriggerMsg, message -> { Long current = DateUtil.getDateline(); //如果執行的延時任務應該是在現在日期之前執行的,那么補救一下,要求系統一秒鐘后執行 if (triggerTime < current) { message.getMessageProperties().setDelay(1000); } else { Long time = (triggerTime - current) * 1000 + 5000 ; message.getMessageProperties().setHeader("x-delay", time); } logger.debug("還有【" + message.getMessageProperties().getExpiration() + "】執行任務"); return message; }); } /** * 修改延時任務 * * @param executerName 執行器 * @param param 執行參數 * @param triggerTime 執行時間 * @param uniqueKey 添加任務時的唯一憑證 */ @Override public void edit(String executerName, Object param, Long oldTriggerTime, Long triggerTime, String uniqueKey) { //標識任務放棄 cache.remove(RabbitmqTriggerUtil.generate(executerName, oldTriggerTime, uniqueKey)); //重新添加任務 this.add(executerName, param, triggerTime, uniqueKey); } /** * 刪除延時任務 * * @param executerName 執行器 * @param triggerTime 執行時間 * @param uniqueKey 添加任務時的唯一憑證 */ @Override public void delete(String executerName, Long triggerTime, String uniqueKey) { cache.remove(RabbitmqTriggerUtil.generate(executerName, triggerTime, uniqueKey)); }}
TimeTriggerExecuter 延時任務執行器接口,自定義延時任務需要實現此接口
package com.enation.app.javashop.framework.trigger.Interface;/** * 延時任務執行器接口 * */public interface TimeTriggerExecuter { /** * 執行任務 * @param object 任務參數 */ void execute(Object object);}PintuanTimeTriggerExecuter 以拼團業務為例,延時任務執行的實現。
package com.enation.app.javashop.consumer.shop.trigger;import com.enation.app.javashop.core.base.message.PintuanChangeMsg;import com.enation.app.javashop.core.base.rabbitmq.TimeExecute;import com.enation.app.javashop.core.promotion.pintuan.model.Pintuan;import com.enation.app.javashop.core.promotion.pintuan.model.PintuanOptionEnum;import com.enation.app.javashop.core.promotion.pintuan.service.PintuanManager;import com.enation.app.javashop.core.promotion.tool.model.enums.PromotionStatusEnum;import com.enation.app.javashop.framework.trigger.Interface.TimeTrigger;import com.enation.app.javashop.framework.trigger.Interface.TimeTriggerExecuter;import com.enation.app.javashop.framework.logs.Logger;import com.enation.app.javashop.framework.logs.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;/** * 拼團定時開啟關閉活動 延時任務執行器 * * @author Chopper * @version v1.0 * @since v7.0 * 2019-02-13 下午5:34 */@Component("pintuanTimeTriggerExecute")public class PintuanTimeTriggerExecuter implements TimeTriggerExecuter { @Autowired private TimeTrigger timeTrigger; @Autowired private PintuanManager pintuanManager; private final Logger logger = LoggerFactory.getLogger(getClass()); /** * 執行任務 * * @param object 任務參數 */ @Override public void execute(Object object) { PintuanChangeMsg pintuanChangeMsg = (PintuanChangeMsg) object; //如果是要開啟活動 if (pintuanChangeMsg.getOptionType() == 1) { Pintuan pintuan = pintuanManager.getModel(pintuanChangeMsg.getPintuanId()); if (PromotionStatusEnum.WAIT.name().equals(pintuan.getStatus()) || (PromotionStatusEnum.END.name().equals(pintuan.getStatus()) && PintuanOptionEnum.CAN_OPEN.name().equals(pintuan.getOptionStatus()))) { pintuanManager.openPromotion(pintuanChangeMsg.getPintuanId()); //開啟活動后,立馬設置一個關閉的流程 pintuanChangeMsg.setOptionType(0); timeTrigger.add(TimeExecute.PINTUAN_EXECUTER, pintuanChangeMsg, pintuan.getEndTime(), "{TIME_TRIGGER}_" + pintuan.getPromotionId()); this.logger.debug("活動[" + pintuan.getPromotionName() + "]開始,id=[" + pintuan.getPromotionId() + "]"); } } else { //拼團活動結束 Pintuan pintuan = pintuanManager.getModel(pintuanChangeMsg.getPintuanId()); if (pintuan.getStatus().equals(PromotionStatusEnum.UNDERWAY.name())) { pintuanManager.closePromotion(pintuanChangeMsg.getPintuanId()); } this.logger.debug("活動[" + pintuan.getPromotionName() + "]結束,id=[" + pintuan.getPromotionId() + "]"); } }} 與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的机票预定系统类图_电商系统延时任务机制源码分享的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vsc提示只有一行_Solution:连
- 下一篇: 安卓点击图片跳转界面_安卓手机APP控制