分享一個seata demo,講兩個個問題

  Seata,阿里開源的分佈式事務框架,多的我就不介紹了,瞭解詳細介紹,請看官網。seata spring boot入門,能夠看我上一篇博客《Spring boot微服務如何集成fescar解決分佈式事務問題?》(fescar後來改名爲seata)。html

  本篇,將介紹,同時使用seata的tcc模式和at模式的一些問題。點擊demo,可查看相關源代碼。git

第一個問題:數據源使用seata代理的數據源,同時使用TCC模式,將致使註冊到TC的分支事務多一倍

  

  在上一篇博客中,咱們說到,要讓分支事務加入全局事務,須要分支事務rm得到全局事務的xid,因此咱們經過feign將xid傳遞到下游的微服務。可是AT模式的rm在下游服務的代理數據源處,TCC模式的rm在上游服務的TccAction處作的代理。此時要解決分支事務重複註冊的問題,在使用TCC模式的時候就不能把xid傳遞到下游服務,這樣,下游服務數據源代理處判斷到這個數據庫操做不在全局事務中,就不會向TC註冊。github

  解決的辦法:咱們在feign header里加入一個標識,標誌此請求是否是TCC模式的請求,若是是,則不將xid傳遞到下游服務。spring

@Component public class RequestHeaderInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder .getRequestAttributes(); boolean seataTransactionATMode = true; if (attributes!=null) { HttpServletRequest request = attributes.getRequest(); Enumeration<String> headerNames = request.getHeaderNames(); if (headerNames != null) { Map<String, Collection<String>> resolvedHeaders = new CaseInsensitiveKeyMap<>(); resolvedHeaders.putAll(template.headers()); while (headerNames.hasMoreElements()) { String name = headerNames.nextElement(); if (!resolvedHeaders.containsKey(name)) { String values = request.getHeader(name); List<String> headers = new ArrayList<String>(); headers.addAll(Arrays.asList(values)); resolvedHeaders.put(name, headers); } } template.headers(null); template.headers(resolvedHeaders); } } Map<String, Collection<String>> headers = template.headers(); if(headers!=null){ Collection<String> values = headers.getOrDefault(SeataConstants.TRANSACTION_MODE_HEADER,null); if (values==null) { values = headers.getOrDefault(SeataConstants.TRANSACTION_MODE_HEADER.toLowerCase(),null); } if(values!=null&&values.contains("TCC")){ seataTransactionATMode = false; } } if(seataTransactionATMode) { String xid = RootContext.getXID(); if (StringUtils.isNotBlank(xid)) { template.header(SeataConstants.XID_HEADER, xid); } } } }

 

 

使用tcc模式有一個點須要注意,sql

@TwoPhaseBusinessAction(name = "CreateOrderTccAction" , commitMethod = "commit", rollbackMethod = "rollback") public boolean prepare(BusinessActionContext actionContext, List<SoMaster> soMasters, @BusinessActionContextParameter(paramName = "SoSysNos") String soSysNos) throws BusinessException;

那就是BusinessActionContextParameter儘可能使用簡單類型,若是是複雜類型,在註冊分支事務時會被序列化成json字符串,把上下文數據存到session。提交或者重試的時候,從actionContext獲取參數的時候actionContext.getActionContext("your argument")返回的是個object對象,此對象是個jsonObject,沒法直接轉爲複雜類型,須要tostring,再json反序列化。數據庫

第二個問題:在目前的undolog序列化協議中,數據庫裏bigint類型的數據,被序列化後,再在undo回滾時反序列化回object類型,真實的值類型變成了int型

{"branchId":2013531184,"sqlUndoLogs":[{"afterImage":{"rows":[{"fields":[{"keyType":"PrimaryKey","name":"sysno","type":-5,"value":1},{"keyType":"NULL","name":"available_qty","type":4,"value":999992},{"keyType":"NULL","name":"allocated_qty","type":4,"value":8}]}],"tableName":"inventory"},"beforeImage":{"rows":[{"fields":[{"keyType":"PrimaryKey","name":"sysno","type":-5,"value":1},{"keyType":"NULL","name":"available_qty","type":4,"value":999994},{"keyType":"NULL","name":"allocated_qty","type":4,"value":6}]}],"tableName":"inventory"},"sqlType":"UPDATE","tableName":"inventory"}],"xid":"172.16.4.137:8091:2013531176"}

如上,{"keyType":"PrimaryKey","name":"sysno","type":-5,"value":1},sysno,type -5表示這是一個bigint的類型,反序列化後,value的真正值類型是int型,這個問題能夠參考issue 1139json

在目前的版本0.6.1,已經支持TC的高可用嗎,但這個bug尚未解決,若是使用類型判斷去作轉換來修復這個bug,預計會寫不少if else。官方回覆的解決辦法是,他們會修改序列化的協議來解決這個bug。期待官方儘快修復這個bug。session

原文出處:https://www.cnblogs.com/DKSL/p/seata-demo.htmlapp

相關文章
相關標籤/搜索