Talk about how to use the integration of apollo and druid to realize the dynamic eagerness of data sources

foreword

The source of the material for this article is a technical exchange with a friend. At that time, my friend complained to me that Apollo is not as easy to use as nacos, and they also had an online accident because of Apollo.

The background of the story is roughly as follows

A while ago, the database of my friend's department was down, which caused the business to fail to operate normally. At that time, my friend's database information was configured on Apollo. My friend's idea was that when the database was down, you could switch the database information configured on Apollo to realize data recovery. Source heat change. But when their database went down, the friend operated according to his idea and found that things were not as he imagined. After they changed the data source, they found that the business service connection was still the old database service. They had no choice but to contact dba processing.

Later, after I listened to my friend’s description, I asked him how you did the database eagerly at that time. His answer was: it’s very simple, just configure the data source information on Apollo. If you want to change the data source, just Change it directly on the portal of apollo. After hearing what my friend said, I asked and then what? The friend's answer is: what then? There is no more.

Through that exchange, there is today's article. Today we will talk about the integration of apollo and druid to realize the data source dynamic eagerness

Realize the core idea

Apollo's configuration change dynamic monitoring + spring AbstractRoutingDataSource reserved method determineCurrentLookupKey for data source switching

Before introducing the implementation of the core logic, let's talk about the configuration center

What is Configuration Center?

The configuration center is a basic service component that manages various application configurations in a unified manner. His core is the unified management of configuration . The category it manages is configuration. As for objects that depend on configuration, such as data sources, it is not managed by the configuration center. Why am I bringing this up alone? It is because my friend seems to have fallen into a misunderstanding, thinking that if the configuration is changed on apollo, the data source that this configuration depends on will also be changed along with it

core code

1. Create a dynamic data source and proxy the original datasource

public class DynamicDataSource extends AbstractRoutingDataSource {
    
    

    public static final String DATASOURCE_KEY = "db";

    @Override
    protected Object determineCurrentLookupKey() {
    
    
        return DATASOURCE_KEY;
    }

    public DataSource getOriginalDetermineTargetDataSource(){
    
    
        return this.determineTargetDataSource();
    }
}

@Configuration
@EnableConfigurationProperties(BackupDataSourceProperties.class)
@ComponentScan(basePackages = "com.github.lybgeek.ds.switchover")
public class DynamicDataSourceAutoConfiguration {
    
    


    @Bean
    @ConditionalOnMissingBean
    @Primary
    @ConditionalOnClass(DruidDataSource.class)
    public AbstractDataSourceManger abstractDataSourceManger(DataSourceProperties dataSourceProperties, BackupDataSourceProperties backupDataSourceProperties){
    
    
        return new DruidDataSourceManger(backupDataSourceProperties,dataSourceProperties);
    }

    @Bean("dataSource")
    @Primary
    @ConditionalOnBean(AbstractDataSourceManger.class)
    public DynamicDataSource dynamicDataSource(AbstractDataSourceManger abstractDataSourceManger) {
    
    
        DynamicDataSource source = new DynamicDataSource();
        DataSource dataSource = abstractDataSourceManger.createDataSource(false);
        source.setTargetDataSources(Collections.singletonMap(DATASOURCE_KEY, dataSource));
        return source;
    }

}

One thing to note here is that the bean name of DynamicDataSource must be dataSource, the purpose is to make the bean fetched by the default datasource of spring be DynamicDataSource

2. Monitor configuration changes and switch data sources

switch data source

 @ApolloConfigChangeListener(interestedKeyPrefixes = PREFIX)
    public void onChange(ConfigChangeEvent changeEvent) {
    
    
        refresh(changeEvent.changedKeys());
    }

    /**
     *
     * @param changedKeys
     */
    private synchronized void refresh(Set<String> changedKeys) {
    
    
        /**
         * rebind configuration beans, e.g. DataSourceProperties
         * @see org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder#onApplicationEvent
         */
        this.applicationContext.publishEvent(new EnvironmentChangeEvent(changedKeys));

        /**
         * BackupDataSourceProperties rebind ,you can also do it in PropertiesRebinderEventListener
         * @see PropertiesRebinderEventListener
         */
        backupDataSourcePropertiesHolder.rebinder();

        abstractDataSourceManger.switchBackupDataSource();

    }
  @SneakyThrows
    @Override
    public void switchBackupDataSource() {
    
    
        if(backupDataSourceProperties.isForceswitch()){
    
    
            if(backupDataSourceProperties.isForceswitch()){
    
    
                log.info("Start to switch backup datasource : 【{}】",backupDataSourceProperties.getBackup().getUrl());
                DataSource dataSource = this.createDataSource(true);
                DynamicDataSource source = applicationContext.getBean(DynamicDataSource.class);
                DataSource originalDetermineTargetDataSource = source.getOriginalDetermineTargetDataSource();
                if(originalDetermineTargetDataSource instanceof DruidDataSource){
    
    
                    DruidDataSource druidDataSource = (DruidDataSource)originalDetermineTargetDataSource;
                    ScheduledExecutorService createScheduler = druidDataSource.getCreateScheduler();
                    createScheduler.shutdown();
                    if(!createScheduler.awaitTermination(backupDataSourceProperties.getAwaittermination(), TimeUnit.SECONDS)){
    
    
                        log.warn("Druid dataSource 【{}】 create connection thread force to closed",druidDataSource.getUrl());
                        createScheduler.shutdownNow();
                    }
                }
                //当检测到数据库地址改变时,重新设置数据源
                source.setTargetDataSources(Collections.singletonMap(DATASOURCE_KEY, dataSource));
                //调用该方法刷新resolvedDataSources,下次获取数据源时将获取到新设置的数据源
                source.afterPropertiesSet();

                log.info("Switch backup datasource : 【{}】 finished",backupDataSourceProperties.getBackup().getUrl());
            }
        }
    }

3. Test

   @Override
    public void run(ApplicationArguments args) throws Exception {
    
    
        while(true){
    
    
            User user = userService.getById(1L);
            System.err.println(user.getPassword());
            TimeUnit.SECONDS.sleep(1);
        }

    }

Before switching, the console prints


After switching, the console prints

Summarize

The above is the overall idea of ​​realizing the integration of apollo and druid to realize the dynamic eagerness of data sources, but there is still a problem in the implementation, that is, there are old connections that have not been processed. Although I didn't do any processing in the sample code, getOriginalDetermineTargetDataSource is reserved in the code, and some additional operations can be done through getOriginalDetermineTargetDataSource.

The implementation of this article can also be implemented using the case provided by apollo on github, the link is as follows

https://github.com/apolloconfig/apollo-use-cases/tree/master/dynamic-datasource

In his case, after connection switching, the old data source will be cleaned up. The data source in it is HikariDataSource. If you use the case provided by apollo, when you use the druid data source, I will post the closed part of the source code of druid


And get the connection source code

Here is a point to note that when the druid data source is closed, if a connection happens to come in at this time, a DataSourceDisableException will be reported at this time, and then the project will exit abnormally

Finally, I would like to add something extra. Before my friend said that Apollo is not as easy to use as nacos, I have reservations. In fact, to measure the quality of a technology, it is necessary to bring the scene. In some scenes, the advantages of the technology Might be a disadvantage

demo link

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-datasource-hot-switchover

Guess you like

Origin blog.csdn.net/kingwinstar/article/details/127345377