Mongo 打开的连接太多

2022-09-03 12:55:07

我试图在Java循环中向MongoDB写入大量数据。我收到基于打开的连接数的错误。

我的理论是,由于MongoDB不是事务性的,因此可以同时打开许多连接。然而,Java代码也能够非常快速地循环,在一段时间后,循环迭代的数量开始超过可用连接的数量,Mongo碰壁了。

我的代码看起来像这样。我已经看到它建议不要做,但然后你只是得到错误更快。m.close()

public static void upsert(){
    Mongo m = null;
    DB db = null;

    try {
    m = new Mongo("localhost");
    db = m.getDB("sempedia");    } catch (UnknownHostException e1) { e1.printStackTrace(); } catch (MongoException e1) { e1.printStackTrace(); }

    // create documents
    // I am doing an upsert - hence the doc, doc
    DBCollection triples;
try {
        triples = db.getCollection("triples");
        triples.update(doc,doc,true,false); 
    } catch (MongoException e) { e.printStackTrace(); }

    m.close();
}

在我的Java控制台中,我收到此错误:

警告:使用 0 java.net.Socket 确定 maxBSON 大小的异常:连接重置

mongodb给出了这个错误:

星期二 10 月 25 22:31:39 [初始化和听] 连接被拒绝,因为太多的开放连接: 204 的 204

处理这个问题的最优雅方法是什么?


答案 1

您正在为每个单独的操作创建一个 Mongo 类的实例。这不起作用,因为每个实例将创建并保存至少一个连接(但默认情况下为10个),并且只有在Java GC清理Mongo实例或调用close()时,这些连接才会被删除。

问题在于,在这两种情况下,即使使用单个线程,您创建它们的速度也比关闭它们的速度快。这将在匆忙中耗尽最大连接量。正确的解决方法是使用单例模式保留一个Mongo实例(Mongo.Holder为此提供了功能,请尝试Mongo.Holder.connect(..))。一个快速的“修复”是增加计算机上的文件描述符限制,以便最大连接量大大增加,但显然您最终可能会达到相同的限制。您可以使用(在外壳中)检查当前的最大值:

db.serverStatus().connections

TL;DR : 将 Mongo 实例视为单例实例,并使它们尽可能长久地存在,您就是黄金。使用静态方法getInstance()实现MongoFactory,返回一个懒惰创建的实例,这将很好地解决问题。祝你好运。


答案 2

每次你通过你的方法出现时,你都会制作一个新的MongoClient。

我也有这个问题,但我解决了它,做了一个检查连接功能:

private static DBCollection checkConnection(String collection) throws UnknownHostException{
    if(db == null){
        db = (new MongoClient(host, port)).getDB(database);
    }
    return db.getCollection(collection);
}

在实例化变量的顶部,有以下内容:

private static DB db = null;
private static String database = "<Your database>";
private static String host = "localhost"; //<--- usually localhost
private static int port = 27017; //<---- usually 27017, but you can change it.

然后,当您创建一个方法时,请像这样使用它:

 public <whatever> someFunction() throws UnknownHostException{
    DBCollection dbCollection = checkConnection("triples"); //<--- can be "triples"
                                                    //or whatever collection you want

     <REST OF YOUR FUNCTION HERE USING THE AMAZING COLLECTION
 }

这种方法有几个优点:

- Code reusability, you won't have to write the same thing at every method
- Readability, which programmer doesn't understand this: 
  DBCollection dbCollection = checkConnection("triples");  
- ONLY ONE CONNECTION WHICH YOU RE-USE (this doesn't affect data not being synced)

希望我有帮助