«Топология была уничтожена» при использовании MongoDB с собственным драйвером и Express.js - PullRequest
0 голосов
/ 25 октября 2018

Я реализовал простое приложение, которое извлекает данные из MongoDB

const express = require('express')
const app = express()
const port = 3000

const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');

const dbConnectionURL = 'mongodb://localhost:27017';
const dbName = 'todo';
const dbClient = new MongoClient(dbConnectionURL);

function findTodos(db, callback) {
    const collection = db.collection('todos');
    collection.find({}).toArray(function (err, todos) {
        assert.equal(err, null);
        console.log("Found the following records");
        console.log(todos)
        callback(todos);
    });
}

app.get('/', (req, res) => {
    dbClient.connect(function (err) {
        assert.equal(null, err);
        console.log("Connected successfully to server");

        const db = dbClient.db(dbName);

        findTodos(db, function(todosArr) {
            var todos = { todos: todosArr }
            res.send(todos)
            dbClient.close()
        });
    });
});

app.listen(port, () => console.log(`Example app listening on port ${port}!`))

Оно в основном копируется из онлайн-учебников.Первый раз я делаю http запрос к 'http://localhost:3000/', это работает.Но во второй раз я получаю

todo-backend/node_modules/mongodb/lib/utils.js:132
    throw err;
    ^

AssertionError [ERR_ASSERTION]: 'MongoError: Topology was destroyed' == null
    at todo-backend/index.js:15:16
    at err (todo-backend/node_modules/mongodb/lib/utils.js:415:14)
    at executeCallback (todo-backend/node_modules/mongodb/lib/utils.js:404:25)
    at handleCallback (todo-backend/node_modules/mongodb/lib/utils.js:128:55)
    at cursor._endSession.cursor._endSession (todo-backend/node_modules/mongodb/lib/operations/cursor_ops.js:207:38)
    at ClientSession.endSession (todo-backend/node_modules/mongodb-core/lib/sessions.js:129:41)
    at Cursor._endSession (todo-backend/node_modules/mongodb-core/lib/cursor.js:189:13)
    at Cursor._endSession (todo-backend/node_modules/mongodb/lib/cursor.js:226:59)
    at cursor._next (todo-backend/node_modules/mongodb/lib/operations/cursor_ops.js:207:20)
    at initializeCursor (todo-backend/node_modules/mongodb-core/lib/cursor.js:766:16)

Я искал ответ в Интернете и обнаружил похожую ошибку, но когда люди делают более сложные вещи.То, что я сделал, кажется более простым, и я не могу найти места для изменений, которые могли бы помочь.

Если я удаляю dbClient.close(), это работает, но затем я вижу в журналах MongoDB, что число соединений постоянно растет.Конечно, я могу ввести флаг и проверить, подключен ли я.Но с этим вопросом я хочу понять причину.

1 Ответ

0 голосов
/ 26 октября 2018

Это потому, что код содержит анти-шаблон: каждый раз, когда приходит новый запрос, он открывает новое соединение с базой данных, а затем закрывает это соединение после отправки ответа.Затем он впоследствии попытался повторно использовать закрытое соединение, отсюда и сообщение об ошибке, которое вы видите во втором запросе.

Вам нужно только один раз подключиться к базе данных на время существования приложения с использованием глобального соединения.затем используйте этот глобальный объект для выполнения операций с базой данных.

Использование этого глобального объекта позволяет драйверу MongoDB правильно создавать пул соединений с базой данных.Этот пул управляется драйвером MongoDB и позволяет избежать дорогостоящего шаблона подключения / переподключения.

Например:

// listen on this port
const port = 3000

// global database client object
var client = null

// listen on the configured port once database connection is established
MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true }, (err, res) => {
  assert.equal(null, err)
  client = res
  app.listen(port, () => console.log(`Example app listening on port ${port}!`))
})

// use the client global object for database operations
app.get('/', (req, res) => {
  db = req.query.db
  col = req.query.col
  client.db(db).collection(col).find({}).toArray((err, docs) => {
    assert.equal(null, err)
    res.send(JSON.stringify(docs))
  })
})

Редактировать , чтобы ответить на ваш вопрос в комментарии:

Почему он пытается повторно использовать предыдущее соединение, когда я подключаюсь каждый раз?

Это потому, что в исходном коде dbClient было определено глобально.Когда был вызван dbClient.close(), глобальный dbClient был закрыт.Затем возникла ошибка, когда этот объект dbClient был повторно использован.Это связано с тем, что connect() создает пул соединений вместо одного соединения, и не ожидалось, что он будет вызываться несколько раз за вызов.

Если переместить переменную dbClient из глобальной области в app.get() context, вы обнаружите, что при многократном вызове конечной точки HTTP ошибка не возникнет, поскольку каждый раз создается новый dbClient объект.

Сказав, что, хотя это будет работать, этоэто не рекомендуемый шаблон.Лучше использовать шаблон, похожий на пример кода, который я выложил выше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...