SpringBoot中的MongoConfig

背景

我负责的一个项目采用了MongoDB作为数据源,同时使用了springboot生态顺利成章的使用了org.springframework.boot:spring-boot-starter-data-mongodb。开始配置采用下面的链接方式

1
spring.data.mongodb.uri=mongodb://127.0.0.1:27017/faceguard?retryWrites=true

这种方案我们遇到了俩个问题:

  1. 很难对连接池进行定制化的设计,连连接数都设置不了。
  2. 更为重要的我们对mongodb的server加了4层的负载,而这种链接方式不自持心跳导致我们的链接空闲一段时间内会自己断掉;

解决方案

自己注入MongoDbFactory,可以按需要注入进自己的参数了。我们需要实现下面几步

  1. 实现MongoClientOptionProperties,将配置文件中mongo.client.option签注的属性注入到MongoClientOptionProperties中
  2. 利用MongoClientOptionProperties生成MongoClientOptions对象,MongoClientOptions是典型的builder模式,通过build()生成对象在传递给MongoClient
  3. 注入MongoDbFactory方法时候将MongoClient传递给MongoDbFactory

talk is cheap上代码,MongoClientOptionProperties只提供了几个常用设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
* @Author: liuhaoeric
* Create time: 2020/04/18
* Description:
*/
@Data
@ConfigurationProperties(prefix = "mongo.client.option")
public class MongoClientOptionProperties {
private int connectionsPerHost = 20;
private int minConnectionsPerHost = 20;
private int connectTimeout = 6000;
private int maxWaitTime = 6000;
private int socketTimeout = 0;
private int heartbeatFrequency = 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package com.ericliu.mongodb.plus.config;

import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
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.DefaultMongoTypeMapper;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;

import java.util.ArrayList;
import java.util.List;

/**
* @author pengkai
* @date 2019-02-21
*/
@Configuration
@EnableConfigurationProperties(MongoClientOptionProperties.class)//建议这样引入MongoClientOptionProperties
public class MongoConfig {
private static final Logger LOG = LoggerFactory.getLogger(MongoConfig.class);

@Value("${mongo.database}")
private String database;
@Value("${mongo.host}")
private String host;
@Value("${mongo.port}")
private int port;
@Value("${mongo.username:}")
private String userName;
@Value("${mongo.password:}")
private String password;

@Bean
public GridFSBucket getGridFSBuckets(MongoDbFactory mongoDbFactory) {
MongoDatabase db = mongoDbFactory.getDb();
return GridFSBuckets.create(db);
}

@Bean
public MappingMongoConverter mappingMongoConverter(MongoDbFactory factory, MongoMappingContext context, BeanFactory beanFactory) {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);

MappingMongoConverter mappingConverter = new MappingMongoConverter(dbRefResolver, context);
try {
mappingConverter.setCustomConversions(beanFactory.getBean(CustomConversions.class));
} catch (NoSuchBeanDefinitionException ignore) {
}

// Don't save _class to mongo
mappingConverter.setTypeMapper(new DefaultMongoTypeMapper(null));
return mappingConverter;
}


// 覆盖默认的MongoDbFactory
@Bean
MongoDbFactory mongoDbFactory(MongoClientOptionProperties mongoClientOptionProperties) {
MongoClientOptions.Builder builder = new MongoClientOptions.Builder();
builder.connectionsPerHost(mongoClientOptionProperties.getConnectionsPerHost());
builder.minConnectionsPerHost(mongoClientOptionProperties.getMinConnectionsPerHost());
builder.connectTimeout(mongoClientOptionProperties.getConnectTimeout());
builder.maxWaitTime(mongoClientOptionProperties.getMaxWaitTime());
builder.socketTimeout(mongoClientOptionProperties.getSocketTimeout());
if (mongoClientOptionProperties.getHeartbeatFrequency() > 0) {
builder.heartbeatFrequency(mongoClientOptionProperties.getHeartbeatFrequency());
}
MongoClientOptions mongoClientOptions = builder.build();

// MongoDB地址列表
List<ServerAddress> serverAddresses = new ArrayList<>();
ServerAddress serverAddress = new ServerAddress(host, port);
serverAddresses.add(serverAddress);

// 连接认证
MongoClient mongoClient = null;
if (!Strings.isEmpty(userName)) {
mongoClient = new MongoClient(serverAddresses, MongoCredential.createCredential(
userName,
database,
password.toCharArray()), mongoClientOptions);
} else {
mongoClient = new MongoClient(serverAddresses, mongoClientOptions);
}

//创建客户端和Factory
return new SimpleMongoDbFactory(mongoClient, database);
}

@Bean
MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory) {
return new MongoTemplate(mongoDbFactory);
}
}

代码

github地址:https://github.com/liuhao163/mongodb-plus