【问题标题】:Primary/secondary datasource failover in Spring MVCSpring MVC 中的主/从数据源故障转移
【发布时间】:2021-05-01 04:59:30
【问题描述】:
我有一个使用 mybatis 在 Spring 框架上开发的 java web 应用程序。我看到数据源是在 beans.xml 中定义的。现在我也想添加一个辅助数据源作为备份。例如,如果应用程序无法连接到数据库并出现错误,或者服务器关闭,那么它应该能够连接到不同的数据源。 Spring 中是否有配置来执行此操作,或者我们必须在应用程序中手动编写代码?
我在 Spring boot 中看到了主要和次要符号,但在 Spring 中什么也没看到。如果与主数据源的连接失败/超时,我可以在创建/检索连接的代码中通过连接到辅助数据源来实现这些。但想知道这是否可以通过仅在 Spring 配置中进行更改来实现。
【问题讨论】:
标签:
spring
database
spring-mvc
mybatis
failover
【解决方案1】:
让我一一澄清-
现在,当一个数据源出现故障时,我们如何实际切换数据源-
- 大多数人不在代码中管理这种高可用性。人们通常更喜欢保持同步的主动-被动模式下的 2 个主数据库实例。对于自动故障转移,可以使用
keepalived 之类的东西。这也是一个高度主观和有争议的话题,这里有很多事情需要考虑,比如我们能否承受复制延迟,每个主服务器是否有从服务器运行(因为那时我们也必须切换从服务器,因为旧主服务器的从服务器现在会出局同步等)如果您的数据库分布在各个地区,这将变得更加困难(读起来很棒),并且需要更多的工程、规划和设计。
- 现在,问题特别提到为此使用应用程序代码。你可以做一件事。 我不建议在生产中使用它。永远。您可以使用自己的自定义注释围绕所有主要事务方法创建 ASPECTJ 建议。让我们将此注释称为
@SmartTransactional 用于我们的演示。
示例代码。不过没测试-
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SmartTransactional {}
public class SomeServiceImpl implements SomeService {
@SmartTransactional
@Transactional("primaryTransactionManager")
public boolean someMethod(){
//call a common method here for code reusability or create an abstract class
}
}
public class SomeServiceSecondaryTransactionImpl implements SomeService {
@Transactional("secondaryTransactionManager")
public boolean usingTransactionManager2() {
//call a common method here for code reusability or create an abstract class
}
}
@Component
@Aspect
public class SmartTransactionalAspect {
@Autowired
private ApplicationContext context;
@Pointcut("@annotation(...SmartTransactional)")
public void smartTransactionalAnnotationPointcut() {
}
@Around("smartTransactionalAnnotationPointcut()")
public Object methodsAnnotatedWithSmartTransactional(final ProceedingJoinPoint joinPoint) throws Throwable {
Method method = getMethodFromTarget(joinPoint);
Object result = joinPoint.proceed();
boolean failure = Boolean.TRUE;// check if result is failure
if(failure) {
String secondaryTransactionManagebeanName = ""; // get class name from joinPoint and append 'SecondaryTransactionImpl' instead of 'Impl' in the class name
Object bean = context.getBean(secondaryTransactionManagebeanName);
result = bean.getClass().getMethod(method.getName()).invoke(bean);
}
return result;
}
}