欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

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

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

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

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

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

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

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

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

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

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

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

SDK項(xiàng)目結(jié)構(gòu)設(shè)計(jì)

先來看看整個(gè)SDK的項(xiàng)目結(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

核心代碼實(shí)現(xiàn)

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

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

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

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

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

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

@Configuration
// 條件注解:只有配置了sdk-primary數(shù)據(jù)源時(shí)才啟用此配置
@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ù)項(xiàng)目中的Bean沖突,所有DAO接口都加上了"Sdk"前綴:

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

4. Service層實(shí)現(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. 自動(dòng)配置類:解決依賴注入問題

這是整個(gè)SDK的核心,我通過條件判斷確保只有配置了對(duì)應(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ù)源時(shí)才創(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ù)源時(shí)才創(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)勢(shì)是能夠根據(jù)配置文件中的屬性值決定是否創(chuàng)建Bean。這樣設(shè)計(jì)的好處是:

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

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

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

6. 注冊(cè)自動(dòng)配置

最后,在spring.factories中注冊(cè)自動(dòng)配置類:

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

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

業(yè)務(wù)方使用我們這個(gè)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);
    }
}

效果與反思

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

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

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

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

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

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

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

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

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

相關(guān)文章

  • 提升網(wǎng)絡(luò)請(qǐng)求穩(wěn)定性HttpClient的重試機(jī)制深入理解

    提升網(wǎng)絡(luò)請(qǐng)求穩(wěn)定性HttpClient的重試機(jī)制深入理解

    這篇文章主要為大家介紹了提升網(wǎng)絡(luò)請(qǐng)求穩(wěn)定性HttpClient的重試機(jī)制深入理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • SpringBoot中整合knife4j接口文檔的實(shí)踐

    SpringBoot中整合knife4j接口文檔的實(shí)踐

    這篇文章主要介紹了SpringBoot中整合knife4j接口文檔的實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • SpringBoot Starter機(jī)制及整合tomcat的實(shí)現(xiàn)詳解

    SpringBoot Starter機(jī)制及整合tomcat的實(shí)現(xiàn)詳解

    這篇文章主要介紹了SpringBoot Starter機(jī)制及整合tomcat的實(shí)現(xiàn),我們知道SpringBoot自己在“后臺(tái)”幫我們配置了很多原本需要我們手動(dòng)去的東西,至于這個(gè)“后臺(tái)”是啥,就是Starter機(jī)制
    2022-09-09
  • Java實(shí)現(xiàn)屏幕截圖工具的代碼分享

    Java實(shí)現(xiàn)屏幕截圖工具的代碼分享

    這篇文章主要為大家介紹了如何利用Java語言編寫一個(gè)電腦屏幕截圖工具,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)有一定的幫助,需要的可以參考一下
    2022-05-05
  • 關(guān)于Javaweb的轉(zhuǎn)發(fā)和重定向詳解

    關(guān)于Javaweb的轉(zhuǎn)發(fā)和重定向詳解

    這篇文章主要介紹了關(guān)于Javaweb的轉(zhuǎn)發(fā)和重定向詳解,請(qǐng)求的轉(zhuǎn)發(fā),是指服務(wù)器收到請(qǐng)求后,從一個(gè)服務(wù)器端資源跳轉(zhuǎn)到同一個(gè)服務(wù)器端另外一個(gè)資源的操作,需要的朋友可以參考下
    2023-05-05
  • Java使用BigDecimal公式精確計(jì)算及精度丟失問題

    Java使用BigDecimal公式精確計(jì)算及精度丟失問題

    在工作中經(jīng)常會(huì)遇到數(shù)值精度問題,比如說使用float或者double的時(shí)候,可能會(huì)有精度丟失問題,下面這篇文章主要給大家介紹了關(guān)于Java使用BigDecimal公式精確計(jì)算及精度丟失問題的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • spring初始化方法的執(zhí)行順序及其原理分析

    spring初始化方法的執(zhí)行順序及其原理分析

    這篇文章主要介紹了spring初始化方法的執(zhí)行順序及其原理分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Java編碼摘要算法實(shí)例解析

    Java編碼摘要算法實(shí)例解析

    這篇文章主要介紹了Java編碼摘要算法實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • vue2向springboot傳值接收不到的解決方法

    vue2向springboot傳值接收不到的解決方法

    本文主要介紹了vue2向springboot傳值接收不到的解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • 密碼系統(tǒng)AES私鑰RSA公鑰的加解密示例

    密碼系統(tǒng)AES私鑰RSA公鑰的加解密示例

    這篇文章主要為大家詮釋并介紹了AES私鑰RSA公鑰的加解密系統(tǒng)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-03-03

最新評(píng)論