使用 hikari 切换到辅助 RDS 区域时出错

问题描述 投票:0回答:1

我有使用 hikari 创建数据源的现有代码。 我正在努力添加新功能,如果当前 rds 关闭,应用程序应切换到辅助 rds。 在 docker compose 文件中,我添加了新图像以进行本地测试 为此,我更新了 docker-compose.yml 文件,如下

version: '3.7'

services:
  mysql-east:
    image: docker.ouroath.com:4443/docker.io/library/mysql:8.0.35
    environment:
      - MYSQL_ALLOW_EMPTY_PASSWORD=yes
    ports:
      - '3306:3306'  # Exposing MySQL for east region on port 3306
    volumes:
      - ./mysql-east-dump:/docker-entrypoint-initdb.d
    networks:
      - app-network

  mysql-west:
    image: docker.ouroath.com:4443/docker.io/library/mysql:8.0.35
    environment:
      - MYSQL_ALLOW_EMPTY_PASSWORD=yes
    ports:
      - '3307:3306'  # Exposing MySQL for west region on port 3307
    volumes:
      - ./mysql-west-dump:/docker-entrypoint-initdb.d
    networks:
      - app-network

  mockServer:
    image: docker.ouroath.com:4443/docker.io/mockserver/mockserver:mockserver-5.13.1
    ports:
      - 1080:1080
    environment:
      MOCKSERVER_PROPERTY_FILE: /config/mockserver.properties
      MOCKSERVER_INITIALIZATION_JSON_PATH: /config/initializerJson.json
    volumes:
      - './config:/config'
    networks:
      - app-network

  redis:
    image: docker.ouroath.com:4443/cloud/redis-docker:20211022-215552
    ports:
      - "6379:6379"
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

之后我更新了具有辅助数据库属性的 application.yml 文件

db:
  east:
    host: "mysql-east"  # Using the Docker service name
    port: 3306
    database: senderhub
    username: root
    password: ""  # Empty password as per Docker Compose configuration
    region: us-east-1

  west:
    host: "mysql-west"  # Using the Docker service name
    port: 3307
    database: senderhub
    username: root
    password: ""  # Empty password as per Docker Compose configuration
    region: us-west-2

我更新了DataSourceConfiguration.java以支持切换到辅助rds

@Configuration
public class DataSourceConfiguration {

    private static final Logger logger = LoggerFactory.getLogger(DataSourceConfiguration.class);

    @Value("${db.east.host}")
    private String hostEast;

    @Value("${db.east.port}")
    private String portEast;

    @Value("${db.east.database}")
    private String databaseEast;

    @Value("${db.east.username}")
    private String usernameEast;

    @Value("${db.east.password}")
    private String passwordEast;

    @Value("${db.east.region}")
    private String regionEast;

    @Value("${db.west.host}")
    private String hostWest;

    @Value("${db.west.port}")
    private String portWest;

    @Value("${db.west.database}")
    private String databaseWest;

    @Value("${db.west.username}")
    private String usernameWest;

    @Value("${db.west.password}")
    private String passwordWest;

    @Value("${db.west.region}")
    private String regionWest;

    private HikariDataSource eastDataSource;
    private HikariDataSource westDataSource;

    @PostConstruct
    public void initialize() {
        logger.info("Initializing DataSourceConfiguration for east and west databases.");
    }

    // Create a DataSource for us-east-1
    @Bean
    public DataSource eastDataSource() {
        logger.info("Configuring east DB: host={}, port={}, database={}, username={}, region={}",
                hostEast, portEast, databaseEast, usernameEast, regionEast);

        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setJdbcUrl("jdbc:mysql://" + hostEast + ":" + portEast + "/" + databaseEast);
        hikariConfig.setUsername(usernameEast);
        hikariConfig.setPassword(passwordEast);  // This will handle the empty password scenario
        applyHikariSettings(hikariConfig);
        eastDataSource = new HikariDataSource(hikariConfig);

        logger.info("East DB configured successfully.");
        return eastDataSource;
    }

    // Create a DataSource for us-west-2
    @Bean
    public DataSource westDataSource() {
        logger.info("Configuring west DB: host={}, port={}, database={}, username={}, region={}",
                hostWest, portWest, databaseWest, usernameWest, regionWest);

        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setJdbcUrl("jdbc:mysql://" + hostWest + ":" + portWest + "/" + databaseWest);
        hikariConfig.setUsername(usernameWest);
        hikariConfig.setPassword(passwordWest);  // This will handle the empty password scenario
        applyHikariSettings(hikariConfig);
        westDataSource = new HikariDataSource(hikariConfig);

        logger.info("West DB configured successfully.");
        return westDataSource;
    }

    // Apply common Hikari settings for both DataSources
    private void applyHikariSettings(HikariConfig hikariConfig) {
        hikariConfig.setConnectionTimeout(60000);  // 60 seconds
        hikariConfig.setValidationTimeout(5000);   // 5 seconds
        hikariConfig.setMaxLifetime(1800000);      // 30 minutes
        hikariConfig.setIdleTimeout(600000);       // 10 minutes
        hikariConfig.setInitializationFailTimeout(0);  // Don't fail on initialization

        logger.debug("HikariCP settings applied: connectionTimeout=60000, validationTimeout=5000, maxLifetime=1800000, idleTimeout=600000");
    }

    @Bean
    @Primary
    public DataSource routingDataSource() {
        logger.info("Setting up DynamicRoutingDataSource.");

        DynamicRoutingDataSource routingDataSource = new DynamicRoutingDataSource();

        // Configure routing data sources
        HashMap<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("east", eastDataSource());
        targetDataSources.put("west", westDataSource());

        routingDataSource.setTargetDataSources(targetDataSources);
        routingDataSource.setDefaultTargetDataSource(eastDataSource());  // Start with east DB

        // Schedule a health check every 30 seconds
        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
            try {
                logger.debug("Checking health of the east DB...");
                if (eastDataSource().getConnection().isValid(5)) {
                    if (!routingDataSource.isEastDbUp()) {
                        routingDataSource.switchToEastDb();
                        logger.info("East DB is back up. Switched to east region.");
                    } else {
                        logger.debug("East DB is healthy and already in use.");
                    }
                }
            } catch (Exception e) {
                if (routingDataSource.isEastDbUp()) {
                    logger.warn("East DB is down. Switching to west region.");
                    routingDataSource.switchToWestDb();
                } else {
                    logger.debug("East DB is still down, staying on west DB.");
                }
            }
        }, 30, 30, TimeUnit.SECONDS);

        return routingDataSource;
    }
}

这里我使用

AbstractRoutingDataSource
进行动态数据源路由。
DynamicRoutingDataSource.java
用于动态路由

public class DynamicRoutingDataSource extends AbstractRoutingDataSource {

    private static final Logger logger = LoggerFactory.getLogger(DynamicRoutingDataSource.class);

    private boolean isEastDbUp = true;  // Start with east DB assumed up

    @Override
    protected Object determineCurrentLookupKey() {
        logger.debug("Determining current DB region: {}", isEastDbUp ? "east" : "west");
        return isEastDbUp ? "east" : "west";
    }

    public void switchToEastDb() {
        logger.info("Switching to the east DB...");
        isEastDbUp = true;
    }

    public void switchToWestDb() {
        logger.warn("Switching to the west DB...");
        isEastDbUp = false;
    }

    public boolean isEastDbUp() {
        return isEastDbUp;
    }
}

请注意,为了节省时间,我没有添加导入语句。 早些时候,当我使用任何 api 时,它都在工作,因为新的更改 API 没有得到任何响应。 我在本地使用 rancher 桌面。所有图像在牧场主上运行良好 有人可以帮忙吗?

amazon-web-services spring-boot jdbc hikaricp
1个回答
0
投票

我对您的方法持怀疑态度,因为应用程序连接到 multi-az RDS 实例中的单个端点。 AWS 负责故障转移到辅助节点。辅助实例是只读实例,有时应用程序连接到它仅用于读取操作以提高性能。

不过,我在您的代码中发现了一些我想解决的问题。在健康检查部分,您应该使用 try-with-resource 打开连接:

try (Connection connection = eastDataSource().getConnection()) {
   if(connection.isValid(5)) {
      //rest of the logic
   }

}

因为您没有关闭连接,所以它们会耗尽。

© www.soinside.com 2019 - 2024. All rights reserved.