【问题标题】:How to avoid an exception Prematurely reached end of stream using mongoDB Java driver 3.4+ or 3.6+? (during insertion)如何使用 mongoDB Java 驱动程序 3.4+ 或 3.6+ 避免异常过早到达流的末尾? (插入时)
【发布时间】:2019-03-05 18:13:06
【问题描述】:

我正在尝试使用此 sn-p 代码将一些文档插入到 capped 集合中:

            // 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));
        }

这意味着 - 重写文档。我正在插入具有更多字段而不是旧文档的新文档,因为上限集合不允许使用其他大小更新现有文档。 因为我得到一个例外:无法更改上限集合中文档的大小

旧文档看起来像:

新的将是:

这段代码工作正常,但过了一段时间(插入期间)我不断收到错误:

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();

我研究并尝试使用此code 解决错误(我正在使用免费的 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

尝试更改version,结果为here

有谁知道我需要纠正什么或如何解决错误Prematurely reached end of stream

感谢您的帮助。

【问题讨论】:

  • 需要澄清一点:1)如果插入后需要修改文档,是真的需要capped collection,还是需要TTL索引? (见docs.mongodb.com/manual/core/index-ttl)。 2)“过早到达流的末尾”通常是服务器/网络问题,这是一组不同的问题与上限集合中的文档修改。你想问哪个问题? 3) Atlas 应该有一个选项可以显示正确的连接字符串。您是否尝试过在MongoClient.create() 中使用它而不是使用new MongoClient()
  • 关于上限集合:如果上限集合中的文档会改变文档的大小,则您不能修改该文档。但是,如果它不改变大小,您可以修改它。例如,如果您插入带有{_id:.., title:..., uri:..., img:..., price:..., sent:false} 的原始文档,如果您随后进行更新,例如db.collection.updateOne({_id:..}, {$set: {sent:true}})不会改变文档大小(只设置false -> true),更新成功。
  • @KevinAdistambha,在插入过程中抛出“过早到达流尾”,所以我不能插入所有新文档而不是旧文档。我想我需要使用一些options 来修改我的连接字符串。
  • Re: 连接字符串,如果你在Atlas中点击“连接”按钮,然后点击“连接你的应用程序”,“驱动程序3.6或更高版本”,它会显示你需要的连接字符串。就我而言,它显示mongodb+srv://kevinadi:<PASSWORD>@cluster0-wsdgm.mongodb.net/test?retryWrites=true。我使用MongoClients.create(...)将该字符串放入测试Java应用程序中,它可以连接到Atlas。
  • 是的,你可以。有关完整矩阵,请参阅 Java driver language compatibility list

标签: java mongodb


【解决方案1】:

当您尝试连接到 Atlas 上的免费集群时,请检查您的 IP 是否已列入白名单。转到 1) 添加连接 IP 地址,然后单击 IP 访问列表选项卡,您可以添加 IP 地址。

【讨论】:

    【解决方案2】:

    选项 #1

    错误已通过连接格式解决(使用参数maxIdleTimeMSsslauthSource):

    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);
    
    1. 定义主机名时,提及所有副本集主机名。

    这将解决 java.net.UnknownHostException。

    1. 您不能将 DNS 短名称用于 mongodb+srv 连接。


    选项 #2

    此外,该错误可以通过调用一次mongodb.MongoClient.connect 来解决,而不是每次请求。尝试重构代码以在插入特定文档期间调用连接一次,而不是每次调用。在这种情况下,您可以避免选项 #1 中的任何附加参数。

    够了:

    mongodb+srv://admin:password@cluster0-ox90k.mongodb.net/test?retryWrites=true&w=majority
    

    特别感谢mongoDB support的帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-12
      • 2018-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多