最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot自動裝配之多數(shù)據(jù)源SDK解決Dubbo性能瓶頸詳解

 更新時間:2025年09月25日 08:30:30   作者:努力的小鄭  
這篇文章主要為大家詳細介紹了SpringBoot自動裝配中如何通過多數(shù)據(jù)源SDK解決Dubbo性能瓶頸,文中的示例代碼講解詳細,有需要的小伙伴可以了解下

明明學(xué)了自動裝配,卻鮮有機會實戰(zhàn)?當(dāng)我面對Dubbo性能瓶頸時,一個自定義Starter的構(gòu)想讓我開啟了Spring Boot條件化裝配的奇妙之旅。

引言:那些年我們學(xué)過的自動裝配

記得畢業(yè)那會剛開始學(xué)習(xí)Spring Boot的時候,自動裝配機制讓我眼前一亮——"約定大于配置"的理念真是太巧妙了!相信很多小伙伴都和我一樣,懷著好奇心去研究@EnableAutoConfigurationspring.factories的奧秘,甚至動手嘗試編寫過自己的Starter。

但說實話,在實際項目開發(fā)中,真正需要自己實現(xiàn)自動裝配的場景并不多。大多數(shù)時候,我們都是在使用Spring Boot官方或者第三方提供的Starter。直到最近,我遇到了一個實實在在的需求,才讓我有機會深入實踐這個機制。

背景:Dubbo調(diào)用成了性能瓶頸

我在公司參與的這個大型項目采用了典型的微服務(wù)架構(gòu),各個服務(wù)之間通過Dubbo進行調(diào)用。項目規(guī)模較大,因此分成多個開發(fā)小組,每個小組負(fù)責(zé)不同的微服務(wù)模塊。

隨著業(yè)務(wù)量增長,我們發(fā)現(xiàn)了一個棘手的問題:某些高頻的數(shù)據(jù)查詢操作通過Dubbo調(diào)用時,性能開銷變得不可忽視。雖然單次調(diào)用的延遲不大,但在高并發(fā)場景下,這些開銷累積起來就相當(dāng)可觀了。同時提供duboo的服務(wù),因為高頻調(diào)用已經(jīng)存在并發(fā)瓶頸,頻繁告警,如果繼續(xù)增加調(diào)用量隨時可能崩潰。(因為數(shù)據(jù)庫規(guī)格較高,瓶頸不在于數(shù)據(jù)庫,而只在于dubbo服務(wù)提供方,且因為各種原因無法進行橫向擴容機器)

經(jīng)過我們小組討論,決定開發(fā)一個多數(shù)據(jù)源SDK,由我負(fù)責(zé)實現(xiàn)。讓各個小組能夠通過SDK直連需要的數(shù)據(jù)庫,減少不必要的Dubbo調(diào)用。這個SDK不僅要給其他小組使用,我們自己也打算針對一些高頻調(diào)用duboo接口替換為本地調(diào)用。

設(shè)計思路:條件化自動裝配的多數(shù)據(jù)源SDK

我的設(shè)計目標(biāo)是開發(fā)一個"智能"的SDK,能夠根據(jù)配置自動裝配所需的數(shù)據(jù)源、Dao和Service。業(yè)務(wù)方只需要引入依賴和添加配置,就可以直接使用相關(guān)的服務(wù)。

由于SDK中有些還需要包含一些業(yè)務(wù)邏輯,我們不能只提供DAO層,還需要提供Service層。為了避免與業(yè)務(wù)項目中可能已經(jīng)存在的Bean出現(xiàn)名稱沖突,所有Bean都加上了"Sdk"前綴。

SDK項目結(jié)構(gòu)設(shè)計

先來看看整個SDK的項目結(jié)構(gòu):

sdk-multi-datasource/
├── src/main/java/com/example/sdk/
│   ├── config/
│   │   ├── condition/
│   │   │   └── AnySdkDataSourceCondition.java
│   │   ├── datasource/
│   │   │   ├── SdkPrimaryDataConfig.java
│   │   │   └── SdkSecondaryDataConfig.java
│   │   └── SdkAutoConfiguration.java
│   ├── dao/
│   │   ├── primary/
│   │   │   └── SdkAppInfoDao.java
│   │   └── secondary/
│   │       └── SdkOtherDataDao.java
│   ├── service/
│   │   ├── SdkAppInfoService.java
│   │   └── SdkOtherDataService.java
│   ├── entity/
│   └── util/
├── src/main/resources/
│   ├── META-INF/
│   │   └── spring.factories
│   └── mapper/
│       ├── primary/
│       └── secondary/
└── pom.xml

核心代碼實現(xiàn)

1. 條件判斷類:智能感知數(shù)據(jù)源配置

首先,我創(chuàng)建了一個條件類,用于判斷是否需要啟用自動配置:

public class AnySdkDataSourceCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment env = context.getEnvironment();
        // 檢查是否配置了任意一個SDK數(shù)據(jù)源
        // 條件注解的優(yōu)勢:只有業(yè)務(wù)方真正配置了數(shù)據(jù)源,SDK才會生效,避免不必要的Bean加載
        return env.containsProperty("spring.datasource.sdk-primary.jdbc-url") ||
               env.containsProperty("spring.datasource.sdk-secondary.jdbc-url");
    }
}

條件注解的優(yōu)勢在于它允許我們根據(jù)環(huán)境動態(tài)決定是否啟用某些配置,這樣可以避免加載不必要的Bean,提高應(yīng)用啟動速度,并且避免與業(yè)務(wù)項目中可能存在的Bean沖突。

2. 數(shù)據(jù)源配置:完整的SDK主數(shù)據(jù)源配置

下面是完整的主數(shù)據(jù)源配置代碼,我添加了詳細的注釋說明:

@Configuration
// 條件注解:只有配置了sdk-primary數(shù)據(jù)源時才啟用此配置
@ConditionalOnProperty(prefix = "spring.datasource.sdk-primary", name = "jdbc-url")
// 指定Mapper接口的掃描路徑,并指定SqlSessionFactory的Bean名稱
@MapperScan(
    basePackages = "com.example.sdk.dao.primary", 
    sqlSessionFactoryRef = "sdkPrimarySqlSessionFactory"
)
public class SdkPrimaryDataConfig {

    // 主數(shù)據(jù)源Bean,使用@ConfigurationProperties讀取配置
    @Bean(name = "sdkPrimaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.sdk-primary")
    public DataSource sdkPrimaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    // 主數(shù)據(jù)源SqlSessionFactory
    @Bean(name = "sdkPrimarySqlSessionFactory")
    public SqlSessionFactory sdkPrimarySqlSessionFactory(
            @Qualifier("sdkPrimaryDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        // 設(shè)置Mapper XML文件的位置
        bean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:mapper/primary/*.xml"));
        return bean.getObject();
    }

    // 主數(shù)據(jù)源SqlSessionTemplate
    @Bean(name = "sdkPrimarySqlSessionTemplate")
    public SqlSessionTemplate sdkPrimarySqlSessionTemplate(
            @Qualifier("sdkPrimarySqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    // 主數(shù)據(jù)源事務(wù)管理器
    @Bean(name = "sdkPrimaryTransactionManager")
    public DataSourceTransactionManager sdkPrimaryTransactionManager(
            @Qualifier("sdkPrimaryDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

次數(shù)據(jù)源配置SdkSecondaryDataConfig的結(jié)構(gòu)與主數(shù)據(jù)源配置基本相同,區(qū)別在于:

  • Bean名稱中的"primary"替換為"secondary"
  • 掃描的包路徑不同(com.example.sdk.dao.secondary
  • 配置前綴不同(spring.datasource.sdk-secondary

3. DAO層接口

為了避免與業(yè)務(wù)項目中的Bean沖突,所有DAO接口都加上了"Sdk"前綴:

@Mapper
public interface SdkAppInfoDao {
    AppInfo getByBusinessId(String businessId);
}

4. Service層實現(xiàn)

Service類也遵循相同的命名規(guī)則,為了保持SDK的簡單性和靈活性,我選擇了傳統(tǒng)的setter注入方式:

public class SdkAppInfoService {
    private SdkAppInfoDao sdkAppInfoDao;

    public void setSdkAppInfoDao(SdkAppInfoDao sdkAppInfoDao) {
        this.sdkAppInfoDao = sdkAppInfoDao;
    }

    public AppInfo getByBusinessId(String businessId) {
        // 這里可以添加具體業(yè)務(wù)邏輯,如本地緩存、日志等
        return sdkAppInfoDao.getByBusinessId(businessId);
    }
}

5. 自動配置類:解決依賴注入問題

這是整個SDK的核心,我通過條件判斷確保只有配置了對應(yīng)數(shù)據(jù)源的情況下才創(chuàng)建相應(yīng)的Service Bean:

@Configuration
@Conditional(AnySdkDataSourceCondition.class)
@Import({SdkPrimaryDataConfig.class, SdkSecondaryDataConfig.class})
public class SdkAutoConfiguration {

    // 只有配置了sdk-primary數(shù)據(jù)源時才創(chuàng)建此Bean
    @Bean
    @Lazy  // 延遲加載,確保DAO先初始化
    @ConditionalOnProperty(prefix = "spring.datasource.sdk-primary", name = "jdbc-url")
    public SdkAppInfoService sdkAppInfoService(SdkAppInfoDao sdkAppInfoDao) {
        SdkAppInfoService service = new SdkAppInfoService();
        service.setSdkAppInfoDao(sdkAppInfoDao);
        return service;
    }
    
    // 只有配置了sdk-secondary數(shù)據(jù)源時才創(chuàng)建此Bean
    @Bean
    @Lazy
    @ConditionalOnProperty(prefix = "spring.datasource.sdk-secondary", name = "jdbc-url")
    public SdkOtherDataService sdkOtherDataService(SdkOtherDataDao sdkOtherDataDao) {
        SdkOtherDataService service = new SdkOtherDataService();
        service.setSdkOtherDataDao(sdkOtherDataDao);
        return service;
    }
}

這里使用了@Conditional(AnySdkDataSourceCondition.class)@ConditionalOnProperty注解,它的優(yōu)勢是能夠根據(jù)配置文件中的屬性值決定是否創(chuàng)建Bean。這樣設(shè)計的好處是:

  • 業(yè)務(wù)方未配置任何sdk數(shù)據(jù)源時,不會進行自動裝配
  • 只有在業(yè)務(wù)方真正配置了對應(yīng)數(shù)據(jù)源時,才會創(chuàng)建相關(guān)的Service Bean
  • 避免了不必要的Bean創(chuàng)建,減少內(nèi)存占用
  • 防止因缺少配置而導(dǎo)致的運行時錯誤

@Lazy 的核心作用是延遲 Bean 的初始化時機。在未使用該注解時,由于 Spring Bean 的創(chuàng)建順序不確定,特別是在條件化配置中,Service 可能會在依賴的 Dao 之前被創(chuàng)建,導(dǎo)致注入的 Dao 實例為 null,進而引發(fā)異常。這本質(zhì)上是由于 Bean 的依賴注入時機與初始化順序不匹配所導(dǎo)致的。

通過添加 @Lazy,可以確保 Service 只有在首次被使用時才初始化,此時其依賴的 Dao 必然已經(jīng)準(zhǔn)備就緒,從而從根本上避免了順序問題。

6. 注冊自動配置

最后,在spring.factories中注冊自動配置類:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.sdk.config.SdkAutoConfiguration

業(yè)務(wù)方使用方式

業(yè)務(wù)方使用我們這個SDK非常簡單:

添加依賴

<dependency>
    <groupId>com.example</groupId>
    <artifactId>sdk-multi-datasource</artifactId>
    <version>1.0.0</version>
</dependency>

配置數(shù)據(jù)源(按照Spring Boot的配置習(xí)慣):

spring:
  datasource:
    sdk-primary:
      jdbc-url: jdbc:mysql://primary-db-host:3306/primary_db
      username: db_user
      password: db_password
      driver-class-name: com.mysql.jdbc.Driver
    sdk-secondary:
      jdbc-url: jdbc:mysql://secondary-db-host:3306/secondary_db
      username: db_user
      password: db_password
      driver-class-name: com.mysql.jdbc.Driver

直接使用Service

@RestController
public class BusinessController {
    
    @Autowired
    private SdkAppInfoService sdkAppInfoService;
    
    @GetMapping("/app-info/{businessId}")
    public AppInfo getAppInfo(@PathVariable String businessId) {
        return sdkAppInfoService.getByBusinessId(businessId);
    }
}

效果與反思

通過這個SDK,我們成功將部分高頻的Dubbo調(diào)用改為了本地數(shù)據(jù)庫直連,顯著降低了延遲和系統(tǒng)負(fù)載。各個小組的反響也很好,他們喜歡這種"開箱即用"的體驗。

條件注解的使用讓我們的SDK更加智能和靈活:

  • 按需加載:只有配置了數(shù)據(jù)源時才會加載相關(guān)Bean
  • 避免沖突:通過條件判斷和Bean命名約定,避免了與業(yè)務(wù)項目的Bean沖突
  • 靈活配置:業(yè)務(wù)方可以根據(jù)需要選擇啟用哪些數(shù)據(jù)源

架構(gòu)思考:微服務(wù)與單體的平衡

這個優(yōu)化過程讓我思考微服務(wù)架構(gòu)與單體架構(gòu)之間的平衡。微服務(wù)架構(gòu)帶來了清晰的服務(wù)邊界和獨立的擴展性,但也**引入了網(wǎng)絡(luò)調(diào)用開銷和分布式系統(tǒng)的復(fù)雜性。

通過這個多數(shù)據(jù)源SDK,我們找到了一種折中方案:既保持了微服務(wù)的架構(gòu)優(yōu)勢,又在特定場景下獲得了接近單體架構(gòu)的性能。

最重要的是根據(jù)實際場景選擇最合適的方案。 在這個微服務(wù)大行其道的時代,偶爾回歸"單體"思維,反而能讓我們找到更好的平衡點。

從微服務(wù)到"部分單體",這不是倒退,而是架構(gòu)思維的成熟。作為開發(fā)者,我們應(yīng)該保持開放的心態(tài),根據(jù)實際需求選擇最合適的技術(shù)方案,而不是盲目追隨技術(shù)潮流。

到此這篇關(guān)于SpringBoot自動裝配之多數(shù)據(jù)源SDK解決Dubbo性能瓶頸詳解的文章就介紹到這了,更多相關(guān)SpringBoot數(shù)據(jù)源SDK優(yōu)化Dubbo性能內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringCloudAlibaba分布式配置中心Nacos持久化方式

    SpringCloudAlibaba分布式配置中心Nacos持久化方式

    文章介紹了如何創(chuàng)建Nacos數(shù)據(jù)庫并初始化,修改Nacos配置文件以通過數(shù)據(jù)庫動態(tài)修改配置文件,作者分享了遇到的問題和解決方法,提醒在重啟服務(wù)器時注意MySQL是否已成功啟動
    2026-01-01
  • 詳解Java接口的相關(guān)知識

    詳解Java接口的相關(guān)知識

    今天給大家?guī)淼氖顷P(guān)于Java基礎(chǔ)的相關(guān)知識,文章圍繞著Java接口展開,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • Java三級菜單工具類實現(xiàn)方式

    Java三級菜單工具類實現(xiàn)方式

    這篇文章通過實例代碼給大家介紹Java三級菜單工具類實現(xiàn)方式,常用的三個字段,子級id、父級id、其次是數(shù)組children,本文結(jié)合實例代碼給大家介紹的非常詳細,需要的朋友參考下吧
    2024-05-05
  • Mybatis傳遞多個參數(shù)的三種實現(xiàn)方法

    Mybatis傳遞多個參數(shù)的三種實現(xiàn)方法

    這篇文章主要介紹了Mybatis傳遞多個參數(shù)的三種實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-04-04
  • HttpClient POST請求第三方接口問題(多參數(shù)傳參)

    HttpClient POST請求第三方接口問題(多參數(shù)傳參)

    這篇文章主要介紹了HttpClient POST請求第三方接口問題(多參數(shù)傳參),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • SpringBoot中@Autowired生效方式詳解

    SpringBoot中@Autowired生效方式詳解

    @Autowired注解可以用在類屬性,構(gòu)造函數(shù),setter方法和函數(shù)參數(shù)上,該注解可以準(zhǔn)確地控制bean在何處如何自動裝配的過程。在默認(rèn)情況下,該注解是類型驅(qū)動的注入
    2022-06-06
  • SpringBoot與Redis的令牌主動失效機制實現(xiàn)

    SpringBoot與Redis的令牌主動失效機制實現(xiàn)

    本文詳細介紹了基于SpringBoot和Redis實現(xiàn)令牌主動失效機制,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-12-12
  • JAVA回顧:封裝,繼承,多態(tài)

    JAVA回顧:封裝,繼承,多態(tài)

    這篇文章主要介紹了java封裝繼承多態(tài),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-08-08
  • SpringBoot-RestTemplate實現(xiàn)調(diào)用第三方API的方式

    SpringBoot-RestTemplate實現(xiàn)調(diào)用第三方API的方式

    RestTemplate?是由?Spring?提供的一個?HTTP?請求工具,它提供了常見的REST請求方案的模版,例如?GET?請求、POST?請求、PUT?請求、DELETE?請求以及一些通用的請求執(zhí)行方法?exchange?以及?execute,下面看下SpringBoot?RestTemplate調(diào)用第三方API的方式
    2022-12-12
  • springboot集成nacos無法動態(tài)獲取nacos配置的問題

    springboot集成nacos無法動態(tài)獲取nacos配置的問題

    這篇文章主要介紹了springboot集成nacos無法動態(tài)獲取nacos配置的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09

最新評論

来安县| 沙洋县| 班玛县| 南陵县| 荣成市| 越西县| 鲁甸县| 遵义市| 永城市| 泰来县| 平乡县| 雷山县| 徐汇区| 汤阴县| 镇沅| 邵阳县| 新闻| 铜山县| 旬阳县| 新巴尔虎右旗| 教育| 三门峡市| 天峨县| 武安市| 垦利县| 芦山县| 康马县| 宁国市| 郎溪县| 瑞安市| 东海县| 肥西县| 海丰县| 鹤峰县| 汕尾市| 新干县| 茂名市| 河池市| 沈阳市| 宜黄县| 灌阳县|