마이크로 서비스 아키텍처 [SpringBoot + SpringCloud + VUE] || 다섯 공공 마이크로 채널 번호 - 동적 데이터 소스

1, 소스 코드 분석

많은 특정 응용 프로그램 시나리오, 우리는 동적 데이터 소스를 사용해야합니다. 예를 별도의 읽기 및 쓰기, 및 기타 멀티 테넌트 (multi-tenant) 시나리오하십시오. 봄 부팅 + MyBatis로 + MySQL을 기반으로이 튜토리얼의 경우는 달성했다. 봄은 AbstractRoutingDataSource 구축 된 추상 클래스, 그것은 서로 다른 키에 따라 데이터 소스지도, 반환 서로 다른 복수의 데이터 소스로 구성 할 수 있습니다. 응용 프로그램이 먼저 키를 설정할 수 있도록 AbstractRoutingDataSource 또한 데이터 소스 인터페이스는, 당신은 데이터베이스에 액세스하기 위해 실제 AbstractRoutingDataSource에서 해당 데이터 소스가 지정된 데이터베이스에 대한 액세스를 얻을 수 있기 때문에. 다음에는 프로젝트에서 다중 데이터 소스 스위칭 소스 분석 :

추상 클래스 AbstractRoutingDataSource 반원 다음과 같이;

private Map<Object, Object> targetDataSources;
private Object defaultTargetDataSource;
private Map<Object, DataSource> resolvedDataSources;
private DataSource resolvedDefaultDataSource;
  • targetDataSources는 키 매핑 및 데이터베이스 연결을 포함
  • defaultTargetDataSource는 기본 연결을 식별
  • resolvedDataSources 이 데이터 구조가 매핑 관계에서 구축, 데이터베이스 저장 구조는 데이터 소스와 targetDataSources에 의해 식별됩니다

이 클래스 determineTargetDataSource () 메소드는 getConnection () 메소드를 호출하여 데이터베이스에 연결하는 연결을 만들 수 있습니다.

public Connection getConnection() throws SQLException {
    return this.determineTargetDataSource().getConnection();
}
public Connection getConnection(String username, String password) throws SQLException {
    return this.determineTargetDataSource().getConnection(username, password);
}

우리는 데이터 소스에 연결된 컨테이너를 봄하기로 결정 determineTargetDataSource () 메소드를 찾습니다. , determineCurrentLookupKey () 메소드는 추상적 인 방법입니다, 우리는이 메소드를 오버라이드 (override)하는 상속 AbstractRoutingDataSource 추상 클래스에 필요합니다. 이 방법은 키를 반환하고, lookupKey에 할당. 값은 데이터 소스의 스위칭 기능을 달성하도록, 상기 키는 키 resolvedDataSources 소스 속성에 대응하는 값에 의해 획득 될 수있다 됨으로써 beanName에 빈에 핵심이다.

protected DataSource determineTargetDataSource() {
    Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
    Object lookupKey = this.determineCurrentLookupKey();
    DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
    if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
        dataSource = this.resolvedDefaultDataSource;
    }
    if (dataSource == null) {
        throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
    } else {
        return dataSource;
    }
}

우리는 AbstractRoutingDataSource InitializingBean 표시 인터페이스와 구현 afterPropertiesSet 방법을 구현하는이 클래스를 찾기 위해 지속적으로 노력하고 있습니다. 빈은 특정 빈에 대해 수행 될 때 afterPropertiesSet 초기화 방법이 실행된다. 데이터 소스 콩이 동적으로 생성되기 때문에, 모든 필요 targetDataSources에 추가 한 다음 빈 업데이트를 알리기 위해 스프링에 대한 () afterPropertiesSet를 호출합니다. 따라서,이 resolvedDataSources targetDataSources의 속성에 대한 키 정보 기억 특성에있어서, 그래서 후속 호출한다.

public void afterPropertiesSet() {
    if (this.targetDataSources == null) {
        throw new IllegalArgumentException("Property 'targetDataSources' is required");
    } else {
        this.resolvedDataSources = new HashMap(this.targetDataSources.size());
        this.targetDataSources.forEach((key, value) -> {
            Object lookupKey = this.resolveSpecifiedLookupKey(key);
            DataSource dataSource = this.resolveSpecifiedDataSource(value);
            this.resolvedDataSources.put(lookupKey, dataSource);
        });
        if (this.defaultTargetDataSource != null) {
            this.resolvedDefaultDataSource = this.resolveSpecifiedDataSource(this.defaultTargetDataSource);
        }
    }
}

2, 프로젝트 구현

원리를 분석 한 후 우리가 먼저 구성 파일을 수정 단일 데이터 소스와 필드의 나머지 부분과 일치하는 사용자 정의 할 수 있습니다 실제 상황, 마스터와 슬레이브에 따라 구성된 두 개의 데이터 소스를 추가, 동적 데이터 소스 스위칭은 매우 간단 실현 것이 분명하다.

spring:
  datasource:
    master:
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: 123465
      jdbc-url: jdbc:mysql://localhost:3306/theme_weixin?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowMultiQueries=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
    slave:
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: 123465
      jdbc-url: jdbc:mysql://192.168.101.18:3306/theme_weixin?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowMultiQueries=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai

클래스가 시작 추가 (가) DataSourceAutoConfiguration.class = {} 제외 , 비활성화 기본 자동 구성 데이터 소스를. 기본 데이터 소스 구성이 자동으로 spring.datasource를 읽습니다. * 당신이 정의를 위해 해제 할 수 있도록 속성이 데이터 소스를 만듭니다.

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class WeixinApplication {
    public static void main(String[] args) {
        SpringApplication.run(WeixinApplication.class, args);
    }
}

데이터 소스 타입 구성, 데이터 소스 주입 구성 속성 생성 마스터, 슬레이브 데이터 소스를 작성

@Configuration
public class DataSourceConfig {
    @Primary
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.master")
    @RefreshScope
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    @RefreshScope
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }
}

속됨 MybatisAutoConfiguration는 데이터 소스 SqlSessionFactory는 상기 복수의 주입된다

@Configuration
public class MyBatisConfig extends MybatisAutoConfiguration {
    @Resource(name = "masterDataSource")
    private DataSource masterDataSource;
    @Resource(name = "slaveDataSource")
    private DataSource slaveDataSource;
    public MyBatisConfig(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider, ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider, ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider, ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
        super(properties, interceptorsProvider, typeHandlersProvider, languageDriversProvider, resourceLoader, databaseIdProvider, configurationCustomizersProvider);
    }
    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        // 重载父类 sqlSessionFactory init
        return super.sqlSessionFactory(roundRobinDataSourceProxy());
    }
    private AbstractRoutingDataSource roundRobinDataSourceProxy() {
        DynamicDataSource proxy = new DynamicDataSource();
        Map<Object, Object> targetDataResources = new HashMap<>(2);
        targetDataResources.put(DbContextHolder.DbType.MASTER,masterDataSource);
        targetDataResources.put(DbContextHolder.DbType.SLAVE,slaveDataSource);
        proxy.setDefaultTargetDataSource(masterDataSource);
        proxy.setTargetDataSources(targetDataResources);
        proxy.afterPropertiesSet();
        return proxy;
    }
}

AbstractRoutingDataSource 상속, DynamicDataSource 클래스를 작성, 재 작성 방법 determineCurrentLookupKey

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DbContextHolder.getDbType().key();
    }
}

DbContextHolder은 다음과 같습니다 :

public class DbContextHolder {
    public enum DbType{
        Master("masterDataSource"),
        Slave("slaveDataSource");
        String beanName;
        DbType(String beanName) {
            this.beanName = beanName;
        }
        public String key() {
            return this.beanName;
        }
    }
    private static final ThreadLocal<DbType> CONTEXT_HOLDER = new ThreadLocal<>();
    public static void setDbType(DbType dbType){
        if(dbType==null){
            throw new NullPointerException();
        }else{
            CONTEXT_HOLDER.set(dbType);
        }
    }
    public static DbType getDbType(){
        return CONTEXT_HOLDER.get()==null? DbType.MASTER:CONTEXT_HOLDER.get();
    }
    public static void clearDbType() {
        CONTEXT_HOLDER.remove();
    }
}

데이터 소스에있어서, 통화 전환, 또는 특수 모드 전환을 기초로 할 수있다. 예시적인 데이터 소스 스위칭 :

public String test() {
    DbContextHolder.setDbType(DbContextHolder.DbType.SLAVE);
    TabCategory tabCategory=new TabCategory();
    tabCategory.setCategoryId(10L);
    tabCategoryMapper.insertSelective(tabCategory);
    DbContextHolder.clearDbType();
    return "success";
}

추천

출처www.cnblogs.com/kevin-ying/p/12392721.html