java - 如何使用 mongoDB Java 驱动程序 3.4+ 或 3.6+ 避免异常过早到达流的末尾?(插入时)
问题描述
我正在尝试使用以下代码片段将一些文档插入到上限集合中:
// get document with specific fields
Document found = collection.find().first();
String getTitle = (String) found.get("title");
String getUrl = (String) found.get("url");
String getImg = (String) found.get("img");
String getPrice = (String) found.get("price");
// document which I want to get as new
Document doc = collection.find(new Document("title", getTitle)
.append("url", getUrl)
.append("img", getImg)
.append("price", getPrice)
.append("sent", true)).first();
// if the document doesn't exist, then insert as new
if (doc == null) {
collection.insertOne(new Document("title", getTitle)
.append("url", getUrl)
.append("img", getImg)
.append("price", getPrice)
.append("sent", true));
}
这意味着 - 重写文件。我正在插入具有更多字段而不是旧文档的新文档,因为上限集合不允许使用其他大小更新现有文档。因为我得到了一个例外:Cannot change the size of a document in a capped collection。
旧文档看起来像:
新的将是:
这段代码工作正常,但过了一段时间(在插入期间)我经常收到一个错误:
com.mongodb.MongoSocketReadException: Prematurely reached end of stream
at com.mongodb.connection.SocketStream.read(SocketStream.java:88)
at com.mongodb.connection.InternalStreamConnection.receiveResponseBuffers(InternalStreamConnection.java:491)
at com.mongodb.connection.InternalStreamConnection.receiveMessage(InternalStreamConnection.java:221)
at com.mongodb.connection.CommandHelper.receiveReply(CommandHelper.java:134)
at com.mongodb.connection.CommandHelper.receiveCommandResult(CommandHelper.java:121)
at com.mongodb.connection.CommandHelper.executeCommand(CommandHelper.java:32)
at com.mongodb.connection.InternalStreamConnectionInitializer.initializeConnectionDescription(InternalStreamConnectionInitializer.java:83)
at com.mongodb.connection.InternalStreamConnectionInitializer.initialize(InternalStreamConnectionInitializer.java:43)
at com.mongodb.connection.InternalStreamConnection.open(InternalStreamConnection.java:115)
at com.mongodb.connection.UsageTrackingInternalConnection.open(UsageTrackingInternalConnection.java:46)
at com.mongodb.connection.DefaultConnectionPool$PooledConnection.open(DefaultConnectionPool.java:381)
at com.mongodb.connection.DefaultConnectionPool.get(DefaultConnectionPool.java:96)
at com.mongodb.connection.DefaultConnectionPool.get(DefaultConnectionPool.java:82)
at com.mongodb.connection.DefaultServer.getConnection(DefaultServer.java:72)
at com.mongodb.binding.ClusterBinding$ClusterBindingConnectionSource.getConnection(ClusterBinding.java:86)
at com.mongodb.operation.OperationHelper.withConnectionSource(OperationHelper.java:237)
at com.mongodb.operation.OperationHelper.withConnection(OperationHelper.java:212)
at com.mongodb.operation.FindOperation.execute(FindOperation.java:482)
at com.mongodb.operation.FindOperation.execute(FindOperation.java:79)
at com.mongodb.Mongo.execute(Mongo.java:772)
at com.mongodb.Mongo$2.execute(Mongo.java:759)
at com.mongodb.FindIterableImpl$FindOperationIterable.first(FindIterableImpl.java:207)
at com.mongodb.FindIterableImpl.first(FindIterableImpl.java:148)
at project.Bot.onUpdateReceived(Bot.java:347)
正如我正确理解的那样,错误出现在行上(可能只是格式问题):
Document found = collection.find().first();
我研究并尝试使用以下代码解决错误(我正在使用免费的 Tier M0 集群):
MongoCredential credential = MongoCredential.createCredential("admin1", "admin", "mypassword".toCharArray());
MongoClientSettings settings = MongoClientSettings.builder()
.credential(credential)
.retryWrites(true)
.applyToConnectionPoolSettings(builder ->
builder.maxConnectionIdleTime(60000, TimeUnit.MILLISECONDS))
.applyToSocketSettings(builder ->
builder.keepAlive(true))
.applyToSslSettings(builder -> builder.enabled(true))
.applyToClusterSettings(builder -> {
builder.hosts(Arrays.asList(
new ServerAddress("cluster0-shard-00-00-ox90k.mongodb.net", 27017),
new ServerAddress("cluster0-shard-00-01-ox90k.mongodb.net", 27017),
new ServerAddress("cluster0-shard-00-02-ox90k.mongodb.net", 27017)
));
builder.requiredReplicaSetName("Cluster0-shard-0");
})
.build();
MongoClient mongoClient = MongoClients.create(settings);
同样的错误:com.mongodb.MongoSocketReadException: Prematurely reached end of stream
更新: 尝试而不是重写完整文档,只更改一个字段,例如:
Document found = database.getCollection("capped_collection").find(new Document("title", title)
.append("url", url)
.append("img", img)
.append("price", price)).first();
if (found == null) {
collection.insertOne(new Document("title", title)
.append("url", url)
.append("img", img)
.append("price", price)
.append("sent", false));
Document doc = collection.find(eq("sent", false)).first();
if(doc != null){
collection.updateOne(eq("sent", false), new Document("$set", new Document("sent", true)));
}
但还是有:
com.mongodb.MongoSocketReadException: Prematurely reached end of stream
有谁知道我需要纠正什么或如何解决错误Prematurely reached end of stream
?
我很感激任何帮助。
解决方案
选项1
错误已通过连接格式解决(使用参数maxIdleTimeMS
,ssl
和authSource
):
maxIdleTimeMS - 连接在被移除和关闭之前可以在池中保持空闲的最大毫秒数。
MongoClient mongoClient = MongoClients.create("mongodb://user:mypassword@cluster0-shard-00-00-ox90k.mongodb.net:27017,cluster0-shard-00-01-ox90k.mongodb.net:27017,cluster0-shard-00-02-ox90k.mongodb.net:27017/test?ssl=true&replicaSet=Cluster0-shard-0&authSource=admin&retryWrites=true&maxIdleTimeMS=5000");
或者您可以通过编程方式配置凭据(使用 java 驱动程序3.6+
版本):
admin
- 是在 Atlas 中定义用户的数据库;
user
- 是用户名;
mypassword
- 是密码;
MongoCredential credential = MongoCredential.createCredential("user", "admin", "mypassword".toCharArray());
MongoClientSettings settings = MongoClientSettings.builder()
.credential(credential)
.retryWrites(true)
.applyToConnectionPoolSettings(builder ->
builder.maxConnectionIdleTime(5000, TimeUnit.MILLISECONDS))
.applyToSslSettings(builder -> builder.enabled(true))
.applyToClusterSettings(builder -> {
builder.hosts(Arrays.asList(
new ServerAddress("cluster0-shard-00-00-ox90k.mongodb.net", 27017),
new ServerAddress("cluster0-shard-00-01-ox90k.mongodb.net", 27017),
new ServerAddress("cluster0-shard-00-02-ox90k.mongodb.net", 27017)
));
builder.requiredReplicaSetName("Cluster0-shard-0");
})
.build();
MongoClient mongoClient = MongoClients.create(settings);
-
定义主机名时,请提及所有副本集主机名。
这将解决 java.net.UnknownHostException。
-
您不能像 mongodb+srv 连接那样使用 DNS 短名称。
选项 #2
此外,错误可以通过调用mongodb.MongoClient.connect
一次来解决,而不是每个请求。尝试重组代码以在插入特定文档期间调用连接一次,而不是每次调用。在这种情况下,您可以避免选项 #1 中的任何附加参数。
正好:
mongodb+srv://admin:password@cluster0-ox90k.mongodb.net/test?retryWrites=true&w=majority
特别感谢mongoDB 支持的帮助。