MongoDB 集群基础(重要)
在连接之前,必须理解 MongoDB 集群的几种常见架构,因为你的 Java 连接字符串会根据架构的不同而变化。

a) 副本集
这是最常见的高可用性架构,它由多个数据节点组成,其中一个主节点(Primary)负责处理所有写操作,其余为从节点(Secondary)负责复制主节点的数据并提供读操作。
- 特点:自动故障转移,如果主节点宕机,副本集会自动从从节点中选举一个新的主节点。
- Java 连接:你需要连接到副本集中的至少一个成员(最好是所有成员的地址),驱动会自动发现集群中的所有成员(主、从、仲裁者)并进行智能路由。
b) 分片集群
用于解决数据量和请求量巨大的问题,它通过将数据水平切分并分布到不同的服务器(分片)上来实现扩展。
- 组成:
- Shard Servers (分片):存储实际数据。
- Config Servers (配置服务器):存储集群的元数据(数据分片在哪个分片上)。
- Query Routers (路由器):最常见的是
mongos,客户端连接它,它会查询配置服务器,然后将请求路由到正确的分片。
- Java 连接:你不需要连接到每个分片,你只需要连接到一个或多个
mongos路由器即可,驱动会与mongos通信,mongos负责后续的路由。
Java 驱动核心:MongoClient
在 Java 中,我们使用 MongoClient 类来连接 MongoDB,对于集群,关键在于如何正确配置它的 MongoClientSettings。
关键配置点:
-
applyToClusterSettings:这是配置集群连接的核心。
(图片来源网络,侵删)hosts(List<ServerAddress>):指定集群中所有节点的地址,对于副本集,这是所有副本集成员的地址;对于分片集群,这是所有mongos路由器的地址。requiredReplicaSetName(String):(仅副本集) 指定副本集的名称,这是驱动区分不同集群的关键。mode(ClusterConnectionMode):指定集群模式,通常是MULTIPLE(连接多个节点)。
-
applyToSocketSettings:配置网络套接字。connectTimeout(Duration):连接超时时间。readTimeout(Duration):读取超时时间。
-
applyToSslSettings:如果集群启用了 TLS/SSL,必须配置此项。enabled(boolean):启用 SSL。invalidHostNameAllowed(boolean):是否允许无效的主机名(生产环境建议为false)。context(SSLContext):提供自定义的 SSL 上下文。
-
credential:认证配置。Credential对象包含了用户名、认证数据库、密码和认证机制(如SCRAM-SHA-1,SCRAM-SHA-256,PLAIN等)。
连接字符串 vs. Builder 模式
虽然可以使用传统的连接字符串,但强烈推荐使用 Builder 模式,因为它更清晰、更易于维护,并且能更好地利用 Java 驱动的最新功能。
连接字符串 示例 (不推荐)
- 副本集:
mongodb://user:pass@host1:27017,host2:27017,host3:27017/mydb?replicaSet=myReplSet&authSource=admin - 分片集群:
mongodb://user:pass@mongos1:27017,mongos2:27017/mydb?authSource=admin
Builder 模式 (强烈推荐)
下面我们使用 Builder 模式来构建不同集群的连接。
完整 Java 代码示例
确保你的 pom.xml 中有最新的 MongoDB Java 驱动依赖:
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>4.11.1</version> <!-- 请使用最新版本 -->
</dependency>
示例 1:连接副本集
假设你的副本集名为 myReplSet,成员为 rs1.example.com:27017, rs2.example.com:27017, rs3.example.com:27017,用户为 clusterUser,认证数据库为 admin。
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import java.util.Arrays;
import java.util.List;
public class MongoReplicaSetConnectionExample {
public static void main(String[] args) {
// 1. 定义集群节点列表
List<ServerAddress> hosts = Arrays.asList(
new ServerAddress("rs1.example.com", 27017),
new ServerAddress("rs2.example.com", 27017),
new ServerAddress("rs3.example.com", 27017)
);
// 2. 定义认证凭据
MongoCredential credential = MongoCredential.createCredential(
"clusterUser",
"admin",
"your_password".toCharArray()
);
// 3. 构建 MongoClientSettings
MongoClientSettings settings = MongoClientSettings.builder()
.applyToClusterSettings(builder ->
builder.hosts(hosts) // 设置集群节点
.requiredReplicaSetName("myReplSet") // 【关键】设置副本集名称
)
.credential(credential) // 设置认证
.applyToSslSettings(builder -> builder.enabled(true)) // 如果启用SSL
.build();
// 4. 创建 MongoClient
try (MongoClient mongoClient = MongoClients.create(settings)) {
System.out.println("成功连接到 MongoDB 副本集!");
// 5. 获取数据库和集合进行操作
MongoDatabase database = mongoClient.getDatabase("testdb");
System.out.println("获取数据库 'testdb' 成功。");
// 可以执行一些CRUD操作...
// database.getCollection("myCollection").insertOne(new Document("name", "Java Driver"));
System.out.println("操作完成。");
} catch (Exception e) {
System.err.println("连接 MongoDB 失败: " + e.getMessage());
e.printStackTrace();
}
}
}
示例 2:连接分片集群
假设你的 mongos 路由器地址为 mongos1.example.com:27017, mongos2.example.com:27017。
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import java.util.Arrays;
import java.util.List;
public class MongoShardedClusterConnectionExample {
public static void main(String[] args) {
// 1. 定义 mongos 路由器列表
List<ServerAddress> hosts = Arrays.asList(
new ServerAddress("mongos1.example.com", 27017),
new ServerAddress("mongos2.example.com", 27017)
);
// 2. 定义认证凭据
MongoCredential credential = MongoCredential.createCredential(
"shardedUser",
"admin",
"your_sharded_password".toCharArray()
);
// 3. 构建 MongoClientSettings
// 注意:对于分片集群,不需要 requiredReplicaSetName
MongoClientSettings settings = MongoClientSettings.builder()
.applyToClusterSettings(builder ->
builder.hosts(hosts) // 设置 mongos 路由器
)
.credential(credential) // 设置认证
.applyToSslSettings(builder -> builder.enabled(true)) // 如果启用SSL
.build();
// 4. 创建 MongoClient
try (MongoClient mongoClient = MongoClients.create(settings)) {
System.out.println("成功连接到 MongoDB 分片集群!");
// 5. 获取数据库和集合进行操作
MongoDatabase database = mongoClient.getDatabase("shardeddb");
System.out.println("获取数据库 'shardeddb' 成功。");
// 执行操作...
// database.getCollection("myShardedCollection").insertOne(new Document("key", "value"));
System.out.println("操作完成。");
} catch (Exception e) {
System.err.println("连接 MongoDB 失败: " + e.getMessage());
e.printStackTrace();
}
}
}
最佳实践
-
使用连接池:
MongoClient内部已经实现了高效、可配置的连接池,你应该为整个应用程序创建一个MongoClient的单例实例,并在整个应用生命周期中复用它,而不是为每个请求都创建一个新的,不要调用mongoClient.close(),除非你的应用即将关闭。 -
启用 TLS/SSL:在生产环境中,MongoDB 集群必须通过 TLS/SSL 加密通信,以保护数据在传输过程中的安全。
-
正确配置超时:为网络连接和读写操作设置合理的超时时间,以防止因网络问题导致的应用程序线程长时间阻塞。
-
处理
MongoTimeoutException:当集群发生故障转移或网络抖动时,你的应用程序可能会遇到MongoTimeoutException或MongoCommandException,你的应用逻辑应该能够优雅地处理这些异常,例如通过重试机制。 -
使用现代 API:尽量使用
com.mongodb.client包下的新 API(如MongoClient,MongoDatabase,MongoCollection),而不是旧的com.mongodb.Mongo。 -
监控:监控你的 MongoDB 集群和 Java 应用程序的连接情况,关注连接池的使用情况(活跃连接数、空闲连接数等),以及慢查询和错误率。
| 特性 | 副本集 | 分片集群 |
|---|---|---|
| 连接目标 | 副本集的所有成员 | 一个或多个 mongos 路由器 |
| 关键配置 | requiredReplicaSetName("...") |
无需此配置 |
| 驱动行为 | 自动发现主从节点,读写分离 | 依赖 mongos 进行路由 |
| 连接字符串 | mongodb://.../db?replicaSet=myReplSet |
mongodb://.../db |
| Builder 模式 | builder.requiredReplicaSetName(...) |
仅设置 hosts |
通过遵循以上指南,你可以构建出健壮、可靠且高效的 Java 应用程序来连接和操作 MongoDB 集群,核心在于正确配置 MongoClientSettings,并遵循单例 MongoClient 和启用 SSL 的最佳实践。
