有时您希望应用程序具有更多的弹性,而不是依赖于单个数据库实例。有多种方法可以做到这一点,但最近我遇到了一种非常简单的方法,可以使用 JDBC 对任何 Java 后端启用故障转移和负载平衡。该库称为 HA-JDBC,允许您透明地将调用路由到多个数据源。它会自动复制所有写调用(使用主从机制)并对所有读调用进行负载平衡。如果数据源被删除,它也可以在恢复时重建数据源。
请注意,您始终需要为所有数据源使用相同的用户名。使用此配置,默认情况下,它会每分钟检查是否可以再次启用任何禁用的数据库(由于连接失败),并将重新同步这些数据库。
这是通过故障转移和负载平衡提供高可用性的非常简单的方法。
import net.sf.hajdbc.SimpleDatabaseClusterConfigurationFactory import net.sf.hajdbc.SynchronizationStrategy import net.sf.hajdbc.balancer.BalancerFactory import net.sf.hajdbc.balancer.random.RandomBalancerFactory import net.sf.hajdbc.balancer.roundrobin.RoundRobinBalancerFactory import net.sf.hajdbc.balancer.simple.SimpleBalancerFactory import net.sf.hajdbc.cache.DatabaseMetaDataCacheFactory import net.sf.hajdbc.cache.eager.EagerDatabaseMetaDataCacheFactory import net.sf.hajdbc.cache.eager.SharedEagerDatabaseMetaDataCacheFactory import net.sf.hajdbc.cache.lazy.LazyDatabaseMetaDataCacheFactory import net.sf.hajdbc.cache.lazy.SharedLazyDatabaseMetaDataCacheFactory import net.sf.hajdbc.cache.simple.SimpleDatabaseMetaDataCacheFactory import net.sf.hajdbc.sql.Driver import net.sf.hajdbc.sql.DriverDatabase import net.sf.hajdbc.sql.DriverDatabaseClusterConfiguration import net.sf.hajdbc.state.StateManagerFactory import net.sf.hajdbc.state.bdb.BerkeleyDBStateManagerFactory import net.sf.hajdbc.state.simple.SimpleStateManagerFactory import net.sf.hajdbc.state.sql.SQLStateManagerFactory import net.sf.hajdbc.state.sqlite.SQLiteStateManagerFactory import net.sf.hajdbc.sync.* import net.sf.hajdbc.util.concurrent.cron.CronExpression import org.springframework.boot.autoconfigure.condition.ConditionalOnClass import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.context.annotation.Configuration import javax.annotation.PostConstruct @ConfigurationProperties(prefix="hajdbc") @ConditionalOnClass(Driver) @Configuration class HaJdbcConfiguration { List<DriverDatabase> driverDatabases; String clusterName = "default" String cronExpression = "0 0/1 * 1/1 * ? *" String balancerFactory = "round-robin" String defaultSynchronizationStrategy = "full" String databaseMetaDataCacheFactory = "shared-eager" String stateManagerFactory = "simple" String stateManagerUrl String stateManagerUser String stateManagerPassword String stateManagerLocation boolean identityColumnDetectionEnabled = true boolean sequenceDetectionEnabled = true @PostConstruct def register() { DriverDatabaseClusterConfiguration config = new DriverDatabaseClusterConfiguration(); config.setDatabases(driverDatabases); config.databaseMetaDataCacheFactory = DatabaseMetaDataCacheChoice.fromId(databaseMetaDataCacheFactory) config.balancerFactory = BalancerChoice.fromId(balancerFactory) config.stateManagerFactory = StateManagerChoice.fromId(stateManagerFactory) switch(stateManagerFactory) { case "sql": def mgr = config.stateManagerFactory as SQLStateManagerFactory if(stateManagerUrl) mgr.urlPattern = stateManagerUrl if(stateManagerUser) mgr.user = stateManagerUser if(stateManagerPassword) mgr.password = stateManagerPassword break; case "berkeleydb": case "sqlite": def mgr = config.stateManagerFactory as BerkeleyDBStateManagerFactory if(stateManagerLocation) mgr.locationPattern = stateManagerLocation break; } config.synchronizationStrategyMap = SynchronizationStrategyChoice.idMap config.defaultSynchronizationStrategy = defaultSynchronizationStrategy config.identityColumnDetectionEnabled = identityColumnDetectionEnabled config.sequenceDetectionEnabled = sequenceDetectionEnabled config.setAutoActivationExpression(new CronExpression(cronExpression)) Driver.setConfigurationFactory(clusterName, new SimpleDatabaseClusterConfigurationFactory<java.sql.Driver, DriverDatabase>(config)); } static enum DatabaseMetaDataCacheChoice { SIMPLE(new SimpleDatabaseMetaDataCacheFactory()), LAZY(new LazyDatabaseMetaDataCacheFactory()), EAGER(new EagerDatabaseMetaDataCacheFactory()), SHARED_LAZY(new SharedLazyDatabaseMetaDataCacheFactory()), SHARED_EAGER(new SharedEagerDatabaseMetaDataCacheFactory()); DatabaseMetaDataCacheFactory databaseMetaDataCacheFactory; DatabaseMetaDataCacheChoice(DatabaseMetaDataCacheFactory databaseMetaDataCacheFactory) { this.databaseMetaDataCacheFactory = databaseMetaDataCacheFactory } static DatabaseMetaDataCacheFactory fromId(String id) { values().find { it.databaseMetaDataCacheFactory.id == id }.databaseMetaDataCacheFactory } } static enum BalancerChoice { ROUND_ROBIN(new RoundRobinBalancerFactory()), RANDOM(new RandomBalancerFactory()), SIMPLE(new SimpleBalancerFactory()); BalancerFactory balancerFactory; BalancerChoice(BalancerFactory balancerFactory) { this.balancerFactory = balancerFactory } static BalancerFactory fromId(String id) { values().find { it.balancerFactory.id == id }.balancerFactory } } static enum SynchronizationStrategyChoice { FULL(new FullSynchronizationStrategy()), DUMP_RESTORE(new DumpRestoreSynchronizationStrategy()), DIFF(new DifferentialSynchronizationStrategy()), FASTDIFF(new FastDifferentialSynchronizationStrategy()), PER_TABLE_FULL(new PerTableSynchronizationStrategy(new FullSynchronizationStrategy())), PER_TABLE_DIFF(new PerTableSynchronizationStrategy(new DifferentialSynchronizationStrategy())), PASSIVE(new PassiveSynchronizationStrategy()); SynchronizationStrategy synchronizationStrategy; SynchronizationStrategyChoice(SynchronizationStrategy synchronizationStrategy) { this.synchronizationStrategy = synchronizationStrategy } static SynchronizationStrategy fromId(String id) { values().find { it.synchronizationStrategy.id == id }.synchronizationStrategy } static Map<String, SynchronizationStrategy> getIdMap() { return values().collectEntries { [(it.synchronizationStrategy.id):it.synchronizationStrategy] } } } static enum StateManagerChoice { SIMPLE(new SimpleStateManagerFactory()), BERKELEYDB(new BerkeleyDBStateManagerFactory()), SQLITE(new SQLiteStateManagerFactory()), SQL(new SQLStateManagerFactory()); private StateManagerFactory stateManagerFactory; StateManagerChoice(StateManagerFactory stateManagerFactory) { this.stateManagerFactory = stateManagerFactory } static StateManagerFactory fromId(String id) { values().find { it.stateManagerFactory.id == id }.stateManagerFactory } } }
当您的 Spring Boot 应用程序选择此类时,您可以通过配置其数据库和 Spring Boot 在包含 JDBC 支持时提供的标准数据源,将 Spring Boot 应用程序配置为使用 HA-JDBC。例如,此配置创建一个在两个 MySQL 数据库之间复制的数据源。
spring.datasource.url=jdbc:ha-jdbc:default spring.datasource.username=root spring.datasource.driver-class-name=net.sf.hajdbc.sql.Driver hajdbc.driverDatabases[0].id=db1 hajdbc.driverDatabases[0].location=jdbc:mysql://localhost/hatest1 hajdbc.driverDatabases[0].user=root hajdbc.driverDatabases[1].id=db2 hajdbc.driverDatabases[1].location=jdbc:mysql://localhost/hatest2 hajdbc.driverDatabases[1].user=root
请注意,您始终需要为所有数据源使用相同的用户名。使用此配置,默认情况下,它会每分钟检查是否可以再次启用任何禁用的数据库(由于连接失败),并将重新同步这些数据库。
这是通过故障转移和负载平衡提供高可用性的非常简单的方法。