日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java生鲜电商平台-SpringCloud微服务架构中分布式事务解决方案

發(fā)布時間:2023/12/13 java 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java生鲜电商平台-SpringCloud微服务架构中分布式事务解决方案 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Java生鮮電商平臺-SpringCloud微服務架構(gòu)中分布式事務解決方案

?

說明:Java生鮮電商平臺中由于采用了微服務架構(gòu)進行業(yè)務的處理,買家,賣家,配送,銷售,供應商等進行服務化,但是不可避免存在分布式事務的問題

業(yè)界有很多的解決方案,對此我相信大家都百度一下子就有很多,但是我巨人大哥想說的是:微服務架構(gòu)中應當盡量避免分布式事務。

?

下面就是來討論下,分布式事務中主要聚焦于強一致性和最終一致性的解決方案。

微服務的發(fā)展

微服務倡導將復雜的單體應用拆分為若干個功能簡單、松耦合的服務,這樣可以降低開發(fā)難度、增強擴展性、便于敏捷開發(fā)。當前被越來越多的開發(fā)者推崇,很多互聯(lián)網(wǎng)行業(yè)巨頭、開源社區(qū)等都開始了微服務的討論和實踐。

微服務落地存在的問題

雖然微服務現(xiàn)在如火如荼,但對其實踐其實仍處于探索階段。很多中小型互聯(lián)網(wǎng)公司,鑒于經(jīng)驗、技術(shù)實力等問題,微服務落地比較困難。

如著名架構(gòu)師Chris Richardson所言,目前存在的主要困難有如下幾方面:

  • 單體應用拆分為分布式系統(tǒng)后,進程間的通訊機制和故障處理措施變的更加復雜。
  • 系統(tǒng)微服務化后,一個看似簡單的功能,內(nèi)部可能需要調(diào)用多個服務并操作多個數(shù)據(jù)庫實現(xiàn),服務調(diào)用的分布式事務問題變的非常突出。
  • 微服務數(shù)量眾多,其測試、部署、監(jiān)控等都變的更加困難。

隨著RPC框架的成熟,第一個問題已經(jīng)逐漸得到解決。例如springcloud可以非常好的支持restful調(diào)用,dubbo可以支持多種通訊協(xié)議。

對于第三個問題,隨著docker、devops技術(shù)的發(fā)展以及各公有云paas平臺自動化運維工具的推出,微服務的測試、部署與運維會變得越來越容易。

而對于第二個問題,現(xiàn)在還沒有通用方案很好的解決微服務產(chǎn)生的事務問題。分布式事務已經(jīng)成為微服務落地最大的阻礙,也是最具挑戰(zhàn)性的一個技術(shù)難題。

ACID

  • 原子性(Atomicity): 一個事務的所有系列操作步驟被看成是一個動作,所有的步驟要么全部完成要么一個也不會完成,如果事務過程中任何一點失敗,將要被改變的數(shù)據(jù)庫記錄就不會被真正被改變。

  • 一致性(Consistency): 數(shù)據(jù)庫的約束 級聯(lián)和觸發(fā)機制Trigger都必須滿足事務的一致性。也就是說,通過各種途徑包括外鍵約束等任何寫入數(shù)據(jù)庫的數(shù)據(jù)都是有效的,不能發(fā)生表與表之間存在外鍵約束,但是有數(shù)據(jù)卻違背這種約束性。所有改變數(shù)據(jù)庫數(shù)據(jù)的動作事務必須完成,沒有事務會創(chuàng)建一個無效數(shù)據(jù)狀態(tài),這是不同于CAP理論的一致性"consistency".

  • 隔離性(Isolation): 主要用于實現(xiàn)并發(fā)控制, 隔離能夠確保并發(fā)執(zhí)行的事務能夠順序一個接一個執(zhí)行,通過隔離,一個未完成事務不會影響另外一個未完成事務。

  • 持久性(Durability): 一旦一個事務被提交,它應該持久保存,不會因為和其他操作沖突而取消這個事務。很多人認為這意味著事務是持久在磁盤上,但是規(guī)范沒有特別定義這點。

一致性理論

分布式事務的目的是保障分庫數(shù)據(jù)一致性,而跨庫事務會遇到各種不可控制的問題,如個別節(jié)點永久性宕機,像單機事務一樣的 ACID 是無法奢望的。

另外,業(yè)界著名的 CAP 理論也告訴我們,對分布式系統(tǒng),需要將數(shù)據(jù)一致性和系統(tǒng)可用性、分區(qū)容忍性放在天平上一起考慮。

兩階段提交協(xié)議(簡稱2PC)是實現(xiàn)分布式事務較為經(jīng)典的方案,但 2PC 的可擴展性很差,在分布式架構(gòu)下應用代價較大,eBay 架構(gòu)師 Dan Pritchett 提出了 BASE 理論,用于解決大規(guī)模分布式系統(tǒng)下的數(shù)據(jù)一致性問題。

BASE 理論告訴我們:可以通過放棄系統(tǒng)在每個時刻的強一致性來換取系統(tǒng)的可擴展性。

CAP 理論

在分布式系統(tǒng)中,一致性(Consistency)、可用性(Availability)和分區(qū)容忍性(Partition Tolerance)3 個要素最多只能同時滿足兩個,不可兼得。其中,分區(qū)容忍性又是不可或缺的。

  • 一致性:分布式環(huán)境下,多個節(jié)點的數(shù)據(jù)是否強一致。
  • 可用性:分布式服務能一直保證可用狀態(tài)。當用戶發(fā)出一個請求后,服務能在有限時間內(nèi)返回結(jié)果。
  • 分區(qū)容忍性:特指對網(wǎng)絡分區(qū)的容忍性。

舉例:Cassandra、Dynamo 等,默認優(yōu)先選擇 AP,弱化 C;HBase、MongoDB 等,默認優(yōu)先選擇 CP,弱化 A。

BASE 理論

核心思想:

  • 基本可用(Basically Available):指分布式系統(tǒng)在出現(xiàn)故障時,允許損失部分的可用性來保證核心可用;
  • 軟狀態(tài)(Soft state):指允許分布式系統(tǒng)存在中間狀態(tài),該中間狀態(tài)不會影響到系統(tǒng)的整體可用性;
  • 最終一致性(Eventual consistency):指分布式系統(tǒng)中的所有副本數(shù)據(jù)經(jīng)過一定時間后,最終能夠達到一致的狀態(tài);
  • 原子性(A)與持久性(D)必須根本保障;
  • 為了可用性、性能與降級服務的需要,只有降低一致性( C ) 與 隔離性( I ) 的要求;
  • 酸堿平衡(ACID-BASE Balance);

BASE 是對 CAP 中 AP 的一個擴展

一致性模型

數(shù)據(jù)的一致性模型可以分成以下三類:

  • 強一致性:數(shù)據(jù)更新成功后,任意時刻所有副本中的數(shù)據(jù)都是一致的,一般采用同步的方式實現(xiàn)。
  • 弱一致性:數(shù)據(jù)更新成功后,系統(tǒng)不承諾立即可以讀到最新寫入的值,也不承諾具體多久之后可以讀到。
  • 最終一致性:弱一致性的一種形式,數(shù)據(jù)更新成功后,系統(tǒng)不承諾立即可以返回最新寫入的值,但是保證最終會返回上一次更新操作的值。

分布式系統(tǒng)數(shù)據(jù)的強一致性、弱一致性和最終一致性可以通過 Quorum NRW 算法分析。

本地事務

  • 在單個數(shù)據(jù)庫的本地并且限制在單個進程內(nèi)的事務
  • 本地事務不涉及多個數(shù)據(jù)來源

分布式事務典型方案

  • 兩階段提交(2PC, Two Phase Commit)方案;
  • 本地消息表 (eBay 事件隊列方案);
  • TCC 補償模式;

分類:

  • 兩階段型
  • 補償型
  • 異步確保型
  • 最大努力通知型

服務模式:

  • 可查詢操作
  • 冪等操作
  • TCC操作
  • 可補償操作

兩階段提交2PC(強一致性)

基于XA協(xié)議的兩階段提交:

  • 第一階段是表決階段,所有參與者都將本事務能否成功的信息反饋發(fā)給協(xié)調(diào)者;
  • 第二階段是執(zhí)行階段,協(xié)調(diào)者根據(jù)所有參與者的反饋,通知所有參與者,步調(diào)一致地在所有分支上提交或者回滾;

缺點:

  • 單點問題:事務管理器在整個流程中扮演的角色很關(guān)鍵,如果其宕機,比如在第一階段已經(jīng)完成,在第二階段正準備提交的時候事務管理器宕機,資源管理器就會一直阻塞,導致數(shù)據(jù)庫無法使用。
  • 同步阻塞:在準備就緒之后,資源管理器中的資源一直處于阻塞,直到提交完成,釋放資源。
  • 數(shù)據(jù)不一致:兩階段提交協(xié)議雖然為分布式數(shù)據(jù)強一致性所設計,但仍然存在數(shù)據(jù)不一致性的可能。比如:在第二階段中,假設協(xié)調(diào)者發(fā)出了事務 Commit 的通知,但是因為網(wǎng)絡問題該通知僅被一部分參與者所收到并執(zhí)行了 Commit 操作,其余的參與者則因為沒有收到通知一直處于阻塞狀態(tài),這時候就產(chǎn)生了數(shù)據(jù)的不一致性。

總的來說,XA 協(xié)議比較簡單,成本較低,但是其單點問題,以及不能支持高并發(fā)(由于同步阻塞)依然是其最大的弱點。

本地消息表(最終一致性)

eBay 的架構(gòu)師 Dan Pritchett,曾在一篇解釋 BASE 原理的論文《Base:An Acid Alternative》中提到一個 eBay 分布式系統(tǒng)一致性問題的解決方案。

?

它的核心思想是將需要分布式處理的任務通過消息或者日志的方式來異步執(zhí)行,消息或日志可以存到本地文件、數(shù)據(jù)庫或消息隊列,再通過業(yè)務規(guī)則進行失敗重試,它要求各服務的接口是冪等的。

本地消息表與業(yè)務數(shù)據(jù)表處于同一個數(shù)據(jù)庫中,這樣就能利用本地事務來保證在對這兩個表的操作滿足事務特性,并且使用了消息隊列來保證最終一致性。

  • 在分布式事務操作的一方完成寫業(yè)務數(shù)據(jù)的操作之后向本地消息表發(fā)送一個消息,本地事務能保證這個消息一定會被寫入本地消息表中;
  • 之后將本地消息表中的消息轉(zhuǎn)發(fā)到 Kafka 等消息隊列中,如果轉(zhuǎn)發(fā)成功則將消息從本地消息表中刪除,否則繼續(xù)重新轉(zhuǎn)發(fā);
  • 消息消費方處理這個消息,并完成自己的業(yè)務邏輯。此時如果本地事務處理成功,表明已經(jīng)處理成功了,如果處理失敗,那么就會重試執(zhí)行。如果是業(yè)務上面的失敗,可以給生產(chǎn)方發(fā)送一個業(yè)務補償消息,通知生產(chǎn)方進行回滾等操作;

優(yōu)點: 一種非常經(jīng)典的實現(xiàn),避免了分布式事務,實現(xiàn)了最終一致性。

缺點: 消息表會耦合到業(yè)務系統(tǒng)中,如果沒有封裝好的解決方案,會有很多雜活需要處理。

這個方案的核心在于第二階段的重試和冪等執(zhí)行。失敗后重試,這是一種補償機制,它是能保證系統(tǒng)最終一致的關(guān)鍵流程。

可靠消息的最終一致性代碼示例

表結(jié)構(gòu)

DROP TABLE IF EXISTS `rp_transaction_message`;CREATE TABLE `rp_transaction_message` (`id` VARCHAR (50) NOT NULL DEFAULT '' COMMENT '主鍵ID',`version` INT (11) NOT NULL DEFAULT '0' COMMENT '版本號',`editor` VARCHAR (100) DEFAULT NULL COMMENT '修改者',`creater` VARCHAR (100) DEFAULT NULL COMMENT '創(chuàng)建者',`edit_time` datetime DEFAULT NULL COMMENT '最后修改時間',`create_time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '創(chuàng)建時間',`message_id` VARCHAR (50) NOT NULL DEFAULT '' COMMENT '消息ID',`message_body` LONGTEXT NOT NULL COMMENT '消息內(nèi)容',`message_data_type` VARCHAR (50) DEFAULT NULL COMMENT '消息數(shù)據(jù)類型',`consumer_queue` VARCHAR (100) NOT NULL DEFAULT '' COMMENT '消費隊列',`message_send_times` SMALLINT (6) NOT NULL DEFAULT '0' COMMENT '消息重發(fā)次數(shù)',`areadly_dead` VARCHAR (20) NOT NULL DEFAULT '' COMMENT '是否死亡',`status` VARCHAR (20) NOT NULL DEFAULT '' COMMENT '狀態(tài)',`remark` VARCHAR (200) DEFAULT NULL COMMENT '備注',`field1` VARCHAR (200) DEFAULT NULL COMMENT '擴展字段1',`field2` VARCHAR (200) DEFAULT NULL COMMENT '擴展字段2',`field3` VARCHAR (200) DEFAULT NULL COMMENT '擴展字段3',PRIMARY KEY (`id`),KEY `AK_Key_2` (`message_id`) ) ENGINE = INNODB DEFAULT CHARSET = utf8;public interface RpTransactionMessageService {/*** 預存儲消息.*/public int saveMessageWaitingConfirm(RpTransactionMessage rpTransactionMessage) throws MessageBizException;/*** 確認并發(fā)送消息.*/public void confirmAndSendMessage(String messageId) throws MessageBizException;/*** 存儲并發(fā)送消息.*/public int saveAndSendMessage(RpTransactionMessage rpTransactionMessage) throws MessageBizException;/*** 直接發(fā)送消息.*/public void directSendMessage(RpTransactionMessage rpTransactionMessage) throws MessageBizException;/*** 重發(fā)消息.*/public void reSendMessage(RpTransactionMessage rpTransactionMessage) throws MessageBizException;/*** 根據(jù)messageId重發(fā)某條消息.*/public void reSendMessageByMessageId(String messageId) throws MessageBizException;/*** 將消息標記為死亡消息.*/public void setMessageToAreadlyDead(String messageId) throws MessageBizException;/*** 根據(jù)消息ID獲取消息*/public RpTransactionMessage getMessageByMessageId(String messageId) throws MessageBizException;/*** 根據(jù)消息ID刪除消息*/public void deleteMessageByMessageId(String messageId) throws MessageBizException;/*** 重發(fā)某個消息隊列中的全部已死亡的消息.*/public void reSendAllDeadMessageByQueueName(String queueName, int batchSize) throws MessageBizException;/*** 獲取分頁數(shù)據(jù)*/PageBean listPage(PageParam pageParam, Map<String, Object> paramMap) throws MessageBizException;} @Service("rpTransactionMessageService") public class RpTransactionMessageServiceImpl implements RpTransactionMessageService {private static final Log log = LogFactory.getLog(RpTransactionMessageServiceImpl.class);@Autowiredprivate RpTransactionMessageDao rpTransactionMessageDao;@Autowiredprivate JmsTemplate notifyJmsTemplate;public int saveMessageWaitingConfirm(RpTransactionMessage message) {if (message == null) {throw new MessageBizException(MessageBizException.SAVA_MESSAGE_IS_NULL, "保存的消息為空");}if (StringUtil.isEmpty(message.getConsumerQueue())) {throw new MessageBizException(MessageBizException.MESSAGE_CONSUMER_QUEUE_IS_NULL, "消息的消費隊列不能為空 ");}message.setEditTime(new Date());message.setStatus(MessageStatusEnum.WAITING_CONFIRM.name());message.setAreadlyDead(PublicEnum.NO.name());message.setMessageSendTimes(0);return rpTransactionMessageDao.insert(message);}public void confirmAndSendMessage(String messageId) {final RpTransactionMessage message = getMessageByMessageId(messageId);if (message == null) {throw new MessageBizException(MessageBizException.SAVA_MESSAGE_IS_NULL, "根據(jù)消息id查找的消息為空");}message.setStatus(MessageStatusEnum.SENDING.name());message.setEditTime(new Date());rpTransactionMessageDao.update(message);notifyJmsTemplate.setDefaultDestinationName(message.getConsumerQueue());notifyJmsTemplate.send(new MessageCreator() {public Message createMessage(Session session) throws JMSException {return session.createTextMessage(message.getMessageBody());}});}public int saveAndSendMessage(final RpTransactionMessage message) {if (message == null) {throw new MessageBizException(MessageBizException.SAVA_MESSAGE_IS_NULL, "保存的消息為空");}if (StringUtil.isEmpty(message.getConsumerQueue())) {throw new MessageBizException(MessageBizException.MESSAGE_CONSUMER_QUEUE_IS_NULL, "消息的消費隊列不能為空 ");}message.setStatus(MessageStatusEnum.SENDING.name());message.setAreadlyDead(PublicEnum.NO.name());message.setMessageSendTimes(0);message.setEditTime(new Date());int result = rpTransactionMessageDao.insert(message);notifyJmsTemplate.setDefaultDestinationName(message.getConsumerQueue());notifyJmsTemplate.send(new MessageCreator() {public Message createMessage(Session session) throws JMSException {return session.createTextMessage(message.getMessageBody());}});return result;}public void directSendMessage(final RpTransactionMessage message) {if (message == null) {throw new MessageBizException(MessageBizException.SAVA_MESSAGE_IS_NULL, "保存的消息為空");}if (StringUtil.isEmpty(message.getConsumerQueue())) {throw new MessageBizException(MessageBizException.MESSAGE_CONSUMER_QUEUE_IS_NULL, "消息的消費隊列不能為空 ");}notifyJmsTemplate.setDefaultDestinationName(message.getConsumerQueue());notifyJmsTemplate.send(new MessageCreator() {public Message createMessage(Session session) throws JMSException {return session.createTextMessage(message.getMessageBody());}});}public void reSendMessage(final RpTransactionMessage message) {if (message == null) {throw new MessageBizException(MessageBizException.SAVA_MESSAGE_IS_NULL, "保存的消息為空");}if (StringUtil.isEmpty(message.getConsumerQueue())) {throw new MessageBizException(MessageBizException.MESSAGE_CONSUMER_QUEUE_IS_NULL, "消息的消費隊列不能為空 ");}message.addSendTimes();message.setEditTime(new Date());rpTransactionMessageDao.update(message);notifyJmsTemplate.setDefaultDestinationName(message.getConsumerQueue());notifyJmsTemplate.send(new MessageCreator() {public Message createMessage(Session session) throws JMSException {return session.createTextMessage(message.getMessageBody());}});}public void reSendMessageByMessageId(String messageId) {final RpTransactionMessage message = getMessageByMessageId(messageId);if (message == null) {throw new MessageBizException(MessageBizException.SAVA_MESSAGE_IS_NULL, "根據(jù)消息id查找的消息為空");}int maxTimes = Integer.valueOf(PublicConfigUtil.readConfig("message.max.send.times"));if (message.getMessageSendTimes() >= maxTimes) {message.setAreadlyDead(PublicEnum.YES.name());}message.setEditTime(new Date());message.setMessageSendTimes(message.getMessageSendTimes() + 1);rpTransactionMessageDao.update(message);notifyJmsTemplate.setDefaultDestinationName(message.getConsumerQueue());notifyJmsTemplate.send(new MessageCreator() {public Message createMessage(Session session) throws JMSException {return session.createTextMessage(message.getMessageBody());}});}public void setMessageToAreadlyDead(String messageId) {RpTransactionMessage message = getMessageByMessageId(messageId);if (message == null) {throw new MessageBizException(MessageBizException.SAVA_MESSAGE_IS_NULL, "根據(jù)消息id查找的消息為空");}message.setAreadlyDead(PublicEnum.YES.name());message.setEditTime(new Date());rpTransactionMessageDao.update(message);}public RpTransactionMessage getMessageByMessageId(String messageId) {Map<String, Object> paramMap = new HashMap<String, Object>();paramMap.put("messageId", messageId);return rpTransactionMessageDao.getBy(paramMap);}public void deleteMessageByMessageId(String messageId) {Map<String, Object> paramMap = new HashMap<String, Object>();paramMap.put("messageId", messageId);rpTransactionMessageDao.delete(paramMap);}@SuppressWarnings("unchecked")public void reSendAllDeadMessageByQueueName(String queueName, int batchSize) {log.info("==>reSendAllDeadMessageByQueueName");int numPerPage = 1000;if (batchSize > 0 && batchSize < 100) {numPerPage = 100;} else if (batchSize > 100 && batchSize < 5000) {numPerPage = batchSize;} else if (batchSize > 5000) {numPerPage = 5000;} else {numPerPage = 1000;}int pageNum = 1;Map<String, Object> paramMap = new HashMap<String, Object>();paramMap.put("consumerQueue", queueName);paramMap.put("areadlyDead", PublicEnum.YES.name());paramMap.put("listPageSortType", "ASC");Map<String, RpTransactionMessage> messageMap = new HashMap<String, RpTransactionMessage>();List<Object> recordList = new ArrayList<Object>();int pageCount = 1;PageBean pageBean = rpTransactionMessageDao.listPage(new PageParam(pageNum, numPerPage), paramMap);recordList = pageBean.getRecordList();if (recordList == null || recordList.isEmpty()) {log.info("==>recordList is empty");return;}pageCount = pageBean.getTotalPage();for (final Object obj : recordList) {final RpTransactionMessage message = (RpTransactionMessage) obj;messageMap.put(message.getMessageId(), message);}for (pageNum = 2; pageNum <= pageCount; pageNum++) {pageBean = rpTransactionMessageDao.listPage(new PageParam(pageNum, numPerPage), paramMap);recordList = pageBean.getRecordList();if (recordList == null || recordList.isEmpty()) {break;}for (final Object obj : recordList) {final RpTransactionMessage message = (RpTransactionMessage) obj;messageMap.put(message.getMessageId(), message);}}recordList = null;pageBean = null;for (Map.Entry<String, RpTransactionMessage> entry : messageMap.entrySet()) {final RpTransactionMessage message = entry.getValue();message.setEditTime(new Date());message.setMessageSendTimes(message.getMessageSendTimes() + 1);rpTransactionMessageDao.update(message);notifyJmsTemplate.setDefaultDestinationName(message.getConsumerQueue());notifyJmsTemplate.send(new MessageCreator() {public Message createMessage(Session session) throws JMSException {return session.createTextMessage(message.getMessageBody());}});}}@SuppressWarnings("unchecked")public PageBean<RpTransactionMessage> listPage(PageParam pageParam, Map<String, Object> paramMap) {return rpTransactionMessageDao.listPage(pageParam, paramMap);}} @Component("messageBiz") public class MessageBiz {private static final Log log = LogFactory.getLog(MessageBiz.class);@Autowiredprivate RpTradePaymentQueryService rpTradePaymentQueryService;@Autowiredprivate RpTransactionMessageService rpTransactionMessageService;/*** 處理[waiting_confirm]狀態(tài)的消息* @param messages*/public void handleWaitingConfirmTimeOutMessages(Map<String, RpTransactionMessage> messageMap) {log.debug("開始處理[waiting_confirm]狀態(tài)的消息,總條數(shù)[" + messageMap.size() + "]");// 單條消息處理(目前該狀態(tài)的消息,消費隊列全部是accounting,如果后期有業(yè)務擴充,需做隊列判斷,做對應的業(yè)務處理。)for (Map.Entry<String, RpTransactionMessage> entry : messageMap.entrySet()) {RpTransactionMessage message = entry.getValue();try {log.debug("開始處理[waiting_confirm]消息ID為[" + message.getMessageId() + "]的消息");String bankOrderNo = message.getField1();RpTradePaymentRecord record = rpTradePaymentQueryService.getRecordByBankOrderNo(bankOrderNo);// 如果訂單成功,把消息改為待處理,并發(fā)送消息if (TradeStatusEnum.SUCCESS.name().equals(record.getStatus())) {// 確認并發(fā)送消息rpTransactionMessageService.confirmAndSendMessage(message.getMessageId());} else if (TradeStatusEnum.WAITING_PAYMENT.name().equals(record.getStatus())) {// 訂單狀態(tài)是等到支付,可以直接刪除數(shù)據(jù)log.debug("訂單沒有支付成功,刪除[waiting_confirm]消息id[" + message.getMessageId() + "]的消息");rpTransactionMessageService.deleteMessageByMessageId(message.getMessageId());}log.debug("結(jié)束處理[waiting_confirm]消息ID為[" + message.getMessageId() + "]的消息");} catch (Exception e) {log.error("處理[waiting_confirm]消息ID為[" + message.getMessageId() + "]的消息異常:", e);}}}/*** 處理[SENDING]狀態(tài)的消息* @param messages*/public void handleSendingTimeOutMessage(Map<String, RpTransactionMessage> messageMap) {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");log.debug("開始處理[SENDING]狀態(tài)的消息,總條數(shù)[" + messageMap.size() + "]");// 根據(jù)配置獲取通知間隔時間Map<Integer, Integer> notifyParam = getSendTime();// 單條消息處理for (Map.Entry<String, RpTransactionMessage> entry : messageMap.entrySet()) {RpTransactionMessage message = entry.getValue();try {log.debug("開始處理[SENDING]消息ID為[" + message.getMessageId() + "]的消息");// 判斷發(fā)送次數(shù)int maxTimes = Integer.valueOf(PublicConfigUtil.readConfig("message.max.send.times"));log.debug("[SENDING]消息ID為[" + message.getMessageId() + "]的消息,已經(jīng)重新發(fā)送的次數(shù)[" + message.getMessageSendTimes() + "]");// 如果超過最大發(fā)送次數(shù)直接退出if (maxTimes < message.getMessageSendTimes()) {// 標記為死亡rpTransactionMessageService.setMessageToAreadlyDead(message.getMessageId());continue;}// 判斷是否達到發(fā)送消息的時間間隔條件int reSendTimes = message.getMessageSendTimes();int times = notifyParam.get(reSendTimes == 0 ? 1 : reSendTimes);long currentTimeInMillis = Calendar.getInstance().getTimeInMillis();long needTime = currentTimeInMillis - times * 60 * 1000;long hasTime = message.getEditTime().getTime();// 判斷是否達到了可以再次發(fā)送的時間條件if (hasTime > needTime) {log.debug("currentTime[" + sdf.format(new Date()) + "],[SENDING]消息上次發(fā)送時間[" + sdf.format(message.getEditTime()) + "],必須過了[" + times + "]分鐘才可以再發(fā)送。");continue;}// 重新發(fā)送消息rpTransactionMessageService.reSendMessage(message);log.debug("結(jié)束處理[SENDING]消息ID為[" + message.getMessageId() + "]的消息");} catch (Exception e) {log.error("處理[SENDING]消息ID為[" + message.getMessageId() + "]的消息異常:", e);}}}/*** 根據(jù)配置獲取通知間隔時間* @return*/private Map<Integer, Integer> getSendTime() {Map<Integer, Integer> notifyParam = new HashMap<Integer, Integer>();notifyParam.put(1, Integer.valueOf(PublicConfigUtil.readConfig("message.send.1.time")));notifyParam.put(2, Integer.valueOf(PublicConfigUtil.readConfig("message.send.2.time")));notifyParam.put(3, Integer.valueOf(PublicConfigUtil.readConfig("message.send.3.time")));notifyParam.put(4, Integer.valueOf(PublicConfigUtil.readConfig("message.send.4.time")));notifyParam.put(5, Integer.valueOf(PublicConfigUtil.readConfig("message.send.5.time")));return notifyParam;}} public class AccountingMessageListener implements SessionAwareMessageListener<Message> {private static final Log LOG = LogFactory.getLog(AccountingMessageListener.class);/*** 會計隊列模板(由Spring創(chuàng)建并注入進來)*/@Autowiredprivate JmsTemplate notifyJmsTemplate;@Autowiredprivate RpAccountingVoucherService rpAccountingVoucherService;@Autowiredprivate RpTransactionMessageService rpTransactionMessageService;public synchronized void onMessage(Message message, Session session) {RpAccountingVoucher param = null;String strMessage = null;try {ActiveMQTextMessage objectMessage = (ActiveMQTextMessage) message;strMessage = objectMessage.getText();LOG.info("strMessage1 accounting:" + strMessage);param = JSONObject.parseObject(strMessage, RpAccountingVoucher.class);// 這里轉(zhuǎn)換成相應的對象還有問題if (param == null) {LOG.info("param參數(shù)為空");return;}int entryType = param.getEntryType();double payerChangeAmount = param.getPayerChangeAmount();String voucherNo = param.getVoucherNo();String payerAccountNo = param.getPayerAccountNo();int fromSystem = param.getFromSystem();int payerAccountType = 0;if (param.getPayerAccountType() != null && !param.getPayerAccountType().equals("")) {payerAccountType = param.getPayerAccountType();}double payerFee = param.getPayerFee();String requestNo = param.getRequestNo();double bankChangeAmount = param.getBankChangeAmount();double receiverChangeAmount = param.getReceiverChangeAmount();String receiverAccountNo = param.getReceiverAccountNo();String bankAccount = param.getBankAccount();String bankChannelCode = param.getBankChannelCode();double profit = param.getProfit();double income = param.getIncome();double cost = param.getCost();String bankOrderNo = param.getBankOrderNo();int receiverAccountType = 0;double payAmount = param.getPayAmount();if (param.getReceiverAccountType() != null && !param.getReceiverAccountType().equals("")) {receiverAccountType = param.getReceiverAccountType();}double receiverFee = param.getReceiverFee();String remark = param.getRemark();rpAccountingVoucherService.createAccountingVoucher(entryType, voucherNo, payerAccountNo, receiverAccountNo, payerChangeAmount, receiverChangeAmount, income, cost, profit, bankChangeAmount, requestNo, bankChannelCode, bankAccount, fromSystem, remark, bankOrderNo, payerAccountType, payAmount, receiverAccountType, payerFee, receiverFee);//刪除消息rpTransactionMessageService.deleteMessageByMessageId(param.getMessageId());} catch (BizException e) {// 業(yè)務異常,不再寫會隊列LOG.error("==>BizException", e);} catch (Exception e) {// 不明異常不再寫會隊列LOG.error("==>Exception", e);}}public JmsTemplate getNotifyJmsTemplate() {return notifyJmsTemplate;}public void setNotifyJmsTemplate(JmsTemplate notifyJmsTemplate) {this.notifyJmsTemplate = notifyJmsTemplate;}public RpAccountingVoucherService getRpAccountingVoucherService() {return rpAccountingVoucherService;}public void setRpAccountingVoucherService(RpAccountingVoucherService rpAccountingVoucherService) {this.rpAccountingVoucherService = rpAccountingVoucherService;}}

  

與常規(guī)MQ的ACK機制對比

常規(guī)MQ確認機制:

  • Producer生成消息并發(fā)送給MQ(同步、異步);
  • MQ接收消息并將消息數(shù)據(jù)持久化到消息存儲(持久化操作為可選配置);
  • MQ向Producer返回消息的接收結(jié)果(返回值、異常);
  • Consumer監(jiān)聽并消費MQ中的消息;
  • Consumer獲取到消息后執(zhí)行業(yè)務處理;
  • Consumer對已成功消費的消息向MQ進行ACK確認(確認后的消息將從MQ中刪除);

常規(guī)MQ隊列消息的處理流程無法實現(xiàn)消息發(fā)送一致性,因此直接使用現(xiàn)成的MQ中間件產(chǎn)品無法實現(xiàn)可靠消息最終一致性的分布式事務解決方案

消息發(fā)送一致性:是指產(chǎn)生消息的業(yè)務動作與消息發(fā)送的一致。也就是說,如果業(yè)務操作成功,那么由這個業(yè)務操作所產(chǎn)生的消息一定要成功投遞出去(一般是發(fā)送到kafka、rocketmq、rabbitmq等消息中間件中),否則就丟消息。

下面用偽代碼進行演示消息發(fā)送和投遞的不可靠性:

先進行數(shù)據(jù)庫操作,再發(fā)送消息:

public void test1(){ //1 數(shù)據(jù)庫操作 //2 發(fā)送MQ消息 }

這種情況下無法保證數(shù)據(jù)庫操作與發(fā)送消息的一致性,因為可能數(shù)據(jù)庫操作成功,發(fā)送消息失敗。

先發(fā)送消息,再操作數(shù)據(jù)庫:

public void test1(){ //1 發(fā)送MQ消息 //2 數(shù)據(jù)庫操作 }

這種情況下無法保證數(shù)據(jù)庫操作與發(fā)送消息的一致性,因為可能發(fā)送消息成功,數(shù)據(jù)庫操作失敗。

在數(shù)據(jù)庫事務中,先發(fā)送消息,后操作數(shù)據(jù)庫:

@Transactional public void test1(){ //1 發(fā)送MQ消息 //2 數(shù)據(jù)庫操作 }

這里使用spring 的@Transactional注解,方法里面的操作都在一個事務中。同樣無法保證一致性,因為發(fā)送消息成功了,數(shù)據(jù)庫操作失敗的情況下,數(shù)據(jù)庫操作是回滾了,但是MQ消息沒法進行回滾。

在數(shù)據(jù)庫事務中,先操作數(shù)據(jù)庫,后發(fā)送消息:

@Transactional public void test1(){ //1 數(shù)據(jù)庫操作 //2 發(fā)送MQ消息 }

這種情況下,貌似沒有問題,如果發(fā)送MQ消息失敗,拋出異常,事務一定會回滾(加上了@Transactional注解后,spring方法拋出異常后,會自動進行回滾)。

這只是一個假象,因為發(fā)送MQ消息可能事實上已經(jīng)成功,如果是響應超時導致的異常。這個時候,數(shù)據(jù)庫操作依然回滾,但是MQ消息實際上已經(jīng)發(fā)送成功,導致不一致。

與消息發(fā)送一致性流程的對比:

  • 常規(guī)MQ隊列消息的處理流程無法實現(xiàn)消息發(fā)送一致性;
  • 投遞消息的流程其實就是消息的消費流程,可細化;

TCC (Try-Confirm-Cancel)補償模式(最終一致性)

TCC 其實就是采用的補償機制,其核心思想是:針對每個操作,都要注冊一個與其對應的確認和補償(撤銷)操作。

它分為三個階段:

  • Try 階段主要是對業(yè)務系統(tǒng)做檢測及資源預留
  • Confirm 階段主要是對業(yè)務系統(tǒng)做確認提交,Try階段執(zhí)行成功并開始執(zhí)行 Confirm階段時,默認 Confirm階段是不會出錯的。即:只要Try成功,Confirm一定成功。
  • Cancel 階段主要是在業(yè)務執(zhí)行錯誤,需要回滾的狀態(tài)下執(zhí)行的業(yè)務取消,預留資源釋放。

舉例(Bob 要向 Smith 轉(zhuǎn)賬):

  • 首先在 Try 階段,要先調(diào)用遠程接口把 Smith 和 Bob 的錢給凍結(jié)起來。
  • 在 Confirm 階段,執(zhí)行遠程調(diào)用的轉(zhuǎn)賬的操作,轉(zhuǎn)賬成功進行解凍。
  • 如果第2步執(zhí)行成功,那么轉(zhuǎn)賬成功,如果第二步執(zhí)行失敗,則調(diào)用遠程凍結(jié)接口對應的解凍方法 (Cancel)。

優(yōu)點:
跟2PC比起來,實現(xiàn)以及流程相對簡單了一些,但數(shù)據(jù)的一致性比2PC也要差一些

缺點:
缺點還是比較明顯的,在2,3步中都有可能失敗。TCC屬于應用層的一種補償方式,所以需要程序員在實現(xiàn)的時候多寫很多補償?shù)拇a,在一些場景中,一些業(yè)務流程可能用TCC不太好定義及處理。

可靠消息最終一致(常用)

不要用本地的消息表了,直接基于MQ來實現(xiàn)事務。比如阿里的RocketMQ就支持消息事務。

可靠消息最終一致性方案

大概流程:

  • A系統(tǒng)先發(fā)送一個prepared消息到mq,如果這個prepared消息發(fā)送失敗那么就直接取消操作別執(zhí)行了
  • 如果這個消息發(fā)送成功過了,那么接著執(zhí)行本地事務,如果成功就告訴mq發(fā)送確認消息,如果失敗就告訴mq回滾消息
  • 如果發(fā)送了確認消息,那么此時B系統(tǒng)會接收到確認消息,然后執(zhí)行本地的事務
  • mq會自動定時輪詢所有prepared消息回調(diào)你的接口,問你,這個消息是不是本地事務處理失敗了,所有沒發(fā)送確認消息?那是繼續(xù)重試還是回滾?一般來說這里你就可以查下數(shù)據(jù)庫看之前本地事務是否執(zhí)行,如果回滾了,那么這里也回滾吧。這個就是避免可能本地事務執(zhí)行成功了,別確認消息發(fā)送失敗了。

這個方案里,要是系統(tǒng)B的事務失敗了咋辦?重試咯,自動不斷重試直到成功,如果實在是不行,要么就是針對重要的資金類業(yè)務進行回滾,比如B系統(tǒng)本地回滾后,想辦法通知系統(tǒng)A也回滾;或者是發(fā)送報警由人工來手工回滾和補償

目前國內(nèi)互聯(lián)網(wǎng)公司大都是這么玩兒的,要不你使用RocketMQ支持的,要不你就基于其他MQ中間件自己封裝一套類似的邏輯,總之思路就是這樣的。

最大努力通知

業(yè)務發(fā)起方將協(xié)調(diào)服務的消息發(fā)送到MQ,下游服務接收此消息,如果處理失敗,將進行重試,重試N次后依然失敗,將不進行重試,放棄處理,這個應用場景要求對事物性要求不高的地方。

最大努力通知方案

?

最終總結(jié):

? ? ? 需要討論與學習,請加QQ群:793305035

??

轉(zhuǎn)載于:https://www.cnblogs.com/jurendage/p/11353968.html

總結(jié)

以上是生活随笔為你收集整理的Java生鲜电商平台-SpringCloud微服务架构中分布式事务解决方案的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

av久久久久久| 黄色成人免费电影 | 久久久免费看视频 | 九九热在线观看 | 天无日天天操天天干 | 国产美腿白丝袜足在线av | 中文字幕在线高清 | 成人三级黄色 | 免费高清男女打扑克视频 | 正在播放 久久 | 狠狠操狠狠 | www.久久久久 | 国产 中文 日韩 欧美 | 成人a视频片观看免费 | 亚洲精品a区 | 在线观看岛国片 | 992tv又爽又黄的免费视频 | 精品视频免费播放 | 亚洲成人黄色在线 | 成人久久精品视频 | 久久人人爽人人爽人人片av免费 | 狠狠干狠狠艹 | 日韩精品一区二区三区丰满 | 最新国产在线视频 | 亚洲欧洲精品一区二区 | 国产中文字幕网 | 蜜臀av性久久久久蜜臀av | 亚洲综合色视频在线观看 | 日韩伦理片一区二区三区 | 日韩欧美综合 | 99综合电影在线视频 | 午夜视频在线观看一区二区三区 | 在线免费国产视频 | 99麻豆视频| 麻豆视频免费网站 | 国产破处在线视频 | av福利在线导航 | 日韩精品视频在线观看网址 | 精品亚洲免a | 免费在线观看毛片网站 | 久久avav | 国产成人一区二区三区免费看 | 97超碰总站 | 国产成人久久av977小说 | 青草视频免费观看 | 人人爽人人爽人人片 | 国产午夜三级一区二区三桃花影视 | 日韩精品免费在线播放 | 亚洲黄色免费在线 | 国产永久免费高清在线观看视频 | 999成人| 日日干激情五月 | 四虎影视成人永久免费观看亚洲欧美 | 亚洲精品婷婷 | 看国产黄色片 | 久久国产精品一区二区三区四区 | 有码一区二区三区 | 日韩免费不卡视频 | 在线观看自拍 | 成人久久精品 | 日韩久久一区 | 久久久久久久亚洲精品 | 国产91在线播放 | 国产小视频你懂的在线 | 青春草免费在线视频 | 亚洲午夜精品一区二区三区电影院 | 成人中文字幕+乱码+中文字幕 | 欧美综合在线视频 | 免费看av片网站 | 成人久久免费视频 | 久久天天躁 | 99视频这里只有 | 色婷婷视频网 | 激情五月婷婷综合 | 韩国av免费 | 久久久久 免费视频 | 在线导航av | 97碰在线 | 色网站在线 | 国内精品久久久久久久久久久 | 日韩美视频 | 久久69精品久久久久久久电影好 | 亚洲精品一区二区三区新线路 | 香蕉在线视频播放网站 | 午夜精品一区二区三区免费视频 | 丁香影院在线 | 操久 | 久久久久免费电影 | 国产午夜不卡 | 99在线精品观看 | 日本在线视频一区二区三区 | 国产精品久久久久久久久久 | 免费在线观看黄网站 | 久久精品麻豆 | 999成人| 色综合久久88色综合天天人守婷 | www.成人精品 | 中国一级特黄毛片大片久久 | 激情开心 | 麻豆视频免费播放 | 成人毛片a| 亚洲黄在线观看 | 欧美巨大荫蒂茸毛毛人妖 | 五月婷av| 美女国产网站 | www天天干| av在线com| 久久久国产在线视频 | 久草男人天堂 | 日韩一级电影在线 | 国产婷婷久久 | 天天精品视频 | 欧美aaa级片 | 丁香综合av | 免费在线播放黄色 | 成人午夜黄色影院 | 亚洲成人动漫在线观看 | 超碰在线最新地址 | 免费a级黄色毛片 | 久草在线最新 | 精品国模一区二区三区 | 99久久精品国产免费看不卡 | 国产综合精品一区二区三区 | 99热在线国产精品 | 国产一级在线播放 | 国产中年夫妇高潮精品视频 | 国产在线欧美在线 | 日韩免费看的电影 | 成人精品一区二区三区中文字幕 | 中文字幕精品三级久久久 | 久久精彩视频 | 91成人精品一区在线播放69 | 在线观看午夜av | 日韩激情网 | 激情视频网页 | 日日摸日日 | 成人在线视| 国产黄网在线 | 黄色大全免费观看 | 国产精品手机播放 | 国内免费久久久久久久久久久 | 在线 精品 国产 | 久久福利 | 免费特级黄毛片 | 人人网av | 不卡av免费在线观看 | 免费观看福利视频 | 91色九色| 国产亚洲成人精品 | 丝袜制服天堂 | 成人欧美亚洲 | 欧美a级成人淫片免费看 | 国产无吗一区二区三区在线欢 | 免费a一级 | 亚洲午夜电影网 | 国产一线二线三线性视频 | 婷婷在线资源 | 亚洲高清在线精品 | 国产成人久久精品亚洲 | 99草视频| 国产精品一区二区吃奶在线观看 | 国产精品免费观看在线 | 天天摸天天操天天爽 | 91日韩免费 | 国产精品99久久久久人中文网介绍 | 久久久久久久久久电影 | 国产免费精彩视频 | 久久久久免费精品视频 | 成人av片免费观看app下载 | 天天想夜夜操 | 最新av网址在线观看 | 99精品免费观看 | 久久在线免费视频 | www91在线观看| 国色天香第二季 | 午夜国产福利在线 | 美女在线免费视频 | 免费观看国产成人 | 在线国产激情视频 | 久久国产视屏 | 超碰官网| 日韩av片无码一区二区不卡电影 | 久久尤物电影视频在线观看 | 色老板在线视频 | 亚洲免费精品一区二区 | 国产亚洲精品久久久久秋 | 久久综合网色—综合色88 | 又黄又爽又无遮挡的视频 | 久久视频在线观看免费 | 日韩免费视频网站 | 婷婷久久五月 | av片子在线观看 | 亚洲午夜久久久综合37日本 | 午夜精品久久久久久久99水蜜桃 | 黄色av电影免费观看 | 亚洲不卡123 | 在线观看亚洲视频 | 射九九 | 亚洲精品国偷自产在线91正片 | 成 人 黄 色视频免费播放 | 成年人免费在线观看 | 麻豆视频国产精品 | 四虎成人av| 亚洲国产综合在线 | 亚洲国产视频在线 | 成人午夜网址 | 黄色av一区 | 亚洲天堂网在线播放 | 精品国产乱码久久久久久三级人 | 精品av在线播放 | 免费观看一级 | 成人av电影在线观看 | 国产精品二区在线观看 | 精品久久久久国产免费第一页 | 91中文在线视频 | 韩国在线一区二区 | 国产精品一区欧美 | 天天干人人插 | 欧美日韩一区二区视频在线观看 | 日韩免费区 | 国产九九九九九 | 久久久久国产一区二区三区四区 | 97爱| 久久99热这里只有精品 | 97视频免费 | www成人精品 | 岛国精品一区二区 | 免费a网址 | 天天曰夜夜爽 | 日韩免费视频线观看 | 99久久精品免费看国产麻豆 | 亚洲精品视频在线观看视频 | 手机av网站 | 97视频精品 | 亚洲,国产成人av | 国产视频一区二区三区在线 | 在线视频观看你懂的 | 天天干天天想 | 一区二区三区韩国免费中文网站 | 在线观看韩国av | 日韩精品在线免费播放 | 亚洲色图激情文学 | 97狠狠干| 日韩丝袜在线观看 | 亚洲人人爱 | 天天草综合 | 国产免费又爽又刺激在线观看 | 99这里精品 | 久久综合九色九九 | 婷婷国产v亚洲v欧美久久 | 亚洲精品国产电影 | 五月天婷婷在线观看视频 | 国产五月婷 | 欧美日韩中文在线观看 | 国产精品嫩草在线 | 国产小视频免费在线观看 | 久久试看| 国内精品久久久久久久久久久久 | 黄色毛片一级 | 成人毛片一区 | 91av99| 丁香婷婷在线观看 | 黄色软件视频大全免费下载 | 亚洲国产成人在线 | 欧美激情xxxx性bbbb | 色亚洲激情 | 一区二区三区四区五区在线视频 | 天天艹| 国内综合精品午夜久久资源 | 久草综合在线观看 | 久久www免费视频 | 97热久久免费频精品99 | 国产成人精品女人久久久 | 西西44人体做爰大胆视频 | 久久久久久久电影 | 玖草在线观看 | 天天色影院| 久久久久免费精品视频 | 国产片免费在线观看视频 | 国产视频一区二区在线播放 | 日本女人的性生活视频 | 天天射,天天干 | 国内精品二区 | 国产精品成人免费一区久久羞羞 | 久久成人一区二区 | 在线视频你懂得 | www.天天成人国产电影 | 婷婷九月丁香 | 福利视频导航网址 | 超级av在线| 一区二区三区不卡在线 | 国产精品久久久久久a | 欧美怡红院 | 九九欧美| 特级黄色片免费看 | 免费日韩一区二区 | 日韩精品综合在线 | 亚洲伦理一区二区 | 国产精品第一页在线 | 丁香一区二区 | 美女网站视频免费黄 | 天堂在线视频中文网 | 国产精品中文久久久久久久 | 99热亚洲精品 | 免费进去里的视频 | 在线观看黄色免费视频 | 国产一区精品在线 | 黄色免费网站 | 亚洲成人午夜av | 狠狠的操| 精品一二三区 | 久草91视频 | 亚洲1级片| 久久久久久久国产精品 | 久久桃花网 | 色吧av色av | 亚洲欧美乱综合图片区小说区 | 中文国产成人精品久久一 | 免费av大片 | 园产精品久久久久久久7电影 | 黄色网在线播放 | 永久免费视频国产 | 美女黄色网在线播放 | 6080yy精品一区二区三区 | 99色在线 | 西西4444www大胆视频 | 精品人妖videos欧美人妖 | 狂野欧美激情性xxxx欧美 | 国产女教师精品久久av | 在线不卡的av | 亚洲国产视频在线 | 九九热有精品 | 中文字幕在线影院 | 成人影视免费 | 亚洲欧洲成人 | 成人小视频在线 | 中文字幕网站 | www.亚洲精品在线 | 精品久久久久免费极品大片 | 99精品国产99久久久久久福利 | 狠狠色丁香九九婷婷综合五月 | 国产精品激情 | 婷婷成人亚洲综合国产xv88 | 99久久精品免费看国产一区二区三区 | 99精品国产一区二区三区麻豆 | 免费视频在线观看网站 | 天堂在线一区二区 | 国产精品3 | 久久艹久久 | 99超碰在线观看 | 91亚洲精品久久久蜜桃借种 | 午夜久久久影院 | 亚洲伦理电影在线 | 国产成人精品在线 | 亚洲欧美日韩国产 | 日韩av中文 | 免费在线观看一区二区三区 | 天堂网av 在线 | 亚洲精品看片 | 国产韩国日本高清视频 | 亚洲黄色激情小说 | 国模一区二区三区四区 | 西西www4444大胆在线 | 91在线操 | 国产精品99久久久久久久久久久久 | 丁香花中文字幕 | 久久精品欧美视频 | 色播激情五月 | 97精品超碰一区二区三区 | 日韩视频免费看 | av不卡中文 | 日本免费久久高清视频 | 欧美精品久久久久久久久免 | 色停停五月天 | 日韩免费一级a毛片在线播放一级 | 国产高清免费 | 国产麻豆精品在线观看 | 久久最新网址 | 伊人亚洲综合网 | 国产一区二区三精品久久久无广告 | 国产亚洲精品精品精品 | 91新人在线观看 | 免费视频97 | 五月婷婷av | 国产一区二区电影在线观看 | 国产一级二级三级视频 | 91爱爱电影| 日韩在线观看第一页 | 亚洲精品在线视频播放 | 日韩精品视频免费专区在线播放 | 国产精品成人久久久久久久 | 欧美激情精品久久久久久变态 | 亚洲色图22p | 免费黄色小网站 | 日韩视频一区二区在线 | 婷婷六月综合亚洲 | 91麻豆产精品久久久久久 | 91.麻豆视频 | 韩国在线一区 | 精品久久精品 | 亚洲综合一区二区精品导航 | 久操中文字幕在线观看 | 国产一区精品在线观看 | 久久综合色影院 | 亚洲永久国产精品 | 国产一级二级在线观看 | av片免费播放 | 免费av免费观看 | 欧洲在线免费视频 | 久久久www成人免费毛片 | 97免费视频在线 | 国产成年免费视频 | 国内精品免费 | 黄色大片免费网站 | 中文字幕综合在线 | 亚洲影院色 | 婷婷资源站 | 国产亚洲精品福利 | 国产精品久久99综合免费观看尤物 | 婷婷播播网 | 黄色av电影一级片 | 六月丁香婷婷网 | 热久久99这里有精品 | 日韩欧美在线观看 | 欧美日韩一级视频 | 国产一区二区中文字幕 | 久久成人毛片 | 中文字幕观看视频 | 91网免费观看 | 国产第一福利 | 成人黄色电影在线观看 | 伊人伊成久久人综合网小说 | 欧美在线视频免费 | 日韩毛片在线免费观看 | 三级黄免费看 | 国产做爰视频 | 亚洲黄色免费电影 | 99这里只有久久精品视频 | 一级黄视频 | 中文免费观看 | 伊人色播| 在线精品在线 | 国产高清视频免费最新在线 | 在线观看亚洲精品 | 成人黄色在线视频 | 久久久久久久久久久国产精品 | 欧美色综合天天久久综合精品 | 国产不卡在线播放 | 久久久久在线视频 | 国产伦精品一区二区三区在线 | 成人小视频在线观看免费 | 日韩欧美电影 | 亚洲aaa级| 特级免费毛片 | 超碰免费97| 久久综合中文字幕 | 99久久婷婷国产综合精品 | 亚洲天堂网在线播放 | 手机av电影在线观看 | 综合色影院 | 2023av在线 | 黄色录像av | 四虎影院在线观看av | 久久免费观看少妇a级毛片 久久久久成人免费 | 蜜桃av综合网 | 天天色天天 | 国产精品久久久久久久久久不蜜月 | 国产精品久久久久久五月尺 | 国产精品女人久久久 | 午夜视频在线观看一区二区三区 | 欧美二区视频 | 亚洲精品在线观看网站 | 热久久免费国产视频 | 色综合久久久久久久久五月 | 一区二区不卡 | www.com操| 久久99久久99久久 | 99久久精品国产观看 | 久草在线免 | 天天综合网国产 | 夜夜爱av | aaa黄色毛片 | 五月天综合激情网 | 久久资源总站 | 香蕉视频亚洲 | 日本免费一二三区 | 久久综合精品一区 | 国偷自产视频一区二区久 | 国产精品久久麻豆 | 啪啪免费试看 | 超碰在线官网 | 一级黄色在线视频 | 99久久久成人国产精品 | 久久综合免费视频 | 不卡电影一区二区三区 | 久久久久免费电影 | 黄色综合 | 日韩欧美99| 久久久久亚洲精品成人网小说 | av在线直接看 | 国产精品久久久久久久av电影 | 波多野结衣在线视频免费观看 | 免费福利在线观看 | 亚洲在线网址 | 日韩电影在线观看一区二区三区 | 久久久久亚洲a | 国产精品va| 国产一级淫片在线观看 | 国产欧美综合视频 | 狠狠精品 | 香蕉在线观看 | 91探花在线视频 | 久久激情片 | 久久tv| 亚洲色图av | 亚洲一区二区三区在线看 | 69成人在线| 午夜久久网| 黄色成人在线 | www.久久色 | 国产xxxx | 欧美日本中文字幕 | 九九视频热| 国内综合精品午夜久久资源 | 国产午夜一区 | 中文av日韩 | 国产精品v欧美精品v日韩 | 日韩久久精品一区二区三区下载 | 免费黄在线观看 | 91精品国产福利在线观看 | 在线视频一二三 | 麻豆视频免费在线 | 黄色大片视频网站 | 97在线视频观看 | 亚洲日本一区二区在线 | 欧美 亚洲 另类 激情 另类 | av一级一片 | 在线观看国产一区二区 | 日本三级不卡视频 | 在线日韩av | 亚洲成av人片在线观看 | 日日夜夜综合网 | 黄色av一级片 | 亚洲欧美日韩国产精品一区午夜 | 日p在线观看 | 九草在线视频 | 久久国产麻豆 | 欧美aa级 | 亚洲影院色| 婷婷色网视频在线播放 | 婷婷视频在线 | 中文字幕观看在线 | 久久1电影院 | 久草在线综合网 | 久久久91精品国产一区二区三区 | 国产精品 999 | 亚洲精品视频在线观看视频 | 精品久久一区二区三区 | 久久99久久99精品免费看小说 | 久草免费看 | 欧美性超爽 | 欧美成人在线免费 | 久久久久久亚洲精品 | 91看成人| 久久网站最新地址 | 97视频人人澡人人爽 | 亚洲97在线 | 99re8这里有精品热视频免费 | 日日夜夜狠狠操 | av超碰在线观看 | 亚洲国产高清视频 | 婷婷国产一区二区三区 | 成人在线视频观看 | 久久久久福利视频 | 99久久精品国产观看 | 欧美老少交 | 黄色小网站在线观看 | 亚洲精品小区久久久久久 | 日本性动态图 | 99久久精品国产免费看不卡 | 免费网站在线观看人 | 四虎影视成人永久免费观看视频 | 91高清免费观看 | 女人18毛片a级毛片一区二区 | 国产精品久久久久久欧美 | 国产精品免费在线播放 | 欧美一级电影 | 综合国产在线 | 国产亚洲精品免费 | 一区二区三区 中文字幕 | 亚洲最新av网站 | www.天天色.com | 免费视频黄| 国产视频网站在线观看 | 成人免费视频观看 | 中文字幕日本特黄aa毛片 | 综合国产视频 | 中文字幕 成人 | 精品在线视频观看 | 国产xx视频 | 久久69av | 狠狠色丁香婷婷综合最新地址 | 97视频免费在线看 | 国产91免费在线 | 日韩av中文在线 | 久久男人影院 | 91在线www | 91麻豆文化传媒在线观看 | 国产一级视频 | 香蕉视频18 | 色综合网 | 91麻豆国产 | 五月婷丁香 | 久久久网站 | 在线黄色毛片 | 精品国产一区二区三区蜜臀 | 国产亚洲一区二区在线观看 | 欧美激情视频一二三区 | 正在播放五月婷婷狠狠干 | 在线观看91精品国产网站 | 欧美激情精品久久久久久变态 | 精品v亚洲v欧美v高清v | 6699私人影院 | 超碰97.com| 久久久三级视频 | 精品96久久久久久中文字幕无 | 日本少妇视频 | 国产精品一区免费在线观看 | 99这里都是精品 | 久久五月婷婷丁香 | 久操97 | 西西4444www大胆无视频 | 91精品国产92久久久久 | 国产精品一区二区三区视频免费 | 天天操天天干天天插 | 久av电影 | 欧美日韩久 | 国产探花在线看 | 日本久久成人中文字幕电影 | 日韩一区二区三区在线看 | 手机在线欧美 | 色天天天| 国产成人一区二区啪在线观看 | 欧美日韩免费视频 | 91午夜精品 | 欧美精品被| 亚洲jizzjizz日本少妇 | 欧美成人精品三级在线观看播放 | 天天草天天摸 | 亚洲成aⅴ人片久久青草影院 | 久久a久久 | 国产丝袜在线 | 婷婷激情av | 九色91视频| 国产中文字幕国产 | 成人在线观看免费视频 | 日韩精品一区在线观看 | www.婷婷色 | 91看片看淫黄大片 | 亚洲a成人v | 黄色avwww | 韩日三级av| 久久人人爽av | 久久久久久久久久久综合 | 午夜a区| 精品欧美在线视频 | 国产精品久久久免费 | 久久精品国产亚洲精品 | 国产精品一区二区久久精品爱涩 | 色干综合| 一本色道久久精品 | 欧美激情奇米色 | 激情九九 | 欧美 日韩 国产 中文字幕 | 美女网站在线看 | 中文字幕三区 | 国产精品综合av一区二区国产馆 | 在线日韩中文 | 97在线观 | 中文字幕一区二区三区四区视频 | 亚洲经典在线 | av黄色一级片 | 天天搞夜夜骑 | 日韩在线无 | 成人影音av| 国产高清久久久 | 欧美久久综合 | 天天干天天插 | 精品视频专区 | wwwwww色| 99久久er热在这里只有精品66 | 国产精品久久久久久久av电影 | 丁香亚洲 | 天天操天天摸天天爽 | 久久久久久久国产精品影院 | 在线日韩 | 国内精品久久久久国产 | 毛片888| 国产小视频国产精品 | 久久免费国产精品 | av成人免费观看 | 2019中文 | 99成人免费视频 | 日日噜噜噜噜夜夜爽亚洲精品 | 国产视频在 | 91黄色成人 | 91干干干 | 久久亚洲人 | 婷婷六月天在线 | 国产精品黑丝在线观看 | 综合网久久| 中文字幕丝袜美腿 | 四虎国产永久在线精品 | 2023国产精品自产拍在线观看 | 国产精品video爽爽爽爽 | 麻花豆传媒一二三产区 | 啪啪动态视频 | 免费中文字幕在线观看 | 久久久影院一区二区三区 | 亚洲精品tv | 999在线精品 | 91激情视频在线观看 | 国产免费黄色 | 国产一区二区三区在线 | 在线国产不卡 | 亚洲另类视频在线观看 | 国产精品高清在线观看 | 黄色毛片网站在线观看 | 天天色视频 | a资源在线 | 男女拍拍免费视频 | 日本精品久久久一区二区三区 | 天天激情 | 国产尤物视频在线 | 日韩在线观看电影 | 最新午夜 | 日本中文字幕电影在线免费观看 | 欧洲精品视频一区二区 | 久久九九九九 | 国产黑丝一区二区 | 色噜噜在线观看视频 | 免费a网站| 亚洲国产色一区 | 久久久久福利视频 | 日韩高清黄色 | 国产色视频| 午夜美女wwww | 亚洲国产欧美在线人成大黄瓜 | 怡红院av久久久久久久 | 天天视频亚洲 | 日本精品在线视频 | av高清免费| 99久久综合国产精品二区 | 啪啪免费视频网站 | 日韩av免费一区 | 久久最新视频 | 日韩丝袜视频 | 成人一级在线观看 | 亚洲第一av在线播放 | 国产粉嫩在线观看 | 成人午夜影院 | 少妇bbw搡bbbb搡bbbb | 国产糖心vlog在线观看 | av不卡免费看| 欧美日韩在线免费观看视频 | 久久久99国产精品免费 | 亚洲一区日韩在线 | 亚洲黄色av | 四虎国产精品免费观看视频优播 | 亚洲久在线 | 在线观看黄网站 | 五月婷亚洲| 日本在线观看中文字幕无线观看 | 中文字幕在线观看视频一区二区三区 | 热久在线 | 国产99久久九九精品免费 | 国产区精品区 | 在线观看v片 | 天堂久久电影网 | 五月婷婷综合在线 | 最近中文字幕视频完整版 | 久草精品在线播放 | 中国一级片在线观看 | 99久久99热这里只有精品 | 国产五月婷 | 国产午夜精品一区二区三区嫩草 | 国内精品久久久久影院优 | 久久久综合香蕉尹人综合网 | 国产美女精品久久久 | 天天曰天天爽 | 亚洲一区二区视频 | 色五月色开心色婷婷色丁香 | 亚洲成熟女人毛片在线 | 日韩最新中文字幕 | 免费看的视频 | 在线看欧美 | 亚洲人av免费网站 | 99久久精品免费看国产一区二区三区 | 伊人影院在线观看 | 在线观看视频日韩 | www免费网站在线观看 | 热久久免费视频 | 天天操天天射天天爽 | 日韩久久久久久久久久久久 | 91人人澡人人爽人人精品 | 国产 日韩 在线 亚洲 字幕 中文 | 久久96国产精品久久99软件 | 欧美日韩一区二区三区在线免费观看 | 欧美激情第一区 | 成年人黄色av | 狠狠干狠狠操 | 91在线永久 | 久久免费的视频 | 久久精品国产亚洲aⅴ | 高清不卡一区二区在线 | 97人人超碰在线 | 色综合天天狠狠 | 18做爰免费视频网站 | 国产精品一区二区美女视频免费看 | 中文字幕色在线视频 | 久久激情日本aⅴ | 国产精品视频你懂的 | 欧美日韩在线观看一区二区 | 夜夜操网 | 日本三级在线观看中文字 | av在线播放快速免费阴 | 欧美极品久久 | 91视频高清免费 | 久草亚洲视频 | 国产成人黄色片 | 成人久久久久久久久久 | 九九电影在线 | 国产精品高清在线观看 | 在线有码中文 | 日韩在线免费观看视频 | 免费看的黄色录像 | 国产精品免费视频网站 | 久久理论电影网 | 亚洲精品国产精品国自产 | 天天射天天 | 日韩精品一区二 | 精品久久久久国产免费第一页 | 国产亚洲综合精品 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 精品国精品自拍自在线 | 国产中文字幕网 | 黄色一级大片免费看 | 日韩www在线 | 亚洲精品免费看 | 999成人 | 国产一区二区精品在线 | 亚洲国产精品激情在线观看 | 日韩电影在线一区二区 | 91成人免费在线 | 中字幕视频在线永久在线观看免费 | 狠狠色丁香婷婷综合欧美 | 97激情影院 | 国产毛片久久 | 天天弄天天操 | 四川bbb搡bbb爽爽视频 | 亚洲劲爆av| 免费在线观看毛片网站 | 伊人成人激情 | 天天爱天天操天天射 | 国产精品成人一区二区三区 | 免费av大片 | 中文字幕乱码视频 | 色婷婷视频 | 波多野结衣在线观看一区二区三区 | 国产高清久久久久 | 一区二区三区免费在线观看 | 亚欧洲精品视频在线观看 | 五月婷亚洲 | 亚洲色视频| 欧美一级免费 | 欧美日韩中文字幕在线视频 | 在线亚洲天堂网 | 精品久久久久久久久亚洲 | 天堂在线成人 | 久久伦理| 天天曰天天曰 | 日韩在线视频播放 | 99视频精品免费视频 | 免费a级大片 | 久久a国产| 521色香蕉网站在线观看 | 高清av中文在线字幕观看1 | 成人免费观看av | 色视频在线观看免费 | 天天综合操 | 五月天婷婷视频 | 色婷婷欧美 | 亚洲第一香蕉视频 | 国产精品自产拍在线观看 | 欧美精品国产综合久久 | 久久久久久久久久久久久9999 | 玖草影院| 成人在线播放av | 欧美日本三级 | 日韩电影一区二区三区在线观看 | 久久电影网站中文字幕 | 婷五月激情 | 国产免费久久 | 久久超碰在线 | 久草在线最新 | 亚洲日本黄色 | 91精品蜜桃 | 丁香婷婷久久 | 伊人久久在线观看 | 天堂av网站 | 久久精品精品电影网 | v片在线看 | 丁香六月五月婷婷 | 精品国产一区二区三区四区在线观看 | 91亚洲精品乱码久久久久久蜜桃 | 91最新视频 | 欧美激情精品久久久久久 | 亚洲国产精品500在线观看 | 黄色成人小视频 | 色综合久久88色综合天天6 | 五月天久久久久 | 国产亚洲免费观看 | 久久久免费观看完整版 | 日韩精品久久久久 | 成人免费观看完整版电影 | 特黄色大片 | 久久精品国产精品亚洲精品 | 成人性生交大片免费观看网站 | 中文字幕在线第一页 | 日韩a级黄色 | 91伊人久久大香线蕉蜜芽人口 | 日韩视频免费观看高清 | 91最新中文字幕 | 日韩免费在线 | 丁香激情综合久久伊人久久 | 欧美日产一区 | 中文字幕 在线 一 二 | 亚洲国产播放 | 亚洲视屏在线播放 | 中文字幕色在线视频 | 国产精品女 | 91精品在线麻豆 | 成年人国产视频 | 中文字幕色婷婷在线视频 | 在线观看的黄色 | 麻豆成人在线观看 | 欧美精品在线免费 | 777久久久 | 丁香亚洲| 在线免费亚洲 | 日韩高清毛片 | 日韩在线观看第一页 | 亚洲精品乱码白浆高清久久久久久 | 亚洲国产最新 | 亚洲免费国产视频 | 丝袜制服综合网 | 五月花丁香婷婷 | 国产99一区二区 | 丰满少妇在线观看资源站 | 九九九九精品九九九九 | 欧美日韩国产精品一区二区 | 天天操天天能 | 在线免费观看av网站 | 亚洲少妇激情 | 热久久影视| 中文字幕免费播放 | 国产精品人成电影在线观看 | 亚洲理论在线 | 在线观看精品一区 | 天天射日 | 欧美污污网站 | 97免费公开视频 | 在线免费观看黄色 | 91九色视频国产 | 精品电影一区 | 国产二区免费视频 | 91麻豆视频| 国产一级视屏 | 欧美成人亚洲成人 | 69久久久久久久 | 欧美另类69 | 免费在线一区二区 | 黄色软件网站在线观看 | 亚洲一区二区三区在线看 | 在线精品视频免费观看 | 精品99视频 | 一区二区三区高清 | 国产生活一级片 | av东方在线 | 欧美综合久久久 | 免费观看一级成人毛片 | 99久久99久久精品国产片果冰 | 草久电影 | 国产成人一二片 | 国产99久久精品一区二区永久免费 | 亚洲精品视频在线观看网站 | 亚洲精品xx| 永久中文字幕 |