我有使用 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 桌面。所有图像在牧场主上运行良好 有人可以帮忙吗?
我对您的方法持怀疑态度,因为应用程序连接到 multi-az RDS 实例中的单个端点。 AWS 负责故障转移到辅助节点。辅助实例是只读实例,有时应用程序连接到它仅用于读取操作以提高性能。
不过,我在您的代码中发现了一些我想解决的问题。在健康检查部分,您应该使用 try-with-resource 打开连接:
try (Connection connection = eastDataSource().getConnection()) {
if(connection.isValid(5)) {
//rest of the logic
}
}
因为您没有关闭连接,所以它们会耗尽。