apollo と druid の統合を使用してデータ ソースの動的な熱心さを実現する方法について説明します。

序文

この記事のネタ元は友人との技術交流で、当時友人から「Apolloはnacosほど使いにくいし、Apolloのせいでオンライン事故も起きた」とぼやかれていました。

物語の背景は大まかに次のようなものです

少し前に、私の友人の部門のデータベースがダウンし、業務が正常に運営できなくなりました。そのとき、私の友人のデータベース情報は Apollo 上に設定されていました。私の友人のアイデアは、データベースがダウンしたときに切り替えられるというものでした。データ復旧を実現するためにApollo上に設定されたデータベース情報 ソース熱変化 しかし、データベースがダウンしたとき、その友人は自分の考えに従って運用していましたが、状況が想像どおりではないことがわかりました。データ ソースを変更した後、ビジネス サービス接続が依然として古いデータベース サービスであることがわかりました。彼らには選択の余地がありませんでした。 dba処理に連絡してください。

後日、友人の説明を聞いた後、当時熱心にデータベースをどうやって作っていたのかと尋ねたところ、「とても簡単です。Apollo 上でデータソースの情報を設定するだけです。データソースを変更したい場合は、変更するだけです」との答えでした。 apollo のポータルで直接変更します。友人の話を聞いた後、私は尋ねました、それでは?友人の答えはこうだ。「それでは?」もうない。

そのやりとりを経て今日の記事ができました。今日はデータソースの動的な熱心さを実現するための apollo と druid の統合について話します。

核となるアイデアを実現する

Apollo の構成変更の動的監視 + Spring AbstractRoutingDataSource データソース切り替え用の予約メソッド決定CurrentLookupKey

コアロジックの実装を紹介する前に、構成センターについて話しましょう

構成センターとは何ですか?

構成センターは、アプリケーションのさまざまな構成を一元管理する基本的なサービス コンポーネントです。彼の核心は、構成の統合管理です。管理するカテゴリは構成であり、データ ソースなどの構成に依存するオブジェクトについては、構成センターでは管理されません。なぜ私が一人でこれを取り上げるのですか?というのも、友人はapollo上で設定を変更すると、その設定が依存しているデータソースも一緒に変更されるのではないかと勘違いしていたようです。

コアコード

1. 動的データソースを作成し、元のデータソースをプロキシします。

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;
    }

}

ここで注意すべき点の 1 つは、DynamicDataSource の Bean 名は dataSource でなければならないということです。その目的は、Spring のデフォルトのデータソースによって取得される Bean を DynamicDataSource にすることです。

2. 構成の変更を監視し、データ ソースを切り替える

データソースを切り替える

 @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. テスト

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

    }

切り替える前にコンソールに表示される


切り替え後、コンソールに表示される

要約する

上記は、apollo と druid の統合を実現してデータ ソースの動的な熱心さを実現するという全体的なアイデアですが、実装にはまだ問題があります。つまり、処理されていない古い接続が存在します。サンプル コードでは何も処理していませんが、コード内では getOriginalDetermineTargetDataSource が予約されており、getOriginalDetermineTargetDataSource を通じていくつかの追加操作を行うことができます。

この記事の実装は、github の apollo が提供するケースを使用して実装することもできます。リンクは次のとおりです。

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

彼の場合、接続の切り替え後、古いデータ ソースがクリーンアップされます。その中のデータソースはHikariDataSourceです apollo提供のケースを使用する場合、druidデータソースを使用する場合はdruidのソースコードのクローズ部分を掲載します


そして、接続ソースコードを取得します。

注意点としては、druid データソースが閉じられているときに、この時点で接続が入ってくると、この時点で DataSourceDisableException が報告され、プロジェクトが異常終了します。

最後に、少しだけ付け加えさせていただきます。以前、友人が「Apollo は nacos ほど使いにくい」と言っていたのですが、私には抵抗がありました。実際、技術の品質を測るには、現場を持ってくる必要があります。シーン、テクノロジーの利点は欠点になる可能性があります

デモリンク

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

おすすめ

転載: blog.csdn.net/kingwinstar/article/details/127345377