• 分享一个seata demo,讲两个个问题


      Seata,阿里开源的分布式事务框架,多的我就不介绍了,了解详细介绍,请看官网。seata spring boot入门,可以看我上一篇博客《Spring boot微服务如何集成fescar解决分布式事务问题?》(fescar后来更名为seata)。

      本篇,将介绍,同时使用seata的tcc模式和at模式的一些问题。点击demo,可查看相关源代码。

    第一个问题:数据源使用seata代理的数据源,同时使用TCC模式,将导致注册到TC的分支事务多一倍

      

      在上一篇博客中,我们说到,要让分支事务加入全局事务,需要分支事务rm获得全局事务的xid,所以我们通过feign将xid传递到下游的微服务。但是AT模式的rm在下游服务的代理数据源处,TCC模式的rm在上游服务的TccAction处做的代理。此时要解决分支事务重复注册的问题,在使用TCC模式的时候就不能把xid传递到下游服务,这样,下游服务数据源代理处判断到这个数据库操作不在全局事务中,就不会向TC注册。

      解决的办法:我们在feign header里加入一个标识,标志此请求是不是TCC模式的请求,如果是,则不将xid传递到下游服务。

    @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模式有一个点需要注意,

        @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 1139

    在目前的版本0.6.1,已经支持TC的高可用吗,但这个bug还没有解决,如果使用类型判断去做转换来修复这个bug,预计会写很多if else。官方回复的解决办法是,他们会修改序列化的协议来解决这个bug。期待官方尽快修复这个bug。

  • 相关阅读:
    数制
    转移指令检测题9
    转移指令笔记(1)
    汇编笔记
    汇编语言学习笔记
    C++中的虚函数
    windows程序设计(四)
    windows程序设计(三)
    windows程序设计(二)
    通过Url网络编程实现下载
  • 原文地址:https://www.cnblogs.com/DKSL/p/seata-demo.html
Copyright © 2020-2023  润新知