mongodb 多數(shù)據(jù)源動態(tài)切換

有這樣一種場景,公司的各個鏈路服務(wù)要進行壓測,為了盡量得出準確的壓測結(jié)果,直接在生產(chǎn)的服務(wù)上進行壓測,但是有幾個條件

  • 壓測的請求必須要標記識別,數(shù)據(jù)庫,中間件這些數(shù)據(jù)的流轉(zhuǎn)存儲也必須作區(qū)分
  • mysql 有影子庫,判斷是壓測的請求就直接操作影子庫,除了 mysql, 可能還有 mongodb, es, hbase, redis這些數(shù)據(jù)都要落到影子庫,壓測完影子庫刪掉,不對生產(chǎn)庫造成影響
  • 重點是壓測請求的識別與數(shù)據(jù)源的動態(tài)切換
  • 請求的識別還包括在一個鏈路調(diào)用中,請求標記的識別跟透傳
  • 為了減少原有服務(wù)壓測時候的改動,可以把這些影子庫配置,動態(tài)切換的邏輯做成一個SDK,壓測項目引入 SDK依賴就可以了

根據(jù)上面場景,現(xiàn)在來說明mongodb的多數(shù)據(jù)源的影子庫,生產(chǎn)庫 動態(tài)切換怎么完成,mysql 已經(jīng)有很多成熟的方案了,redis,mq這些有機會再研究.

1 壓測請求的標記識別

這個應(yīng)該說是相當簡單了,在壓測的請求加個header 標記, 過濾去里面判斷到 header 標記是否存在,然后把標記放到 ThreadLocal 里面,后面的動態(tài)切換需要依賴這個 副本變量

public class RequestTagFilter implements Filter {
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        
    }
    
    @Override
    public void doFilter(ServletRequest request,
                         ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String header = httpServletRequest.getHeader("test");
        HeaderThreadLocal.setIsTestRequest(!StringUtils.isEmpty(header));
        chain.doFilter(request, response);
    }
    
    @Override
    public void destroy() {
        
    }
}

2 mongodb 多數(shù)據(jù)源配置

模擬生產(chǎn)的mongo配置.png
生產(chǎn)和影子庫的配置文件是分開的
mongo影子庫的配置.png

3 多數(shù)據(jù)源的切換邏輯的bean注入,只要加入這2個bean 就可以了

public class DynamicMongoDbFactory extends SimpleMongoDbFactory {

    //生產(chǎn)庫的客戶端與數(shù)據(jù)庫
    private MongoClient mongoClientPro;
    private String databaseNamePro;

    //影子庫的客戶端與數(shù)據(jù)庫
    private MongoClient mongoClientShodow;
    private String databaseNameShodow;
    
    private final boolean mongoInstanceCreated = false;
    private final PersistenceExceptionTranslator exceptionTranslator = null;

    private @Nullable WriteConcern writeConcern;

    public DynamicMongoDbFactory(MongoClient mongoClientPro, MongoClient mongoClientShodow,
                                 String databaseNamePro, String databaseNameShodow) {
        super(mongoClientPro, databaseNamePro);
        this.mongoClientPro = mongoClientPro;
        this.mongoClientShodow = mongoClientShodow;
        this.databaseNamePro = databaseNamePro;
        this.databaseNameShodow = databaseNameShodow;
    }
    @Override
    public void destroy() throws Exception {
        super.destroy();
    }
    @Override
    public MongoDatabase getDb() throws DataAccessException {
        //實現(xiàn)數(shù)據(jù)源動態(tài)切換的邏輯,判斷是否是壓測請求
        if (HeaderThreadLocal.isTestRequest()){
            return mongoClientShodow.getDatabase(databaseNameShodow);
        }
        return mongoClientPro.getDatabase(databaseNamePro);
    }
    @Override
    public MongoDatabase getDb(String dbName) throws DataAccessException {
        MongoDatabase mongoDatabase = mongoClientPro.getDatabase(dbName);
        if (mongoDatabase != null){
            return mongoDatabase;
        }
        return mongoClientPro.getDatabase(dbName);
    }
    @Override
    public PersistenceExceptionTranslator getExceptionTranslator() {
        return new MongoExceptionTranslator();
    }
    @SuppressWarnings("deprecation")
    @Override
    public DB getLegacyDb() {
        return mongoClientPro.getDB(databaseNamePro);
    }
}
@Configuration
@EnableAutoConfiguration
public class DynamicMongoRegister implements EnvironmentAware{

    private Environment environment;
    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
    @Bean
    public DynamicMongoDbFactory simpleMongoDbFactory(){
        String mongoUrlPro = environment.getProperty("spring.data.mongodb.uri");
        String mongoUrlShadow = environment.getProperty("spring.data.mongodb.uri.shadow");
        //這里獲取2個數(shù)據(jù)源的配置,生成2個數(shù)據(jù)源的客戶端實例
        MongoClient mongoClientPro = new MongoClient(new MongoClientURI(mongoUrlPro, MongoClientOptions.builder()));
        MongoClient mongoClientShadow = new MongoClient(new MongoClientURI(mongoUrlShadow, MongoClientOptions.builder()));

        return new DynamicMongoDbFactory(mongoClientPro, mongoClientShadow, "produce", "shadow");
    }
    @Bean
    public MongoTemplate mongoTemplate(){
        return new MongoTemplate(simpleMongoDbFactory());
    }
}

4mongo數(shù)據(jù)庫新建2個庫,對應(yīng)好庫名,建好表

image.png

5 進行測試

使用springboot-data-mongodb的操作方式.png

測試用例調(diào)用.png

經(jīng)過這樣的配置,是可以達到根據(jù)請求標記動態(tài)切換數(shù)據(jù)庫的

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 國家電網(wǎng)公司企業(yè)標準(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 12,537評論 6 13
  • ORA-00001: 違反唯一約束條件 (.) 錯誤說明:當在唯一索引所對應(yīng)的列上鍵入重復(fù)值時,會觸發(fā)此異常。 O...
    我想起個好名字閱讀 6,026評論 0 9
  • 一、前言 有贊致力于成為商家服務(wù)領(lǐng)域里最被信任的引領(lǐng)者,因為被信任,所有我們更需要為商家保駕護航,保障系統(tǒng)的穩(wěn)定性...
    有贊技術(shù)團隊閱讀 7,237評論 0 41
  • 電子商務(wù)網(wǎng)站的系統(tǒng):用戶模塊;商品模塊;訂單模塊;購物車模塊;供應(yīng)商模塊。 用戶模塊: 1.包括屬性:用戶名、密碼...
    近笙夜閱讀 599評論 0 0
  • 喜歡陽光 喜歡暴雨 喜歡藍天 喜歡大海 喜歡荷塘 喜歡涼鞋 喜歡發(fā)呆 喜歡閑聊 喜歡水果 喜歡扇子 喜歡竹凳 喜歡...
    離火七月閱讀 165評論 0 0

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