SpringBoot+Durid+dynamic-datasource implementa transacciones distribuidas de múltiples fuentes de datos

SpringBoot+Durid+dynamic realiza transacciones distribuidas de múltiples fuentes de datos

inserte la descripción de la imagen aquí

introducción:

En las aplicaciones modernas, se ha convertido en norma utilizar múltiples fuentes de datos para manejar diferentes necesidades comerciales. Sin embargo, manejar transacciones distribuidas entre múltiples fuentes de datos es un problema complejo. Este artículo presentará cómo usar Spring Boot, Druid y dynamic-datasource-spring-boot-starter para implementar transacciones distribuidas con múltiples fuentes de datos.

fondo

En las aplicaciones tradicionales de una sola fuente de datos, la gestión de transacciones es relativamente simple. Pero la gestión de transacciones se vuelve más complicada cuando se incorporan múltiples fuentes de datos. Para resolver este problema, usaremos Spring Boot como el marco básico de la aplicación, Druid como el conjunto de conexiones de la fuente de datos y combinaremos dynamic-datasource-spring-boot-starter para realizar el cambio dinámico de fuentes de datos y la gestión distribuida de transacciones.

Integre el grupo de conexiones de fuentes de datos de Druid y la conmutación dinámica de fuentes de datos dinámicas y la gestión de transacciones distribuidas

Druid es un conjunto de conexiones de base de datos de alto rendimiento con potentes funciones de supervisión y estadísticas. Integrar Druid en un proyecto de Spring Boot es muy simple, solo agregue las dependencias relevantes y configúrelas. La integración se puede realizar a través de los siguientes pasos:

  1. Agregue dependencias al archivo pom.xml.
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.16</version>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.5.2</version>
</dependency>
  1. Configure Druid y parámetros relacionados con la dinámica en el archivo application.properties o application.yml.
spring:
  datasource:
    dynamic:
      # druid连接池设置
      druid:
        # 配置初始化线程数
        initialSize: 5
        # 最小线程数
        minIdle: 5
        # CPU核数+1,也可以大些但不要超过20,数据库加锁时连接过多性能下降
        maxActive: 11
        # 最大等待时间,内网:800,外网:1200(三次握手1s)
        maxWait: 60000
        # 连接可空闲存活时间(ms)
        timeBetweenEvictionRunsMillis: 60000
        # 连接保持空闲而不被驱逐的最长存活时间(ms)
        minEvictableIdleTimeMillis: 300000
        # 用来检测连接是否有效的sql,如果validationQuery为空,那么testOnBorrow、testOnReturn、testWhileIdle这三个参数都不会起作用
        validationQuery: SELECT 1
        # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效;
        testWhileIdle: true
        # 建议配置为false,申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
        testOnBorrow: false
        # 建议配置为false,归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能;
        testOnReturn: false
        # PSCache对支持游标的数据库性能提升巨大
        poolPreparedStatements: true
        # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
        filters: stat
        # 保持minIdle数量的长连接
        keepAlive: true
        # 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。
        # 在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100。缺省值为-1
        maxPoolPreparedStatementPerConnectionSize: 20
        # 是否合并多个DruidDataSource的监控数据
        useGlobalDataSourceStat: true
        # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
      primary: huawei
      datasource:
        # 主库数据源
        master:
          driverClassName: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://10.211.55.6:13306/ddz?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
          username: root
          password: root
        # 从库数据源
        slave:
          driverClassName: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://10.211.55.5:13306/ddz?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
          username: root
          password: root
# MyBatis Plus配置
mybatis-plus:
  # 搜索指定包别名
  typeAliasesPackage: com.ddz.**.domain
  # 配置mapper的扫描,找到所有的mapper.xml映射文件
  mapperLocations: classpath*:mapper/**/*Mapper.xml
  configuration:
    # 使全局的映射器启用或禁用缓存
    cache-enabled: true
    # 允许JDBC 支持自动生成主键
    use-generated-keys: true
    # 配置默认的执行器.SIMPLE就是普通执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新
    default-executor-type: simple
    # 指定 MyBatis 所用日志的具体实现
    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
    # 使用驼峰命名法转换字段
    map-underscore-to-camel-case: true
  1. Crear dos clases de Mapper y clases de entidad
@DS("master")
public interface UserMapper extends BaseMapper<User> {
    
    
    /**
     * 新增数据
     *
     * @param user 实例对象
     * @return 影响行数
     */
    int insertSql(User user);

}

@DS("slave")
public interface ScoreMapper extends BaseMapper<Score> {
    
    
    /**
     * 新增数据
     *
     * @param score 实例对象
     * @return 影响行数
     */
    int insertSql(Score score);
}

@Data
public class User {
    
    

    private Integer id;

    private String name;

    private Integer balance;

    private String remark;

    public User(String name, Integer balance) {
    
    
        this.name = name;
        this.balance = balance;
    }
}

@Data
public class Score {
    
    
    private Integer id;

    private Integer count;

    public Score(Integer count) {
    
    
        this.count = count;
    }
}
  1. Escriba la prueba del controlador
    y agregue la anotación @DSTransactional al método para realizar la gestión de transacciones
@RestController
@RequestMapping("user")
public class UserController {
    
    

    @Resource
    private UserMapper userMapper;

    @Resource
    private ScoreMapper scoreMapper;

    @GetMapping("/get")
    @DSTransactional
    public void get() {
    
    
    	// 随机触发异常检查两个数据源的一致性
        int i = random.nextInt(10);
        userMapper.insertSql(new User("ddz", i));
        scoreMapper.insertSql(new Score(i*10));
        Random random = new Random();
        if (i % 2 == 0) {
    
    
            i = 1 / 0;
        }
    }
}
  1. Necesita agregar una clase de configuración para la página de monitoreo durid
@Configuration
public class DruidConfig {
    
    
    /**
     * 配置Druid 监控启动页面
     *
     * @return servletRegistrationBean
     */
    @Bean
    @ConditionalOnMissingBean
    public ServletRegistrationBean<Servlet> druidStartViewServlet() {
    
    
        ServletRegistrationBean<Servlet> servletRegistrationBean = new ServletRegistrationBean<Servlet>(new StatViewServlet(), "/druid/*");
        // 白名单
        servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
        // 黑名单
//        servletRegistrationBean.addInitParameter("deny", "");
        // 登录查看信息的账密,用于登录Druid监控后台
        servletRegistrationBean.addInitParameter("loginUsername", "ddz");
        servletRegistrationBean.addInitParameter("loginPassword", "ddz2023");
        // 是否能够重置数据
        servletRegistrationBean.addInitParameter("resetEnable", "true");
        return servletRegistrationBean;
    }
 
    /**
     * Druid监控过滤器配置规则
     * ConditionalOnMissingBean 防止注册相同的bean
     *
     * @return filterFilterRegistrationBean
     */
    @Bean
    @ConditionalOnMissingBean
    public FilterRegistrationBean<Filter> filterRegistrationBean() {
    
    
        FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>();
        filterFilterRegistrationBean.setFilter(new WebStatFilter());
        // 添加过滤规则
        filterFilterRegistrationBean.addUrlPatterns("/*");
        // 添加不需要忽略的格式信息
        filterFilterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        return filterFilterRegistrationBean;
    }
}
  1. Después de agregar la clase de configuración, reinicie el proyecto e ingrese http://127.0.0.1:8080/druid/login.html en la página para acceder
    inserte la descripción de la imagen aquí

Consideraciones para Transacciones Distribuidas

Cuando utilice múltiples fuentes de datos para transacciones distribuidas, debe prestar atención a los siguientes puntos:

  • La corrección de la configuración de la fuente de datos y la gestión de transacciones, incluida la información de conexión de la fuente de datos, el comportamiento de propagación de transacciones, etc.
  • La estrategia de tiempo de espera y reversión de la transacción garantiza que la transacción se complete o se revierta en un tiempo adecuado.
  • Para escenarios que requieren alta consistencia de datos entre diferentes fuentes de datos, se pueden usar administradores de transacciones distribuidas (como Atomikos, Bitronix, etc.) para implementar transacciones XA.
  • Para diferentes tipos de bases de datos, es posible que sea necesario configurar controladores y administradores de transacciones específicos.

Resumir

Este artículo describe cómo usar Spring Boot, Druid y dynamic-datasource-spring-boot-starter para implementar transacciones distribuidas con múltiples fuentes de datos. Al configurar correctamente los grupos de conexiones de fuentes de datos, las fuentes de datos dinámicas y la gestión de transacciones, podemos tratar de manera efectiva los problemas de consistencia de las transacciones en múltiples fuentes de datos. En aplicaciones prácticas, se puede seleccionar una solución adecuada de gestión de transacciones distribuidas de acuerdo con escenarios y requisitos específicos.

Referencias:

documentación de dynamic-datasource-spring-boot-starter: https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611
Documentación de gestión de transacciones de Spring: https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction
Documentación oficial de Druid: https://github.com/alibaba /druid

Supongo que te gusta

Origin blog.csdn.net/weixin_45626288/article/details/131257905
Recomendado
Clasificación