MongoDB: ReadPreferenceServerSelector 没有选择服务器

MongoDB: No server chosen by ReadPreferenceServerSelector

最近在用mongodbjava新发布的async驱动。我正在写一些简单的测试代码,它们是:

    MongoClient mongoClient = MongoClients.create();
    System.out.println("database has been connected!");

    SingleResultCallback<Void> callbackWhenFinished = new SingleResultCallback<Void>() {
        @Override
        public void onResult(final Void result, final Throwable t) {
            System.out.println("Operation Finished!");
        }
    };

    mongoClient.listDatabaseNames().forEach(new Block<String>() {
        @Override
        public void apply(final String s) {
            System.out.println(s);
        }
    }, callbackWhenFinished);

但是没有调用回调函数,控制台输出为:

April 18, 2015 10:50:27 afternoon com.mongodb.diagnostics.logging.JULLogger log message: Cluster created with settings {hosts=[localhost:27017], mode=SINGLE, requiredClusterType=UNKNOWN, serverSelectionTimeout='30000 ms', maxWaitQueueSize=500}
database has been connected! April 18, 2015 10:50:28 afternoon com.mongodb.diagnostics.logging.JULLogger log message: No server chosen by ReadPreferenceServerSelector{readPreference=primary} from cluster description ClusterDescription{type=UNKNOWN, connectionMode=SINGLE, all=[ServerDescription{address=localhost:27017, type=UNKNOWN, state=CONNECTING}]}. Waiting for 30000 ms before timing out

所以你可以看到没有调用回调函数。有人知道为什么吗?

简短的回答是你的回调最终会被调用。

对于长答案,让我们研究一下您的代码:

    MongoClient mongoClient = MongoClients.create();
    System.out.println("database has been connected!");

MongoClient 不会阻止在后台等待与 MongoDB 的连接,内部连接池正在尝试连接。从您的日志中我可以看到您的默认 serverSelectionTimeout 为 30000 毫秒。

下一步你做一个 println 立即输出所以 "database has been connected!" 不管怎样都会被打印出来。

最后,您调用了 listDatabaseNames(),但不清楚是否有任何等待回调被调用。如果您添加一个闩锁然后等待响应,那么您将看到回调被调用,例如:

  System.out.println("======= Start =======");

  MongoClient mongoClient = MongoClients.create();

  final CountDownLatch latch = new CountDownLatch(1);

  SingleResultCallback<Void> callbackWhenFinished = new SingleResultCallback<Void>() {
      @Override
      public void onResult(final Void result, final Throwable t) {
          System.out.println("Operation Finished!");
          if (t != null) {
              System.out.println("listDatabaseNames() errored: " + t.getMessage());
          }
          latch.countDown();
      }
  };

  mongoClient.listDatabaseNames().forEach(new Block<String>() {
      @Override
      public void apply(final String s) {
          System.out.println(s);
      }
  }, callbackWhenFinished);

  latch.await();

  // close resources
  mongoClient.close();
  System.out.println("======= Finish =======");

现在使用锁存器 await() 直到回调被调用,现在我们应该看到以下两种情况之一发生:

  1. 没有MongoDB可用。 它最终会调用回调并打印出有错误。它将等到 serverSelectionTimeout 超时。

  2. 有一个 MongoDB 可用。 它最终会连接,对于每个数据库,它将应用 Block 并打印出数据库名称,然后最后它会调用回调信号它已完成。

我认为您每次都应该在 finally 子句中关闭 MongoClient 对象。我也遇到了同样的问题,当我在命令行中关闭连接时,我看到有很多连接是打开的。

尝试这样的事情(我使用 mongodb 3.0.7 和 mongo-java-driver 3.1.0):

package com.mkyong.core;

import org.bson.Document;
import org.junit.Test;

import com.mongodb.Block;
import com.mongodb.MongoClient;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoDatabase;

/**
 * Unit test for simple App.
 */
public class AppTest {

    @Test
    public void firstTest() throws Exception {
        MongoClient mongoClient = null;
        try {
            mongoClient = new MongoClient("127.0.0.1", 27017);
            MongoDatabase db = mongoClient.getDatabase("census");
            FindIterable<Document> iterable = db.getCollection("states").find();
            iterable.forEach(new Block<Document>() {
                @Override
                public void apply(final Document document) {
                    System.out.println(document);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                mongoClient.close();
            } catch (Exception e2) {
            }
        }
    }

}

有了这个,我可以毫无问题地使用我的连接。