首頁 > 軟體

SpringBoot 整合mongoDB並自定義連線池的範例程式碼

2022-02-25 19:01:49

得力於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

注意:其中的address引數可以設定為一個陣列(代表叢集模式)

 address: 
    - "host:port"
    - "host2:port2"

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.boot.context.properties.EnableConfigurationProperties;
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.Arrays;
import java.util.List;

@Slf4j
@Configuration
@EnableConfigurationProperties(MongoConfig.MongoClientOptionProperties.class)
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);
        // remove _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.get(0), mongoCredential, options);
        //叢集模式
        if (serverAddresses.size() > 1) {
            mongoClient = new MongoClient(serverAddresses, new ArrayList<>(Arrays.asList(mongoCredential)));
        }
        /** ps: 建立非認證使用者端*/
        //MongoClient mongoClient = new MongoClient(serverAddresses, mongoClientOptions);
        return new SimpleMongoDbFactory(mongoClient, properties.getDatabase());
    }

    /**
     * mongo使用者端引數設定
     * @return
     */
    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();
    }

    @Getter
    @Setter
    @Validated
    @ConfigurationProperties(prefix = "mongodb")
    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);
	@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設定類中生成的,所以我們自定義的連線池引數也就生效了。

到此這篇關於SpringBoot 整合mongoDB並自定義連線池的文章就介紹到這了,更多相關SpringBoot自定義連線池內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com