前一阵子,服务器出现了问题,导致数据库需要更换地址,业务服不能做到不停机更换,经过这次事件我便开始着手研究如何让服务不重启更换数据库。监听nacos的配置修改+druid 配置多数据源 可以实现。

一、前置准备:druid 配置多数据源

DruidConfig

@Configuration
public class DruidConfig
{
    @Bean
    @ConfigurationProperties("spring.datasource.druid.master")
    public DataSource masterDataSource(DruidProperties druidProperties)
    {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }

    @Bean
    @ConfigurationProperties("spring.datasource.druid.slave")
    @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
    public DataSource slaveDataSource(DruidProperties druidProperties)
    {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }

    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dataSource(DataSource masterDataSource)
    {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
        setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
        return new DynamicDataSource(masterDataSource, targetDataSources);
    }

    /**
     * 设置数据源
     * 
     * @param targetDataSources 备选数据源集合
     * @param sourceName 数据源名称
     * @param beanName bean名称
     */
    public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName)
    {
        try
        {
            DataSource dataSource = SpringUtils.getBean(beanName);
            targetDataSources.put(sourceName, dataSource);
        }
        catch (Exception e)
        {
        }
    }

    /**
     * 去除监控页面底部的广告
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Bean
    @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true")
    public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties)
    {
        // 获取web监控页面的参数
        DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
        // 提取common.js的配置路径
        String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
        String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
        final String filePath = "support/http/resources/js/common.js";
        // 创建filter进行过滤
        Filter filter = new Filter()
        {
            @Override
            public void init(javax.servlet.FilterConfig filterConfig) throws ServletException
            {
            }

            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                    throws IOException, ServletException
            {
                chain.doFilter(request, response);
                // 重置缓冲区,响应头不会被重置
                response.resetBuffer();
                // 获取common.js
                String text = Utils.readFromResource(filePath);
                // 正则替换banner, 除去底部的广告信息
                text = text.replaceAll("<a.*?banner\"></a><br/>", "");
                text = text.replaceAll("powered.*?shrek.wang</a>", "");
                response.getWriter().write(text);
            }

            @Override
            public void destroy()
            {
            }
        };
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(filter);
        registrationBean.addUrlPatterns(commonJsPattern);
        return registrationBean;
    }
}

如上所示,把数据源交给Druid管理,后续naocs监听类会注入你所要修改的数据源。

Restart

@Configuration
@RefreshScope
@Data
@Slf4j
public class DruidDataSourceRestart {

    @Value("${spring.application.name}")
    private String appName;

    @Value("${spring.profiles.active}")
    private String env;

    @Autowired
    private NacosConfigProperties nacosConfigProperties;

	// 这里注入你要修改的数据源,也可以注入多个,下面监听处理
    @Resource(name = "masterDataSource")
    private DruidDataSource dataSource;


	@PostConstruct
    public void init() {
        try {
            log.info("nacos监听启动,当前环境:" + appName + "-" + env + ".yml" + ",分组:" + nacosConfigProperties.getGroup());
            nacosConfigProperties.configServiceInstance().addListener(appName + "-" + env + ".yml", nacosConfigProperties.getGroup(), new Listener() {
                @Override
                public Executor getExecutor() {
                    return null;
                }

                @Override
                public void receiveConfigInfo(String s) {
                    Map master = null;
                    try {
                        Yaml yaml = new Yaml();
                        Map map = (Map) yaml.load(s);
                        Map spring = (Map) map.get("spring");
                        Map datasource = (Map) spring.get("datasource");
                        Map dynamic = (Map) datasource.get("dynamic");
                        Map dynamicDatasource = (Map) dynamic.get("datasource");
                        master = (Map) dynamicDatasource.get("master");
                    } catch (Exception e) {
                        log.error("监听解析nacos返回的配置信息出错", e.getMessage());
                        return;
                    }
                    System.out.println("最新的master配置信息:" + master);
                    Boolean newMaster = true;
                    //这里拿到最新的配置信息,通过对比旧的配置信息决定是否更新数据库信息
                    // xxxxxxxx
                    // xxxxxxxx newMaster = xx;
                    if (newMaster) {
                        try {
                            dataSource.restart();
                            dataSource.setUrl("新的url");
                            dataSource.setUsername("新的用户名");
                            dataSource.setPassword("新的密码");
                            dataSource.setDriverClassName("新的驱动名");
                        } catch (SQLException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            });
        } catch (NacosException e) {
            log.error("监听nacos配置错误");
            throw new RuntimeException(e);
        }
    }
}

通过上述代码便可服务不重启达成更改数据库信息的效果,上面只处理了主数据源,网友们也可以继续处理其他数据源。欢迎点赞收藏。

Logo

一站式 AI 云服务平台

更多推荐