Seata初试采坑

背景:

公司采用dubbo的分布式服务,现在业务上遇到了分布式事物的问题,和同事讨论改过几个分布式的开源框架,最终采用了seata,seata的性能相对比较客观,但是回滚情境下,性能下降特别厉害,这个与seata的网络IO和数据IO多次交互相关。具体各种分布式事物管理的开源框架推荐一篇文章可以参考:若干分布式事务框架”与“我的偏见”( 测试/分析),此处我们部署搭建是最新的1.1.0版本。

采坑之路:

1.Seata Server搭建部署

服务端搭建,可以直接从seata开源社区下载最新版本,下载的文件可以直接在服务器解压部署,自行修改配置文件自己的环境参数主要是下图的file.conf和register.conf
在这里插入图片描述
register.conf 主要配置如下:

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  # 注册中心采用的zk
  type = "zk"

  nacos {
    serverAddr = "localhost"
    namespace = ""
    cluster = "default"
  }
  eureka {
    serviceUrl = "http://localhost:8761/eureka"
    application = "default"
    weight = "1"
  }
  redis {
    serverAddr = "localhost:6379"
    db = "0"
  }
  zk {
    cluster = "default"
    # 集群配置采用,隔开
    serverAddr = "127.0.0.1:2181,127.0.0.1:2183,127.0.0.1:2183"
    session.timeout = 6000
    connect.timeout = 2000
  }
  consul {
    cluster = "default"
    serverAddr = "127.0.0.1:8500"
  }
  etcd3 {
    cluster = "default"
    serverAddr = "http://localhost:2379"
  }
  sofa {
    serverAddr = "127.0.0.1:9603"
    application = "default"
    region = "DEFAULT_ZONE"
    datacenter = "DefaultDataCenter"
    cluster = "default"
    group = "SEATA_GROUP"
    addressWaitTime = "3000"
  }
  file {
    name = "file.conf"
  }
}

config {
  # file、nacos 、apollo、zk、consul、etcd3
  # 这边的配置没有对接配置中心所以采用file的方式配置,这边配置之后会自动读取file文件的配置
  type = "file" 

  nacos {
    serverAddr = "localhost"
    namespace = ""
    group = "SEATA_GROUP"
  }
  consul {
    serverAddr = "127.0.0.1:8500"
  }
  apollo {
    app.id = "seata-server"
    apollo.meta = "http://192.168.1.204:8801"
    namespace = "application"
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  file {
    name = "file.conf"
  }
}

file.conf 配置如下

store {
  ## store mode: file、db
  # 存储我们设置为db模式
  mode = "db"

  ## file store property
  file {
    ## store location dir
    dir = "sessionStore"
    # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
    maxBranchSessionSize = 16384
    # globe session size , if exceeded throws exceptions
    maxGlobalSessionSize = 512
    # file buffer size , if exceeded allocate new buffer
    fileWriteBufferCacheSize = 16384
    # when recover batch read size
    sessionReloadReadSize = 100
    # async, sync
    flushDiskMode = async
  }
  ## database store property
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
    datasource = "druid"
    ## mysql/oracle/h2/oceanbase etc.
    dbType = "mysql"
    driverClassName = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://127.0.01:3306/seata"
    user = "admin"
    password = "admin"
    minConn = 1
    maxConn = 10
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
  }
}

修改相关的配置文件之后,可以直接在bin目录下执行相应的shell启动脚本。

2.客户端配置

  • 依赖引用
<dependency>
     <groupId>io.seata</groupId>
     <artifactId>seata-spring-boot-starter</artifactId>
     <version>1.1.0</version>
   </dependency>
   # seata zk连接主要依赖zkclient
   <dependency>
     <groupId>com.101tec</groupId>
     <artifactId>zkclient</artifactId>
     <version>0.11</version>
   </dependency>
  • yml 配置
seata:
 enabled: true
 application-id: ${spring.application.name}  # 可以自定义
 tx-service-group: seller_seata_group # 此处的名称一定要与 vgroup-mapping下配置的参数保持一致
 registry:
   file:
    #因为seata版本驼峰参数映射有问题导致,seata的zk配置参数设置不上导致异常,所以采用了file方式
     name: registry-test.conf 
 service:
 	grouplist:
 	    # seata seaver的 地址配置,此处可以集群配置是个数组
     	default: 127.0.0.1:8091
   vgroup-mapping:
     # key一定要与tx-service-group一致
     seller_seata_group: default
# 这边自动代理关掉是因为,, seata源码中SeataDataSourceBeanPostProcessor的初始化要比我的datasource初始话晚,导致datasoure不会被包装为代理类,此处我自己代码做了处理
 enableAutoDataSourceProxy: false

配置注意点:

  • tx-service-group配置的值一定要与vgroup-mapping配置下的key保持一致,否则会异常

    [2020-03-13 14:44:00,188] ERROR [timeoutChecker_2      ]                                    i.s.c.r.n.NettyClientChannelManager:170 - [ ] no available service 'null' found, please make sure registry config correct
    
    

    具体异常发生在NettyClientChannelManager,在执行void reconnect(String transactionServiceGroup)时去查询transactionServiceGroup下的服务是查询不到的,所以打印了错误日志,也就是意味着此时seata的事物管理是失效的。

  • 最新seata-spring-boot-starter支持在yml中直接配置相关注册中心的一些参数配置,目前1.1.0存在驼峰转化上的异常,这边跟踪源码得出的兵并且已经提了issues,于1.2.0 版本修复,临时解决方案为seata.registry.file.name=register.conf注册中心配置为读取file文件register.conf

    
    registry {
      # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
      # 注册中心采用的zk
      type = "zk"
      zk {
        cluster = "default"
        # 集群配置采用,隔开
        serverAddr = "127.0.0.1:2181,127.0.0.1:2183,127.0.0.1:2183"
        session.timeout = 6000
        connect.timeout = 2000
      }
    }
    
  • DataSource需要包装成seat的DataSourceProxy代理,这边seata-spring-boot-starter 是通过Spring的BeanPostProcessor后置在类初始化之后去包装DataSource,相关源码可参照SeataDataSourceBeanPostProcessor,但是我遇到的情况是我的DataSource初始化要在这个SeataDataSourceBeanPostProcessor之前,public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansExceptio 拦截不到DataSource类型的对象,所以这边我们选择了关闭掉数据源自动代理,(如果开启的情况下,自行去封装代理DataSourceProxy,代码会抛异常)自己去实现代理的封装,此处采用了aop拦截去处理的。主要代码如下:

    @Configuration
    @Import({DataSourceAspect.class})
    public class DataSourceConfig {
    
        @Autowired
        private DbPasswordCallback dbPasswordCallback;
    
    
        @Bean
        @ConfigurationProperties("spring.datasource.druid")
        public DataSource dataSource() {
            DruidDataSource druidDataSource = new DruidDataSource();
            druidDataSource.setPasswordCallback(dbPasswordCallback);
            druidDataSource.setConnectionInitSqls(Collections.singletonList("set names utf8mb4;"));
            return druidDataSource;
        }
    
        @Bean
        public DataSourceProxy dataSourceProxy(DataSource dataSource){
            return new DataSourceProxy(dataSource);
        }
    
    }
    
    @Aspect
    public class DataSourceAspect {
    
        @Resource
        private ConfigurableEnvironment environment;
    
        @Pointcut("@annotation(org.springframework.context.annotation.Bean)")
        public void pointCut() {
        }
    
        @Around(value = "pointCut() && @annotation(configProperties)")
        public Object around(ProceedingJoinPoint joinPoint, ConfigurationProperties configProperties) throws Throwable {
            Object object = joinPoint.proceed();
            if (null != object && (object instanceof DataSource)) {
                DataSource dataSource = (DataSource) object;
                bindDataSource(dataSource, configProperties.value());
                return new DataSourceProxy(dataSource);
            }
            return object;
        }
    
        private void bindDataSource(DataSource dataSource, String name) {
            Binder binder = Binder.get(environment);
            Bindable<DataSource> bindable = Bindable.ofInstance(dataSource);
            binder.bind(name, bindable);
        }
    
    }
    
    
    • 采用最新的seata版本,mysql驱动版本是有要求的,存在版本不兼容的问题,这个文档是没有说明的,我们需要升级到mysql驱动至少在6.0以上,否则遇到时间处理上,会异常我这边提了issues

通过上面的一系列操作,我们就可以正常的开始我们的seata之旅了。

在这里插入图片描述

发布了41 篇原创文章 · 获赞 14 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Yunwei_Zheng/article/details/104839881