SpringBoot中DataSource配置@ConfigurationProperties注解的坑

背景

今年負(fù)責(zé)一個特別緊急的項(xiàng)目,從需求溝通到應(yīng)用上線只有一個月的時間,四月中旬開始具體溝通項(xiàng)目需求并準(zhǔn)備資源,五月中旬要完成第一次上線。這次不聊整個項(xiàng)目的開展過程,而是說一下通過一個月艱苦卓絕的奮斗,順利完成了需求溝通、分析、設(shè)計(jì)、開發(fā)、測試、驗(yàn)收,上線前也準(zhǔn)備了詳盡的上線序列并一一清點(diǎn)了,但上線時應(yīng)用啟動時出現(xiàn)的一個異常的排查過程。

現(xiàn)象

測試環(huán)境驗(yàn)證通過后,將鏡像推送給運(yùn)維部署到生產(chǎn)環(huán)境,應(yīng)用啟動時報如下錯誤

[ERROR] [DbSessionContextPlugin.java:] 測試數(shù)據(jù)庫連接失敗(Test DB Connection Fail)[driver:oracle.jdbc.driver.OracleDriver,url:store:xxxx.store:xxxx,user:uuuuuu]
java.sql.SQLException: No suitable driver found for store:xxxx.store:xxxx
        at java.sql.DriverManager.getConnection(DriverManager.java:689)
        at java.sql.DriverManager.getConnection(DriverManager.java:247)
        at com.sinolife.sf.framework.dbcontext.DbSessionContextPlugin.testConnectionValidate(DbSessionContextPlugin.java:427)
        at com.sinolife.sf.framework.dbcontext.DbSessionContextPlugin.postProcessAfterInitialization(DbSessionContextPlugin.java:61)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:437)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1710)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:579)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:501)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
        at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$146/1684106402.getObject(Unknown Source)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:251)
......

差異

測試環(huán)境與生產(chǎn)環(huán)境之間的差異主要有兩個方面

  • 數(shù)據(jù)庫連接串的配置方式
測試環(huán)境的配置方式
jdbc.user=system
jdbc.password=oracle
jdbc.jdbcUrl=jdbc:oracle:thin:@ip:port:uid
生產(chǎn)環(huán)境的配置方式
jdbc.user=tmsopr
jdbc.jdbcUrl=store:xxxx.store:xxxx

生產(chǎn)的配置方式是將連接串及秘鑰信息加密保存到指定路徑下的xxxx.store文件,需要這些信息時再去store文件中讀取,目的主要是指定路徑權(quán)限和加密方式可以保證敏感信息的安全,防范攻擊。

  • 數(shù)據(jù)庫版本差異
    測試環(huán)境數(shù)據(jù)庫是11G,而生產(chǎn)環(huán)境數(shù)據(jù)庫是19c。公司其他應(yīng)用使用的11G,11G官方支持周期到2018年就結(jié)束了,于是公司開始嘗試使用oracle 19c的版本,而我們的應(yīng)用有幸成為了第一個嘗試者。

分析 - 數(shù)據(jù)庫版本差異

根據(jù)異常日志信息java.sql.SQLException: No suitable driver found for第一反應(yīng)是oracle升級了,但是ojdbc驅(qū)動沒有升級,有可能是驅(qū)動版本老舊導(dǎo)致的問題。解決方法有兩個:升級OJDBC驅(qū)動版本;回退oracle數(shù)據(jù)庫版本。

  • 升級OJDBC驅(qū)動版本
    由于項(xiàng)目中間使用了兩個個數(shù)據(jù)源,應(yīng)用的數(shù)據(jù)庫是19c的版本,而另一個數(shù)據(jù)源數(shù)據(jù)庫的版本是11c,如果升級OJDBC驅(qū)動的版本,有可能會影響另一個數(shù)據(jù)源。另外,MAVEN中對OJDBC的依賴較多,調(diào)整影響面大,而且需要架構(gòu)組支持,有可能會出現(xiàn)反復(fù)調(diào)整并測試驗(yàn)證的情況。為了盡快解決問題讓應(yīng)用部署上線,放棄升級OJDBC驅(qū)動的方案。


    ojdbc的依賴關(guān)系
  • 回退oracle數(shù)據(jù)庫版本
    選定了方案之后,第一時間與運(yùn)維溝通好后,運(yùn)維開始協(xié)助回退oracle數(shù)據(jù)庫的版本。運(yùn)維一陣犀利的操作過后(導(dǎo)出、刪除19c、創(chuàng)建11g、導(dǎo)入),數(shù)據(jù)庫回退成11c的版本。
    滿懷希望的通知部署人員重新部署應(yīng)用,然而令人絕望的是,依然是原來的錯誤,一模一樣。
    說明: 此處優(yōu)先選擇這個方案另一個重要的原因是測試環(huán)境oracle是11g,應(yīng)用可以正常啟動,至于為什么測試和生產(chǎn)不一致,后面會提及。

分析 - 數(shù)據(jù)庫連接串的配置方式

數(shù)據(jù)庫回退沒解決掉問題,只能將矛頭指向數(shù)據(jù)庫連接串的配置方式上了。將生成環(huán)境的連接串配置方式調(diào)整為和測試環(huán)境一致,jdbc.jdbcUrljdbc.password直接在配置文件中寫死。調(diào)整后重新打包鏡像并推送運(yùn)維部署。
應(yīng)用居然成功啟動了!
開始懷疑是不是store文件的格式和解析是否有問題?但是這個是通用的模塊,已經(jīng)使用很多年了。為了消除疑惑,改回從store文件讀取jdbc.jdbcUrljdbc.password配置,增加日志,重新部署啟動還是和原來一樣報錯。檢查輸出日志,參數(shù)都正常解析并獲取到了。那究竟為什么在properties文件中配置就可以,而在store文件中配置就報錯了呢?
數(shù)據(jù)源的配置很常規(guī),很簡單,如下:

    @Bean(name = "dataSource")
    @Primary
    @ConfigurationProperties(prefix = "jdbc")
    public DataSource dataSource() throws PropertyVetoException {
        ComboPooledDataSource dataSource = DataSourceBuilder.create().type(ComboPooledDataSource.class).build();

        String jdbcUrl                 = context.getEnvironment().getProperty("jdbc.jdbcUrl");
        String user                    = context.getEnvironment().getProperty("jdbc.user");
        String driverClass             = context.getEnvironment().getProperty("jdbc.driverClass");
        ......

        // 從store文件中解析并讀取jdbcUrl和password
        String[] jdbcUrlAndPassword = DbParamterResolverUtil.resolveJdbcUrlAndPassword(jdbcUrl, user, null);
        dataSource.setDriverClass(driverClass);
        dataSource.setJdbcUrl(jdbcUrlAndPassword[0]);
        dataSource.setUser(user);
        dataSource.setPassword(jdbcUrlAndPassword[1]);
        ......

        return dataSource;
    }

通過再次仔細(xì)分析異常日志,將焦點(diǎn)放在了@ConfigurationProperties(prefix = "jdbc")這個注解上,

......// 原理待完善

@ConfigurationProperties(prefix = "jdbc")注解去掉,重新部署,一切正常。

思考

  • 知其然,知其所以然
    作為一個程序員不求甚解是要不得的,應(yīng)該在熟練使用工具的同時,多了解其背景和原理,這樣才不會濫用,遇到問題才能快速準(zhǔn)確的定位。

As a programmer, I hate to use things I don’t understand.

  • 溝通
    應(yīng)該積極主動的溝通,不應(yīng)該回避溝通,避免出現(xiàn)信息孤島。
    其實(shí)運(yùn)維的同事在開發(fā)環(huán)境部署了一套19c,但是并沒有通知我們。由于項(xiàng)目周期短,時間太緊張了,開發(fā)人員直接在測試環(huán)境開發(fā),已期望提高部署和用戶驗(yàn)證的效率。從而導(dǎo)致了很多不必要的操作。
  • 規(guī)范
    我們有開發(fā)、測試、生產(chǎn)三個環(huán)境,考慮到項(xiàng)目時間緊張,期望最大化提高效率,只維護(hù)了測試環(huán)境,直接忽略了開發(fā)環(huán)境。這樣不規(guī)范的操作導(dǎo)致了一系列的問題,環(huán)境問題,配置問題,開發(fā)頻繁部署,導(dǎo)致用戶測試體驗(yàn)極差等等。
    開發(fā)、測試、生產(chǎn)環(huán)境應(yīng)該保持一致,都應(yīng)該部署19c的數(shù)據(jù)庫,但是不知道什么原因運(yùn)維再開發(fā)部署了19c、測試部署了11g、生產(chǎn)部署了19c。環(huán)境不一致增加了問題分析的很多不必要的復(fù)雜度。
    引入更新或一項(xiàng)新技術(shù)的時候,應(yīng)該先做好充足的知識儲備和測試環(huán)境驗(yàn)證(最好由架構(gòu)組先嘗試、總結(jié)、然后培訓(xùn)推廣),而不是盲目的嘗試,評估的時候應(yīng)該更謹(jǐn)慎。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容