Hable sobre cómo usar la integración de apollo y druid para darse cuenta del entusiasmo dinámico de las fuentes de datos

prefacio

La fuente del material de este artículo es un intercambio técnico con un amigo. En ese momento, mi amigo se quejó de que Apollo no es tan fácil de usar como nacos, y también tuvieron un accidente en línea debido a Apollo.

El trasfondo de la historia es más o menos el siguiente

Hace un tiempo, la base de datos del departamento de mi amigo no funcionaba, lo que provocó que el negocio no funcionara normalmente. En ese momento, la información de la base de datos de mi amigo estaba configurada en Apollo. La idea de mi amigo era que cuando la base de datos no funcionaba, podía cambiar la información de la base de datos configurada en Apollo para realizar la recuperación de datos Cambio de fuente de calor. Pero cuando su base de datos dejó de funcionar, el amigo operó de acuerdo con su idea y descubrió que las cosas no eran como él las imaginaba. Después de cambiar la fuente de datos, descubrieron que la conexión del servicio comercial seguía siendo el antiguo servicio de la base de datos. No tuvieron más remedio que para ponerse en contacto con el procesamiento de dba.

Más tarde, después de escuchar la descripción de mi amigo, le pregunté con entusiasmo cómo hiciste la base de datos en ese momento. Su respuesta fue: es muy simple, solo configure la información de la fuente de datos en Apollo. Si desea cambiar la fuente de datos, solo Cámbialo directamente en el portal de apolo. Después de escuchar lo que dijo mi amigo, pregunté y ¿entonces qué? La respuesta del amigo es: ¿entonces qué? No hay más.

A través de ese intercambio, está el artículo de hoy. Hoy hablaremos sobre la integración de apollo y druid para realizar el afán dinámico de la fuente de datos.

Darse cuenta de la idea central

Monitoreo dinámico de cambio de configuración de Apollo + primavera AbstractRoutingDataSource método reservado determineCurrentLookupKey para el cambio de fuente de datos

Antes de presentar la implementación de la lógica central, hablemos del centro de configuración

¿Qué es el Centro de configuración?

El centro de configuración es un componente de servicio básico que gestiona varias configuraciones de aplicaciones de forma unificada. Su núcleo es la gestión unificada de la configuración . La categoría que maneja es configuración, en cuanto a los objetos que dependen de la configuración, como fuentes de datos, no es manejado por el centro de configuración. ¿Por qué menciono esto solo? Es porque mi amigo parece haber caído en un malentendido, pensando que si se cambia la configuración en apollo, la fuente de datos de la que depende esta configuración también se cambiará junto con ella.

código central

1. Crear una fuente de datos dinámica y representar la fuente de datos original

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

}

Una cosa a tener en cuenta aquí es que el nombre del bean de DynamicDataSource debe ser dataSource, el propósito es hacer que el bean obtenido por la fuente de datos predeterminada de Spring sea DynamicDataSource

2. Supervise los cambios de configuración y cambie las fuentes de datos

cambiar la fuente de datos

 @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. prueba

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

    }

Antes de cambiar, la consola imprime


Después de cambiar, la consola imprime

Resumir

Lo anterior es la idea general de realizar la integración de apollo y druid para concretar el afán dinámico de las fuentes de datos, pero aún existe un problema en la implementación, es decir, existen conexiones antiguas que no han sido procesadas. Aunque no realicé ningún procesamiento en el código de muestra, getOriginalDetermineTargetDataSource está reservado en el código y se pueden realizar algunas operaciones adicionales a través de getOriginalDetermineTargetDataSource.

La implementación de este artículo también se puede implementar utilizando el caso proporcionado por apollo en github, el enlace es el siguiente

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

En su caso, después del cambio de conexión, se limpiará la fuente de datos anterior. La fuente de datos que contiene es HikariDataSource. Si usa el caso provisto por apollo, cuando use la fuente de datos de druida, publicaré la parte cerrada del código fuente de druida


Y obtén el código fuente de la conexión.

Aquí hay un punto para tener en cuenta que cuando la fuente de datos del druida está cerrada, si ocurre una conexión en este momento, se informará una DataSourceDisableException en este momento, y luego el proyecto se cerrará de manera anormal.

Por último, me gustaría agregar algo extra. Antes de que mi amigo dijera que Apollo no es tan fácil de usar como nacos, tengo mis reservas. De hecho, para medir la calidad de una tecnología, es necesario traer la escena. En algunos escenas, las ventajas de la tecnología pueden ser una desventaja

enlace de demostración

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

Supongo que te gusta

Origin blog.csdn.net/kingwinstar/article/details/127345377
Recomendado
Clasificación