天天看點

debug error怎麼解決_分布式事務的解決方案

debug error怎麼解決_分布式事務的解決方案

一. 跨庫事務解決方案

使用

二階段送出(try-commit/rollback)

的方式:

debug error怎麼解決_分布式事務的解決方案

二. 微服務解決方案

1. TCC(try - confirm/cancel)二階段送出方案——追求強一緻性方案
debug error怎麼解決_分布式事務的解決方案
2. TCC 二階段柔性方案
debug error怎麼解決_分布式事務的解決方案
3. 使用日志輪詢的方案
debug error怎麼解決_分布式事務的解決方案

訂單類 :

@Service(value = "orderServiceImpl")
public class OrderServiceImpl extends ServersManager<Order, OrderDao> implements
        OrderService {
	private static final org.slf4j.Logger logger = LoggerFactory
			.getLogger(OrderServiceImpl.class);
    @Resource(name="orderDaoFront")
    @Override
    public void setDao(OrderDao orderDao) {
        this.dao = orderDao;
    }

    @Autowired
	private OrderdetailDao orderdetailDao;
    @Autowired
	private OrderpayDao orderpayDao;
    @Autowired
	private OrdershipDao ordershipDao;
    @Autowired
	private OrderlogDao orderlogDao;
    @Autowired
	private ProductService productService;
    @Autowired
	private AccountService accountService;
   

	public void setAccountService(AccountService accountService) {
		this.accountService = accountService;
	}

	public void setProductService(ProductService productService) {
		this.productService = productService;
	}

	public void setOrderpayDao(OrderpayDao orderpayDao) {
		this.orderpayDao = orderpayDao;
	}

	public void setOrderlogDao(OrderlogDao orderlogDao) {
		this.orderlogDao = orderlogDao;
	}

	public void setOrdershipDao(OrdershipDao ordershipDao) {
		this.ordershipDao = ordershipDao;
	}

	public void setOrderdetailDao(OrderdetailDao orderdetailDao) {
		this.orderdetailDao = orderdetailDao;
	}

	public Order createOrder(Order order, List<Orderdetail> orderdetailList, Ordership ordership)
			throws Exception {
		if(order==null || orderdetailList==null || orderdetailList.size()==0 || ordership==null){
			throw new NullPointerException("參數不能為空!");
		}
		//建立訂單
		int orderID = dao.insert(order);
		logger.debug("orderID=" + orderID);
		
		//建立訂單項
		for (int i = 0; i < orderdetailList.size(); i++) {
			Orderdetail orderdetail = orderdetailList.get(i);
			orderdetail.setOrderID(orderID);
			orderdetailDao.insert(orderdetail);
		}
		
		//建立支付記錄對象
		Orderpay orderpay = new Orderpay();
		orderpay.setOrderid(order.getId());
		orderpay.setPaystatus(Orderpay.orderpay_paystatus_n);
		orderpay.setPayamount(Double.valueOf(order.getAmount()));
		orderpay.setPaymethod(Orderpay.orderpay_paymethod_alipayescow);
		int orderpayID = orderpayDao.insert(orderpay);
		logger.error("orderpayID="+orderpayID);
		order.setOrderpayID(String.valueOf(orderpayID));
		
		//記錄配送資訊
		ordership.setOrderid(String.valueOf(orderID));
		ordershipDao.insert(ordership);
		
		//記錄訂單建立日志
		Orderlog orderlog = new Orderlog();
		orderlog.setOrderid(String.valueOf(orderID));
		orderlog.setAccount(order.getAccount());
		orderlog.setContent("【建立訂單】使用者建立訂單。訂單總金額:"+order.getAmount());
		orderlog.setAccountType(Orderlog.orderlog_accountType_w);
		orderlogDao.insert(orderlog);
		Order orderData=new Order();
		orderData.setId(String.valueOf(orderID));
		orderData.setOrderpayID(order.getOrderpayID());
		return orderData;
	}
    
    @Autowired
    public MessageService messageService;
	
	public Order createMiaoshaOrder(Order order) {
		if(order==null){
			throw new RuntimeException("訂單參數不能為空!");
		}
		List<Orderdetail> orderdetailList = order.getOrderdetail();
		Ordership ordership = order.getOrdership();
		if(orderdetailList==null || orderdetailList.size()==0 || ordership==null){
			throw new RuntimeException("訂單明細或位址參數不能為空!");
		}
		
		//建立訂單
		int orderID  = dao.insert(order);
		logger.debug("orderID=" + orderID);
        
        //使用mq的方式解決分布式事務
        Message message = new Message(orderID,"",productId,"init");
        messageService.receiveMsg(message);
        
        
		
		//建立訂單明細
		for (int i = 0; i < orderdetailList.size(); i++) {
			Orderdetail orderdetail = orderdetailList.get(i);
			orderdetail.setOrderID(orderID);
			orderdetailDao.insert(orderdetail);
		}
		
		//建立支付記錄對象
		Orderpay orderpay = new Orderpay();
		orderpay.setOrderid(order.getId());
		orderpay.setPaystatus(Orderpay.orderpay_paystatus_n);
		orderpay.setPayamount(Double.valueOf(order.getAmount()));
		orderpay.setPaymethod(Orderpay.orderpay_paymethod_alipayescow);
		int orderpayID = orderpayDao.insert(orderpay);
		logger.error("orderpayID="+orderpayID);
		order.setOrderpayID(String.valueOf(orderpayID));
		
		//記錄配送資訊
		ordership.setOrderid(String.valueOf(orderID));
		ordershipDao.insert(ordership);
		
		//記錄訂單建立日志
		Orderlog orderlog = new Orderlog();
		orderlog.setOrderid(String.valueOf(orderID));
		orderlog.setAccount(order.getAccount());
		orderlog.setContent("【建立訂單】使用者建立訂單。訂單總金額:"+order.getAmount());
		orderlog.setAccountType(Orderlog.orderlog_accountType_w);
		orderlogDao.insert(orderlog);
		
        
        //TODO 插入日志 log(orderId,productId,deductStockCount,status)
        //定時任務 輪詢執行程式,從日志裡擷取該訂單資訊,往庫存庫裡操作,如果插入成功,定時任務就不需要繼續執行,如果不成功,繼續執行。
        
		//減庫存,使用分布式事務後減庫存邏輯需交給消息中心處理,這塊邏輯需要注釋掉
		for (int i = 0; i < orderdetailList.size(); i++) {
			String productID = String.valueOf(orderdetailList.get(i).getProductID());
			//資料庫減庫存
			Product product = new Product();
			product.setId(productID);
			//TODO 插入日志 log(orderId,productId,deductStockCount,status)
			int updateNums = productService.updateStockAfterMiaoshaSuccess(product);
			if (updateNums <= 0) {
				throw new ProductSoldOutException("更新庫存失敗");
			}
		}
		
		Order orderData=new Order();
		orderData.setId(String.valueOf(orderID));
		orderData.setOrderpayID(order.getOrderpayID());
        
        
        //發送确認消息
        //使用mq的方式解決分布式事務
        Message message = new Message(orderID,"",productId,"sent");
        messageService.receiveMsg(message);
        
        
		return orderData;
	}

	@Override
	public List<Order> selectOrderInfo(Order order) {
		return dao.selectOrderInfo(order);
	}

//	@Override
//	public boolean updateOrderStatus(Order order) {
//		if(order==null){
//			throw new NullPointerException("參數不能為空!");
//		}
//
//		Orderpay orderpay = orderpayDao.selectById(order.getOrderpayID());
//		if(orderpay==null){
//			throw new NullPointerException("根據支付記錄号查詢不到支付記錄資訊!");
//		}
//		String orderid = orderpay.getOrderid();//訂單ID
//		
//		//更新支付記錄為成功支付
//		Orderpay orderpay2 = new Orderpay();
//		orderpay2.setId(order.getOrderpayID());
//		orderpay2.setTradeNo(order.getTradeNo());
//		orderpay2.setPaystatus(Orderpay.orderpay_paystatus_y);
//		orderpayDao.update(orderpay2);
//		
//		//更新訂單的支付狀态為成功支付
//		order.setId(orderid);
//		order.setPaystatus(Order.order_paystatus_y);
//		dao.update(order);
//		return true;
//	}
	
	@Override
	public boolean alipayNotify(String trade_status,String refund_status,String out_trade_no,String trade_no) {
		try {
			return alipayNotify0(trade_status, refund_status, out_trade_no, trade_no);
		} catch (Exception e) {
			logger.error(">>>alipayNotify...Exception..");
			e.printStackTrace();
			return false;
		}
	}
	
	private boolean alipayNotify0(String trade_status,String refund_status,String out_trade_no,String trade_no) {
		synchronized (FrontContainer.alipay_notify_lock) {
			logger.error("trade_status = " + trade_status + ",refund_status = " + refund_status + ",out_trade_no = " + out_trade_no + ",trade_no = " + trade_no);
			if ((StringUtils.isBlank(trade_status)
					&& StringUtils.isBlank(refund_status)) || (StringUtils.isBlank(out_trade_no)
							&& StringUtils.isBlank(trade_no))) {
				logger.error("請求非法!");
				return false;
			}
			String orderpayID = null;
			if(out_trade_no.startsWith("test")){
				//此處做一個說明,localhost或127.0.0.1下的訂單的請求發給支付寶的商戶訂單号都是test開頭的,正式的都是非test開頭的。
				//可以參見OrdersAction.createPayInfo()方法。
				orderpayID = out_trade_no.substring(4);
			}else{
				orderpayID = out_trade_no;
			}
			logger.error("orderpayID = " + orderpayID);
			Orderpay orderpay = orderpayDao.selectById(orderpayID);
			if(orderpay==null){
				throw new NullPointerException("根據支付記錄号查詢不到支付記錄資訊!");
			}
			String orderid = orderpay.getOrderid();//訂單ID
			String content = null;
			
			if(StringUtils.isNotBlank(refund_status)){
				/**
				 * 退款流程
				 */
				if(refund_status.equals("WAIT_SELLER_AGREE")){//等待賣家同意退款	==>賣家需處理
					content = "【支付寶異步通知-->退款流程】等待賣家同意退款(WAIT_SELLER_AGREE)。";
					
				}else if(refund_status.equals("WAIT_BUYER_RETURN_GOODS")){//賣家同意退款,等待買家退貨	==>通知買家退貨,此 可以發站内信、短信、或郵件 通知對方
					content = "【支付寶異步通知-->退款流程】退款協定達成,等待買家退貨(WAIT_BUYER_RETURN_GOODS)。";
					
				}else if(refund_status.equals("WAIT_SELLER_CONFIRM_GOODS")){//買家已退貨,等待賣家收到退貨	==>支付寶會通知賣家
					content = "【支付寶異步通知-->退款流程】等待賣家收貨(WAIT_SELLER_CONFIRM_GOODS)。";
					
				}else if(refund_status.equals("REFUND_SUCCESS")){//賣家收到退貨,退款成功,交易關閉	==>賣家登陸支付寶,确認OK。
					//http://club.alipay.com/simple/?t9978565.html
					content = "【支付寶異步通知-->退款流程】退款成功(REFUND_SUCCESS)。";
					
				}else if(refund_status.equals("REFUND_CLOSED")){//賣家收到退貨,退款成功,交易關閉	==>賣家登陸支付寶,确認OK。
					//http://club.alipay.com/simple/?t9978565.html
					content = "【支付寶異步通知-->退款流程】退款關閉(REFUND_CLOSED)。";
					
				}else if(refund_status.equals("SELLER_REFUSE_BUYER")){//賣家收到退貨,退款成功,交易關閉	==>賣家登陸支付寶,确認OK。
					//http://club.alipay.com/simple/?t9978565.html
					content = "【支付寶異步通知-->退款流程】賣家不同意協定,等待買家修改(SELLER_REFUSE_BUYER)。";
				}
				else{
					//一般不會出現
					content = "【支付寶異步通知-->退款流程】未知。refund_status = " + refund_status;
				}
				updateRefundStatus(orderid, refund_status);
			}else if(StringUtils.isNotBlank(trade_status)){
				/**
				 * 交易流程
				 */
				if(trade_status.equals("WAIT_BUYER_PAY")){//等待買家付款
					content = "【支付寶異步通知】等待買家付款(WAIT_BUYER_PAY)。";
					
				}else if(trade_status.equals("WAIT_SELLER_SEND_GOODS")){//已付款,等待賣家發貨
					if(orderpay.getPaystatus().equals(Orderpay.orderpay_paystatus_y)){
						//由于支付寶的同步通知、異步通知,那麼WAIT_SELLER_SEND_GOODS的時候會有2次通知,是以需要synchronized處理好,保證訂單狀态和日志的一緻性。
						return true;
					}
					
					content = "【支付寶異步通知】已付款,等待賣家發貨(WAIT_SELLER_SEND_GOODS)。";
					
					//更新支付記錄為【成功支付】
					Orderpay orderpay2 = new Orderpay();
					orderpay2.setId(orderpayID);
					orderpay2.setTradeNo(trade_no);
					orderpay2.setPaystatus(Orderpay.orderpay_paystatus_y);
					orderpayDao.update(orderpay2);
					
					/**
					 * 真實砍庫存,并同步減少内容庫存數
					 */
					logger.error("真實砍庫存,并同步減少内容庫存數...");
					Orderdetail orderdetail = new Orderdetail();
					orderdetail.setOrderID(Integer.valueOf(orderid));
					List<Orderdetail> orderdetailList = orderdetailDao.selectList(orderdetail);
					logger.error("orderdetailList = " + orderdetailList.size());
					String lowStocks = null;//訂單是否缺貨記錄
//					int score = 0;//商品積分彙總
					for (int i = 0; i < orderdetailList.size(); i++) {
						Orderdetail detailInfo = orderdetailList.get(i);
						String productID = String.valueOf(detailInfo.getProductID());
						//記憶體砍庫存呢
						ProductStockInfo stockInfo = SystemManager.getInstance().getProductStockMap().get(productID);
						if(stockInfo.getStock() >= detailInfo.getNumber()){
							stockInfo.setStock(stockInfo.getStock() - detailInfo.getNumber());
							stockInfo.setChangeStock(true);
							//資料庫砍庫存
							Product product = new Product();
							product.setId(productID);
							product.setStock(stockInfo.getStock());
							product.setAddSellcount(detailInfo.getNumber());//增加銷量
							productService.updateStockAfterPaySuccess(product);
						}else{
							lowStocks = Order.order_lowStocks_y;
							//記錄庫存不足
							Orderdetail od = new Orderdetail();
							od.setId(detailInfo.getId());
							od.setLowStocks(Orderdetail.orderdetail_lowstocks_y);
							orderdetailDao.update(od);
						}
//						score += stockInfo.getScore();
						logger.error("productID = " + productID + ",stockInfo.getStock() = " + stockInfo.getStock());
//						SystemManager.productStockMap.put(product.getId(),stockInfo);
					}
					
					//更新訂單的支付狀态為【已支付】
					Order order = new Order();
					order.setId(orderid);
					if (lowStocks != null) {
						order.setLowStocks(Order.order_lowStocks_y);
					}
					order.setPaystatus(Order.order_paystatus_y);
					dao.update(order);
					
				}else if(trade_status.equals("WAIT_BUYER_CONFIRM_GOODS")){//已發貨,等待買家确認收貨
					content = "【支付寶異步通知】已發貨,等待買家确認收貨(WAIT_BUYER_CONFIRM_GOODS)。";
					//更新訂單狀态為【已發貨】
					Order order = dao.selectById(orderid);
					if(order==null){
						throw new NullPointerException("根據訂單号查詢不到訂單資訊,orderid="+orderid);
					}
					
					logger.error("order.getStatus()"+order.getStatus()+",trade_status=WAIT_BUYER_CONFIRM_GOODS");
					
					Orderlog orderlog = new Orderlog();
					orderlog.setContent(content);
					orderlog.setAccount(order.getAccount());
					//臨時解決辦法,防止此日志重複記錄.
					if(orderlogDao.selectCount(orderlog) > 0){
						return true;
					}
					
//					if(order.getStatus().equals(Order.order_status_send)){
//						//目前訂單狀态已經是這個狀态了,無需重複操作。
//						return true;
//					}
					
					//防止由于支付寶異步消息的先後順序,導緻把訂單的狀态更新混亂了。
					if(order.getStatus().equals(Order.order_status_pass)){
						order = new Order();
						order.setId(orderid);
						order.setStatus(Order.order_status_send);
						dao.update(order);
					}
				}else if(trade_status.equals("TRADE_FINISHED")){//交易完成
					content = "【支付寶異步通知】交易完成(TRADE_FINISHED)。";
					
					Order order = dao.selectById(orderid);
					if(order==null){
						throw new NullPointerException("根據訂單号查詢不到訂單資訊,orderid="+orderid);
					}
					
					//訂單結束後,訂單上面贈送的積分都成功轉移到使用者賬戶上。
					Account acc = new Account();
					acc.setAccount(order.getAccount());
					acc.setAddScore(order.getScore() - order.getAmountExchangeScore());//支付完成,扣除訂單消耗的積分
					accountService.updateScore(acc);
					
					//更新訂單狀态為【已簽收】
					order = new Order();
					order.setId(orderid);
					order.setStatus(Order.order_status_sign);
					dao.update(order);
				}else{
					//一般不會出現
					content = "【支付寶異步通知】未知。trade_status = " + trade_status;
				}
			}else{
				throw new RuntimeException("運作異常!");
			}
			
			/**
			 * 以上代碼,如不可以傳回都會走到此處,記錄下日志.
			 */
			insertOrderlog(orderid, content);
			
			return true;
		}
	}
	
	/**
	 * 更新訂單的退款狀态
	 * @param orderid	訂單ID
	 * @param refundStatus	退款狀态
	 */
	private void updateRefundStatus(String orderid,String refundStatus){
		Order order = new Order();
		order.setId(orderid);
		order.setRefundStatus(refundStatus);
		dao.update(order);
	}
	
	/**
	 * 插入訂單記錄檔
	 * @param orderid	訂單ID
	 * @param content	日志内容
	 */
	private void insertOrderlog(String orderid,String content) {
		Orderlog orderlog = new Orderlog();
		orderlog.setOrderid(orderid);//訂單ID
		orderlog.setAccount("alipay_notify");//操作人賬号
		orderlog.setContent(content);//日志内容
		orderlog.setAccountType(Orderlog.orderlog_accountType_p);
		orderlogDao.insert(orderlog);
	}

	@Override
	public OrderSimpleReport selectOrdersSimpleReport(String account) {
		return dao.selectOrdersSimpleReport(account);
	}
}
           
4. 使用mq消息隊列的方式做補償
debug error怎麼解決_分布式事務的解決方案

消息中間件:

/**
 * 獨立消息服務,目前隻實作一個大架構,主要目的是給大家理清分布式事務的控制思路
 *
 */
@Service(value = "messageServiceImpl")
public class MessageServiceImpl implements MessageService {

	@Override
	@Transactional
	public void receiveMsg(Message message) {
		if (message == null) {
			throw new RuntimeException("消息為空");
		}

		switch (message.getStatus()) {
		case MessageStatus.INIT:
			// TODO save msg
			System.out.println("儲存初始消息");
			break;
		case MessageStatus.SENT:
			// TODO update msg status 為sent
			// TODO 發送消息到訂單減庫存隊列,這塊待同學們自己實作
			System.out.println("更新消息狀态為sent");
			break;
		case MessageStatus.END:
			// TODO update msg status 為end
			break;

		default:
			throw new RuntimeException("消息狀态有誤");
		}
	}

}
           

MessageService :

public interface MessageService {

	void receiveMsg(Message msg);
}
           

Message :

import java.io.Serializable;

public class Message  implements Serializable {

	private Integer id;
	private String bizId;
	private String bizType;
	private String bizData;
	private int status;
	
	public Message(String bizId, String bizType, String bizData, int status) {
		super();
		this.bizId = bizId;
		this.bizType = bizType;
		this.bizData = bizData;
		this.status = status;
	}
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getBizId() {
		return bizId;
	}
	public void setBizId(String bizId) {
		this.bizId = bizId;
	}
	public String getBizType() {
		return bizType;
	}
	public void setBizType(String bizType) {
		this.bizType = bizType;
	}
	public String getBizData() {
		return bizData;
	}
	public void setBizData(String bizData) {
		this.bizData = bizData;
	}
	public int getStatus() {
		return status;
	}
	public void setStatus(int status) {
		this.status = status;
	}	
}
           

三. 定時任務 Quartz的使用

1. Quartz 概述

Quartz 是 OpenSymphony 開源組織在 Job Scheduling 領域又一個開源項目,它可以與 J2EE 與 J2SE 應用程式相結合也可以單獨使用。Quartz 可以用來建立簡單或為運作十個,百個,甚至是好幾萬個 Jobs 這樣複雜的程式。Jobs 可以做成标準的 Java 元件或 EJBs。

2. 為什麼使用 Quartz?

Quartz 是一個任務排程架構。比如你遇到這樣的問題:

  • 每天 02:00 發送一份工作郵件給工作組成員并抄送給老闆(假裝自己很努力的工作到深夜)
  • 每月 2 号提醒自己還信用卡或自動還款
  • 每秒鐘發 N 筆髒資料給競争對手公司的伺服器(!←_←)

這些問題總結起來就是:在某一個有規律的時間點幹某件事。并且時間的觸發的條件可以非常複雜,複雜到需要一個專門的架構來幹這個事。Quartz 就是來幹這樣的事,你給它一個觸發條件的定義,它負責到了時間點,觸發相應的 Job 起來幹活(睡 NMB 起來嗨!)。

3. 什麼是 cron 表達式?

一個 cron 表達式具體表現就是一個字元串,這個字元串中包含 6~7 個字段,字段之間是由空格分割的,每個字段可以由任何允許的值以及允許的特殊字元所構成,下面表格列出了每個字段所允許的值和特殊字元

debug error怎麼解決_分布式事務的解決方案
  • *

    字元被用來指定所有的值。如:

    *

    在分鐘的字段域裡表示“每分鐘”。
  • -

    字元被用來指定一個範圍。如:

    10-12

    在小時域意味着“10點、11點、12點”
  • ,

    字元被用來指定另外的值。如:

    MON,WED,FRI

    在星期域裡表示“星期一、星期三、星期五”.
  • ?

    字元隻在日期域和星期域中使用。它被用來指定“非明确的值”。當你需要通過在這兩個域中的一個來指定一些東西的時候,它是有用的。
  • L

    字元指定在月或者星期中的某天(最後一天)。即 “Last” 的縮寫。但是在星期和月中 “L” 表示不同的意思,如:在月字段中 “L” 指月份的最後一天 “1月31日,2月28日”,如果在星期字段中則簡單的表示為“7”或者“SAT”。如果在星期字段中在某個 value 值得後面,則表示 “某月的最後一個星期 value” ,如 “6L” 表示某月的最後一個星期五。
  • W

    字元隻能用在月份字段中,該字段指定了離指定日期最近的那個星期日。
  • #

    字元隻能用在星期字段,該字段指定了第幾個星期 value 在某月中

每一個元素都可以顯式地規定一個值(如 6),一個區間(如 9-12 ),一個清單(如 9,11,13 )或一個通配符(如 *)。“月份中的日期”和“星期中的日期”這兩個元素是互斥的,是以應該通過設定一個問号(?)來表明你不想設定的那個字段。

debug error怎麼解決_分布式事務的解決方案
cron線上編輯器 :

線上Cron表達式生成器

4. Spring Boot 內建 Quartz

(1).

建立項目

建立一個名為

hello-quartz

的項目

(2).

pom檔案
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.funtl</groupId>
    <artifactId>hello-quartz</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>hello-quartz</name>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
           

主要增加了

org.springframework.boot:spring-boot-starter-quartz

依賴

(3).

Application
package com.duo.hello.quatrz;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableScheduling
@SpringBootApplication
public class HelloQuatrzApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloQuatrzApplication.class, args);
    }
}
           

使用

@EnableScheduling

注解來開啟計劃任務功能

(4).

建立任務

我們建立一個每 5 秒鐘列印目前時間的任務來測試 Quartz

package com.duo.hello.quatrz.tasks;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

@Component
public class PrintCurrentTimeTask {
    @Scheduled(cron = "0/5 * * * * ? ")
    public void printCurrentTime() {
        System.out.println("Current Time is:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }
}
           
5. 定時任務
@Component  
public class AnnotationQuartz {  
	
	
    @Scheduled(cron = "0/30 * * * * ?")
    public void processInitMsg() { 
    	//TODO 回調消息發送服務中心接口用消息業務id查找對應的業務資料是否生成,如果生成則直接将消息改成已發送狀态并投遞消息到mq
    }  
    
    @Scheduled(cron = "0/60 * * * * ?")
    public void processSentMsg() {  
    	//TODO 定時處理未完成的消息,如果已經幾次查找消息都還是處于未完成狀态,則很可能消息沒有被消費者接收成功,由于消費者有幂等性實作,則可重發消息給mq
    }  
} 
           

繼續閱讀