Монго открывает слишком много связей - PullRequest
13 голосов
/ 25 октября 2011

Я пытаюсь записать много данных в MongoDB в цикле Java. Я получаю ошибки в зависимости от количества открытых соединений.

Моя теория состоит в том, что поскольку 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 я получаю эту ошибку:

ВНИМАНИЕ: Исключение, определяющее размер maxBSON с использованием0 java.net.SocketException: сброс соединения

И mongodb выдает эту ошибку:

Вт 25.10 22:31:39 [initandlisten] соединение отказано, потому что тоже много открытых соединений: 204 из 204

Какой самый элегантный способ решить эту проблему?

Ответы [ 2 ]

20 голосов
/ 25 октября 2011

Вы создаете экземпляр класса Mongo для каждой отдельной операции. Это не будет работать, так как каждый экземпляр будет создавать и удерживать хотя бы одно соединение (но по умолчанию 10), и эти соединения будут удалены, только если Java GC очистит ваш экземпляр Mongo или когда вы вызовете close ().

Проблема в том, что в обоих случаях вы создаете их быстрее, чем они закрываются, даже используя один поток. Это исчерпает максимальное количество соединений в спешке. Правильное решение - сохранить один экземпляр Mongo, используя шаблон синглтона (Mongo.Holder предоставляет для этого функциональность, попробуйте Mongo.Holder.connect (..)). Быстрое «исправление» - это увеличение лимита файловых дескрипторов на вашем компьютере, чтобы максимальное количество подключений было значительно выше, но, очевидно, вы в конечном итоге можете достичь того же предела. Вы можете проверить свой текущий максимум, используя (в оболочке):

db.serverStatus().connections

TL; DR: относитесь к экземпляру Mongo как к одиночке и делайте его как можно более долгоживущим, и вы золотые. Реализация MongoFactory со статическим методом getInstance (), который возвращает лениво созданный экземпляр, отлично справится с задачей. Удачи.

4 голосов
/ 21 ноября 2014

Вы создаете новый MongoClient каждый раз, когда проходите свой метод.

У меня тоже была эта проблема, но я решил ее с помощью функции checkConnection:

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)

Надеюсь, я помог

...