1、情景描述项目要实现MySQL多数据源的主从自动切换(主库宕机自动切换从库、从库宕机切换主库)
2、项目在测试的时候发现的问题
项目无法成功切换数据源
1、正确的访问从库 去掉@HystrixCommand()
2、访问错误的从库 没去掉@HystrixCommand()
多数据源配置与Hystrix命令的结合出现了问题,导致数据源切换逻辑未能正确生效。
3、解决方案 在Hystrix的线程池中也能正确切换数据源,需要实现自定义的HystrixConcurrencyStrategy,并在其中覆盖wrapCallable方法来注入数据源的切换逻辑。
1、添加DelegatingContextCallable类 行前后自动管理动态数据源的选择
import java.util.concurrent.Callable;
/**
* @title DelegatingTenantContextCallable
* @projectName gstc2020
* @packageName com.chinalife.gstc.commons.config
* @createDate 2024/09/25 13:22
* @description
*/
public final class DelegatingContextCallable<V> implements Callable<V> {
private final Callable<V> delegate;
private final String source;
public DelegatingContextCallable(Callable<V> delegate, String source) {
this.delegate = delegate;
this.source = source;
}
@Override
public V call() throws Exception {
try {
// 获取数据源
DynamicDataSourceHolder.setDynamicDataSourceKey(source);
return delegate.call();
} finally {
// 移除数据源
DynamicDataSourceHolder.removeDynamicDataSourceKey();
}
}
}
2、添加 ThreadLocalAwareStrategy类 主要用于在 Hystrix 并发策略中集成 ThreadLocal 相关的逻辑,特别是与动态数据源管理相关的逻辑
import com.netflix.hystrix.HystrixThreadPoolKey;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariable;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
import com.netflix.hystrix.strategy.properties.HystrixProperty;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Component;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Component
public class ThreadLocalAwareStrategy extends HystrixConcurrencyStrategy {
private static final Log log = LogFactory.getLog(ThreadLocalAwareStrategy.class);
private HystrixConcurrencyStrategy delegate;
public ThreadLocalAwareStrategy() {
try {
this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
if (this.delegate instanceof ThreadLocalAwareStrategy) {
// Welcome to singleton hell...
return;
}
HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins
.getInstance().getCommandExecutionHook();
HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance()
.getEventNotifier();
HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance()
.getMetricsPublisher();
HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance()
.getPropertiesStrategy();
this.logCurrentStateOfHystrixPlugins(eventNotifier, metricsPublisher,
propertiesStrategy);
HystrixPlugins.reset();
HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
HystrixPlugins.getInstance()
.registerCommandExecutionHook(commandExecutionHook);
HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
} catch (Exception e) {
log.error("Failed to register Sleuth Hystrix Concurrency Strategy", e);
}
}
private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier,
HystrixMetricsPublisher metricsPublisher,
HystrixPropertiesStrategy propertiesStrategy) {
if (log.isDebugEnabled()) {
log.debug("Current Hystrix plugins configuration is ["
+ "concurrencyStrategy [" + this.delegate + "]," + "eventNotifier ["
+ eventNotifier + "]," + "metricPublisher [" + metricsPublisher + "],"
+ "propertiesStrategy [" + propertiesStrategy + "]," + "]");
log.debug("Registering Sleuth Hystrix Concurrency Strategy.");
}
}
@Override
public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
return this.delegate.getBlockingQueue(maxQueueSize);
}
@Override
public <T> HystrixRequestVariable<T> getRequestVariable(
HystrixRequestVariableLifecycle<T> rv) {
return this.delegate.getRequestVariable(rv);
}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixProperty<Integer> corePoolSize,
HystrixProperty<Integer> maximumPoolSize,
HystrixProperty<Integer> keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
return this.delegate.getThreadPool(threadPoolKey, corePoolSize,
maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
return this.delegate.wrapCallable(new DelegatingContextCallable<>(callable, DynamicDataSourceHolder.getDynamicDataSourceKey()));
}
}
这样就能实现Hystrix注解与多数据源产生线程冲突了