SpringBoot 整合mongoDB并自定義連接池

SpringBoot 整合mongoDB并自定義連接池

得力于SpringBoot的特性,整合mongoDB是很容易的,我們整合mongoDB的目的就是想用它給我們提供的mongoTemplate,它可以很容易的操作mongoDB數據庫。

為了自定義連接池,我們在配置類中主要與MongoClientOptions、MongoCredential、MongoClient、MongoDbFactory打交道。最終的目的就是配置好一個MongoDbFactory的bean交由Spring管理。

Maven 依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

配置文件

mongodb:
  database: bfa_mongo
  username: "xxx"
  password: "xxxxx"
  address: "host:port"
  authenticationDatabase: [設置你的認證數據庫,如果有的話]
  # 連接池配置
  clientName: ${spring.application.name} # 客戶端的標識,用于定位請求來源等
  connectionTimeoutMs: 10000     # TCP連接超時,毫秒
  readTimeoutMs: 15000       # TCP讀取超時,毫秒
  poolMaxWaitTimeMs: 3000        #當連接池無可用連接時客戶端阻塞等待的時長,單位毫秒
  connectionMaxIdleTimeMs: 60000   #TCP連接閑置時間,單位毫秒
  connectionMaxLifeTimeMs: 120000    #TCP連接最多可以使用多久,單位毫秒
  heartbeatFrequencyMs: 20000      #心跳檢測發送頻率,單位毫秒
  minHeartbeatFrequencyMs: 8000    #最小的心跳檢測發送頻率,單位毫秒
  heartbeatConnectionTimeoutMs: 10000  #心跳檢測TCP連接超時,單位毫秒
  heartbeatReadTimeoutMs: 15000    #心跳檢測TCP連接讀取超時,單位毫秒
  connectionsPerHost: 20       # 每個host的TCP連接數
  minConnectionsPerHost: 5     #每個host的最小TCP連接數
  #計算允許多少個線程阻塞等待可用TCP連接時的乘數,算法:threadsAllowedToBlockForConnectionMultiplier*connectionsPerHost,當前配置允許10*20個線程阻塞
  threadsAllowedToBlockForConnectionMultiplier: 10

MongoConfig配置類

配置類中使用了lombok,如果你沒有用lombok依賴和IDE插件,你要重寫getter、Setter方法:
代碼稍長,可以復制在IDEA中查看:

import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Configuration
public class MongoConfig {

    /**
     * monogo 轉換器
     * @return
     */
    @Bean
    public MappingMongoConverter mappingMongoConverter(MongoDbFactory factory,
                                                       MongoMappingContext context, BeanFactory beanFactory, MongoCustomConversions conversions) {
        DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);
        MappingMongoConverter mappingConverter = new MappingMongoConverter(dbRefResolver, context);
        // 刪除 _class field
//    mappingConverter.setTypeMapper(new DefaultMongoTypeMapper(null));
        mappingConverter.setCustomConversions(conversions);
        return mappingConverter;
    }

    /**
     * 自定義mongo連接池
     * @param properties
     * @return
     */
    @Bean
    public MongoDbFactory mongoDbFactory(MongoClientOptionProperties properties) {
        //創建客戶端參數
        MongoClientOptions options = mongoClientOptions(properties);

        //創建客戶端和Factory
        List<ServerAddress> serverAddresses = new ArrayList<>();
        for (String address : properties.getAddress()) {
            String[] hostAndPort = address.split(":");
            String host = hostAndPort[0];
            Integer port = Integer.parseInt(hostAndPort[1]);
            ServerAddress serverAddress = new ServerAddress(host, port);
            serverAddresses.add(serverAddress);
        }

        //創建認證客戶端
        MongoCredential mongoCredential = MongoCredential.createScramSha1Credential(properties.getUsername(),
                properties.getAuthenticationDatabase() != null ? properties.getAuthenticationDatabase() : properties.getDatabase(),
                properties.getPassword().toCharArray());

        MongoClient mongoClient = new MongoClient(serverAddresses, mongoCredential, options);

        /** ps: 創建非認證客戶端*/
        //MongoClient mongoClient = new MongoClient(serverAddresses, mongoClientOptions);
        return new SimpleMongoDbFactory(mongoClient, properties.getDatabase());
    }

    /**
     * mongo客戶端參數配置
     * @return
     */
    @Bean
    public MongoClientOptions mongoClientOptions(MongoClientOptionProperties properties) {
        return MongoClientOptions.builder()
                .connectTimeout(properties.getConnectionTimeoutMs())
                .socketTimeout(properties.getReadTimeoutMs()).applicationName(properties.getClientName())
                .heartbeatConnectTimeout(properties.getHeartbeatConnectionTimeoutMs())
                .heartbeatSocketTimeout(properties.getHeartbeatReadTimeoutMs())
                .heartbeatFrequency(properties.getHeartbeatFrequencyMs())
                .minHeartbeatFrequency(properties.getMinHeartbeatFrequencyMs())
                .maxConnectionIdleTime(properties.getConnectionMaxIdleTimeMs())
                .maxConnectionLifeTime(properties.getConnectionMaxLifeTimeMs())
                .maxWaitTime(properties.getPoolMaxWaitTimeMs())
                .connectionsPerHost(properties.getConnectionsPerHost())
                .threadsAllowedToBlockForConnectionMultiplier(
                        properties.getThreadsAllowedToBlockForConnectionMultiplier())
                .minConnectionsPerHost(properties.getMinConnectionsPerHost()).build();
    }

    @Primary
    @Bean
    @ConfigurationProperties(prefix = "mongodb")
    public MongoClientOptionProperties mongoClientOptionProperties() {
        return new MongoClientOptionProperties();
    }

    @Getter
    @Setter
    @Validated
    @Configuration
    public static class MongoClientOptionProperties {

        /** 基礎連接參數 */
        private String database;
        private String username;
        private String password;
        @NotNull
        private List<String> address;
        private String authenticationDatabase;

        /** 客戶端連接池參數 */
        @NotNull
        @Size(min = 1)
        private String clientName;
        /** socket連接超時時間 */
        @Min(value = 1)
        private int connectionTimeoutMs;
        /** socket讀取超時時間 */
        @Min(value = 1)
        private int readTimeoutMs;
        /** 連接池獲取鏈接等待時間 */
        @Min(value = 1)
        private int poolMaxWaitTimeMs;
        /** 連接閑置時間 */
        @Min(value = 1)
        private int connectionMaxIdleTimeMs;
        /** 連接最多可以使用多久 */
        @Min(value = 1)
        private int connectionMaxLifeTimeMs;
        /** 心跳檢測發送頻率 */
        @Min(value = 2000)
        private int heartbeatFrequencyMs;

        /** 最小的心跳檢測發送頻率 */
        @Min(value = 300)
        private int minHeartbeatFrequencyMs;
        /** 計算允許多少個線程阻塞等待時的乘數,算法:threadsAllowedToBlockForConnectionMultiplier*connectionsPerHost */
        @Min(value = 1)
        private int threadsAllowedToBlockForConnectionMultiplier;
        /** 心跳檢測連接超時時間 */
        @Min(value = 200)
        private int heartbeatConnectionTimeoutMs;
        /** 心跳檢測讀取超時時間 */
        @Min(value = 200)
        private int heartbeatReadTimeoutMs;

        /** 每個host最大連接數 */
        @Min(value = 1)
        private int connectionsPerHost;
        /** 每個host的最小連接數 */
        @Min(value = 1)
        private int minConnectionsPerHost;
    }
}

MappingMongoConverter可以自定義mongo轉換器,主要自定義存取mongo數據時的一些操作,例如 mappingConverter.setTypeMapper(new DefaultMongoTypeMapper(null)) 方法會將mongo數據中的_class字段去掉。

最后通過 new SimpleMongoDbFactory(mongoClient, properties.getDatabase())方法配置了一個MongoDbFactory交由Spring管理,Springboot會拿這個MongoDbFactory工廠bean來new一個MongoTemplate,在MongoDbFactoryDependentConfiguration類下可以看到SpringBoot幫你做得事:

/**
 * Configuration for Mongo-related beans that depend on a {@link MongoDbFactory}.
 *
 * @author Andy Wilkinson
 */
@Configuration
@ConditionalOnBean(MongoDbFactory.class)
class MongoDbFactoryDependentConfiguration {

    private final MongoProperties properties;

    MongoDbFactoryDependentConfiguration(MongoProperties properties) {
        this.properties = properties;
    }
    
    //SpringBoot創建MongoTemplate實例
    @Bean
    @ConditionalOnMissingBean
    public MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory, MongoConverter converter) {
        return new MongoTemplate(mongoDbFactory, converter);
    }

    @Bean
    @ConditionalOnMissingBean(MongoConverter.class)
    public MappingMongoConverter mappingMongoConverter(MongoDbFactory factory, MongoMappingContext context,
            MongoCustomConversions conversions) {
        DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);
        MappingMongoConverter mappingConverter = new MappingMongoConverter(dbRefResolver, context);
        mappingConverter.setCustomConversions(conversions);
        return mappingConverter;
    }
    
    //...
}

SpringBoot利用我們配置好的MongoDbFactory在配置類中生成一個MongoTemplate,之后我們就可以在項目代碼中直接@Autowired了。因為用于生成MongoTemplate的MongoDbFactory是我們自己在MongoConfig配置類中生成的,所以我們自定義的連接池參數也就生效了。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,739評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,634評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,653評論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,063評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,835評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,235評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,459評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,000評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,819評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,004評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,257評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,676評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,937評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,717評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,003評論 2 374

推薦閱讀更多精彩內容

  • 本實驗主要包含三部分: 1、從源文件中加載數據至數據庫中。 2、從數據庫中查詢數據驗證加載是否完成。 3、自定義特...
    三萬_chenbing閱讀 2,713評論 0 2
  • 本文包括以下幾個方面: –安全措施 – 部署架構 – 系統優化 – 索引設計 – 備份監控 – 模式設計 – 程序...
    張偉科閱讀 3,998評論 0 9
  • 最近要做一個統計用戶行為的功能,需求不是很明確,數據結構也會不斷變化。本著快速開發的想法,也不想頻繁修改表結...
    神易風閱讀 14,828評論 0 6
  • 一大早弟弟一家同母親我們帶上自家養的土雞去L村看二姨。 夏季的鄉村是最美的,尤其在雨天整個山間被雨霧籠罩,顯得飄飄...
    二愣子的秋天閱讀 284評論 0 2
  • nodom2.js中出現的所有類 Class Complier Directive DirectiveManage...
    wwda閱讀 304評論 0 0