夏日残霞 發表於 2019-12-5 21:58:00

SpringBoot 整合mongoDB并自定义连接池

<h1 id="springboot-整合mongodb并自定义连接池">SpringBoot 整合mongoDB并自定义连接池</h1>
<blockquote>
<p>得力于SpringBoot的特性,整合mongoDB是很容易的,我们整合mongoDB的目的就是想用它给我们提供的mongoTemplate,它可以很容易的操作mongoDB数据库。</p>
</blockquote>
<p>为了自定义连接池,我们在配置类中主要与MongoClientOptions、MongoCredential、MongoClient、MongoDbFactory打交道。最终的目的就是配置好一个MongoDbFactory的bean交由Spring管理。</p>
<h2 id="maven-依赖">Maven 依赖</h2>
<pre><code>&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-data-mongodb&lt;/artifactId&gt;
&lt;/dependency&gt;
</code></pre>
<h2 id="配置文件">配置文件</h2>
<pre><code>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
</code></pre>
<p>注意:其中的address参数可以配置为一个数组(代表集群模式)</p>
<pre><code> address:
    - "host:port"
    - "host2:port2"
</code></pre>
<h3 id="mongoconfig配置类">MongoConfig配置类</h3>
<p>配置类中使用了lombok,如果你没有用lombok依赖和IDE插件,你要重写getter、Setter方法:<br>
代码稍长,可以复制在IDEA中查看:</p>
<pre><code class="language-Java">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&lt;ServerAddress&gt; serverAddresses = new ArrayList&lt;&gt;();
      for (String address : properties.getAddress()) {
            String[] hostAndPort = address.split(":");
            String host = hostAndPort;
            Integer port = Integer.parseInt(hostAndPort);
            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() &gt; 1) {
            mongoClient = new MongoClient(serverAddresses, new ArrayList&lt;&gt;(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&lt;String&gt; 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;
    }
}
</code></pre>
<p><strong>MappingMongoConverter</strong>可以自定义mongo转换器,主要自定义存取mongo数据时的一些操作,例如 mappingConverter.setTypeMapper(new DefaultMongoTypeMapper(null)) 方法会将mongo数据中的_class字段去掉。</p>
<p>最后通过 new SimpleMongoDbFactory(mongoClient, properties.getDatabase())方法配置了一个MongoDbFactory交由Spring管理,Springboot会拿这个MongoDbFactory工厂bean来new一个MongoTemplate,在MongoDbFactoryDependentConfiguration类下可以看到SpringBoot帮你做得事:</p>
<pre><code class="language-Java">/**
* 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;
        }
       
        //...
}
</code></pre>
<p>SpringBoot利用我们配置好的MongoDbFactory在配置类中生成一个MongoTemplate,之后我们就可以在项目代码中直接@Autowired了。因为用于生成MongoTemplate的MongoDbFactory是我们自己在MongoConfig配置类中生成的,所以我们自定义的连接池参数也就生效了。</p><br><br>
来源:https://www.cnblogs.com/keeya/p/11992267.html
頁: [1]
查看完整版本: SpringBoot 整合mongoDB并自定义连接池