ActiveMQ解決分散式事務方案以及程式碼實現(一)
阿新 • • 發佈:2019-01-08
1.場景描述
可以設想一個比較常見的分散式事務場景,商品上架操作,該操作涉及到商品模組的Service服務中的上架操作,同時必須要滿足在solr中建立商品的索引方便前臺搜尋以及生成商品的靜態化頁面,在上架操作中傳送了一條訊息,訊息接收方搜尋工程以及靜態化工程分別提供建立索引的服務以及生成靜態化的服務。該兩個服務本身無狀態且獨立,構成一個完整的事務。如下圖所示
2.商品上架程式碼以及問題引出
@Autowired
private JmsTemplate jmsTemplate;
@Autowired
private ProductDao productDao;
/**
* 商品上架
* 在solr索引庫建立索引資訊
* 生成靜態化頁面
* @param ids 商品id集合
*/
@Transactional
public void isShow(Long[] ids) {
// 校驗 防止空指標異常
if (null == ids || ids.length == 0) {
return;
}
Product product = new Product();
// 設定商品狀態為上架
product.setIsShow(true);
for (final Long id : ids) {
product.setId(id);
// 資料庫中更新商品狀態為上架
productDao.updateByPrimaryKeySelective(product);
// 在solr索引庫建立索引資訊 生成靜態化頁面
// 傳送訊息
jmsTemplate.send(new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
// 傳遞商品的id
return session.createTextMessage(String.valueOf(id));
}
});
}
商品上架,同時也是訊息的傳送方。首先要明確訊息中介軟體是在分散式檔案系統中完成訊息的傳送和接受的基礎軟體,而訊息中介軟體是非同步的,因此訊息傳送方並不知道自己傳送的訊息會被誰接受,也不能正確的接收到訊息接收方接收到訊息之後的狀態。這就意味著一旦訊息接收方出現了異常比如說伺服器的宕機或者斷電斷網等因素導致了不能正確的建立索引以及生成靜態化頁面。事務不能得到保證,商品還是上架了。那麼怎麼當靜態化工程或者搜尋工程的一方出現異常的時候馬上的進行事務的回滾,讓商品上架失敗呢?
3.解決方案
①在資料庫建立日誌表
CREATE TABLE `pdbs_pdata_logs` (
`pdata_id` int(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`pdata_product_id` bigint(20) DEFAULT NULL COMMENT '商品id',
`pdata_is_solr` tinyint(1) DEFAULT NULL COMMENT '是否建立solr索引',
`pdata_is_freemarker` tinyint(1) DEFAULT NULL COMMENT '是否生成靜態化頁面',
PRIMARY KEY (`ts_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
②分散式事務解決方案
public void isShow(Long[] ids) {
// 在此處執行以下sql
// insert into pdbs_pdata_logs ('pdata_product_id') values(id1),(id2),(id3).....
if (null == ids || ids.length == 0) {
return;
}
Product product = new Product();
product.setIsShow(true);
for (final Long id : ids) {
product.setId(id);
productDao.updateByPrimaryKeySelective(product);
// 在靜態化工程 和搜尋工程中 分別執行以下SQL
// update pdbs_pdata_logs t set t.pdata_is_freemarker =1 where t.pdata_product_id= ?
// update pdbs_pdata_logs t set t.pdata_is_solr=1 where t.pdata_product_id= ?
jmsTemplate.send(new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
// 傳遞商品的id
return session.createTextMessage(String.valueOf(id));
}
});
}
// 執行以下sql
//select COUNT(1) from pdbs_pdata_logs t where (ISNULL(t.pdata_is_freemarker) or ISNULL(t.pdata_is_solr)) AND t.pdata_product_id in(id1,id2,....);
// 如果count > 0的話 這說明 靜態化工程或者搜尋工程有一方有異常或者沒有正確的執行 上架失敗。丟擲一個執行時異常 spring 捕捉到執行時異常則會進行事務的回滾 這樣在一定的方面 解決了分散式事務的一部分問題
}
分散式事務,本質上是對多個數據庫的事務進行統一控制,按照控制力度可以分為:不控制、部分控制和完全控制。不控制就是不引入分散式事務,部分控制就是各種變種的兩階段提交,包括上面提到的訊息事務+最終一致性、TCC模式,而完全控制就是完全實現兩階段提交。部分控制的好處是併發量和效能很好,缺點是資料一致性減弱了,完全控制則是犧牲了效能,保障了一致性,具體用哪種方式,最終還是取決於業務場景。作為技術人員,一定不能忘了技術是為業務服務的,不要為了技術而技術,針對不同業務進行技術選型也是一種很重要的能力!