Как проверить, действителен ли клиент для экземпляра MongoDB?

29

В частности, в настоящее время я пытаюсь проверить правильность подключения к клиенту, используя следующую функцию:

def mongodb_connect(client_uri):
    try:
        return pymongo.MongoClient(client_uri)
    except pymongo.errors.ConnectionFailure:
         print "Failed to connect to server {}".format(client_uri)

Затем я использую эту функцию следующим образом:

def bucket_summary(self):
    client_uri = "some_client_uri"
    client = mongodb_connect(client_uri)
    db = client[tenant_id]
    ttb = db.timebucket.count() # If I use an invalid URI it hangs here

Есть ли способ перехватить и выбросить исключение в последней строке, если указан неверный URI? Сначала я подумал, что именно для этого предназначен ConnectionFailure (так что это может быть обнаружено при подключении), но я ошибся.

Если я запускаю программу с недопустимым URI, который не запускается, выдает KeyboardInterrupt:

File "reportjob_status.py", line 58, in <module>
tester.summarize_timebuckets()
File "reportjob_status.py", line 43, in summarize_timebuckets
ttb = db.timebucket.count() #error
File "/Library/Python/2.7/site-packages/pymongo/collection.py", line   1023, in count
return self._count(cmd)
File "/Library/Python/2.7/site-packages/pymongo/collection.py", line 985, in _count
with self._socket_for_reads() as (sock_info, slave_ok):
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/contextlib.py", line 17, in __enter__
return self.gen.next()
File "/Library/Python/2.7/site-packages/pymongo/mongo_client.py", line 699, in _socket_for_reads
with self._get_socket(read_preference) as sock_info:
File  "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/contextlib.py", line 17, in __enter__
return self.gen.next()
File "/Library/Python/2.7/site-packages/pymongo/mongo_client.py", line 663, in _get_socket
server = self._get_topology().select_server(selector)
File "/Library/Python/2.7/site-packages/pymongo/topology.py", line 121, in select_server
address))
File "/Library/Python/2.7/site-packages/pymongo/topology.py", line 106, in select_servers
self._condition.wait(common.MIN_HEARTBEAT_INTERVAL)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py", line 358, in wait
_sleep(delay)
    
задан Leeren 29.05.2015 в 23:15
источник
  • pymongo.errors.ConnectionFailure вызывается, когда хост из client_uri недоступен, но если база данных не существует на хосте, она создана, поэтому вам нужно будет проверить существование db раньше. –  imarban 29.05.2015 в 23:32
  • Спасибо. Как вы проверяете существование db заранее? –  Leeren 29.05.2015 в 23:34
  • Метод database_names в классе MongoClient может помочь. –  imarban 29.05.2015 в 23:37

4 ответа

41

Параметр ключевого слова serverSelectionTimeoutMS в pymongo.mongo_client.MongoClient контролирует, как долго драйвер будет пытаться подключиться к серверу. Значение по умолчанию составляет 30 с.

Установите очень низкое значение, совместимое с вашим типичным временем соединения¹, чтобы немедленно сообщить об ошибке. После этого вам нужно запросить БД, чтобы инициировать попытку подключения:

>>> maxSevSelDelay = 1 # Assume 1ms maximum server selection delay
>>> client = pymongo.MongoClient("someInvalidURIOrNonExistantHost",
                                 serverSelectionTimeoutMS=maxSevSelDelay)
//                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>>> client.server_info()

Это повысит pymongo.errors.ServerSelectionTimeoutError .

¹ Видимо установка serverSelectionTimeoutMS в 0 может даже работать в конкретном случае, когда у вашего сервера очень низкая задержка (например, в случае «локального» сервера с очень небольшой нагрузкой) )

Вы должны поймать это исключение и правильно его обработать. Нечто подобное так:

try:
    client = pymongo.MongoClient("someInvalidURIOrNonExistantHost",
                                     serverSelectionTimeoutMS=maxSevSelDelay)
    client.server_info() # force connection on a request as the
                         # connect=True parameter of MongoClient seems
                         # to be useless here 
except pymongo.errors.ServerSelectionTimeoutError as err:
    # do whatever you need
    print(err)

отобразит:

No servers found yet
    
ответ дан Sylvain Leroux 29.05.2015 в 23:31
  • Это хорошо работает. У меня возникла проблема: если для serverSelectionTimeoutMS установлено значение 0, всегда будет указано, что экземпляр MongoDB не работает. Установка этого параметра на 1 мс исправила эту проблему. –  James Milner 21.02.2016 в 19:40
  • Спасибо, что указали, что @James! Я соответствующим образом обновил ответ. Вы используете кластер серверов MongoDB или один сервер? –  Sylvain Leroux 23.02.2016 в 11:29
  • @SylvainLeroux Я просто использую один сервер :) –  James Milner 23.02.2016 в 18:09
4

Привет, чтобы узнать, установлено соединение или нет, вы можете сделать это:

from pymongo import MongoClient
from pymongo.errors import ConnectionFailure
client = MongoClient()
try:
   # The ismaster command is cheap and does not require auth.
   client.admin.command('ismaster')
except ConnectionFailure:
   print("Server not available")
    
ответ дан Manochehr Rasouli 02.08.2017 в 13:21
1

serverSelectionTimeoutMS не работает для меня (Python 2.7.12, MongoDB 3.6.1, pymongo 3.6.0). A. Джесси Джирью Дэвис предложил в проблему GitHub , чтобы мы попытались установить соединение на уровне сокета сначала как лакмусовая бумажка. Это помогает мне.

def throw_if_mongodb_is_unavailable(host, port):
    import socket
    sock = None
    try:
        sock = socket.create_connection(
            (host, port),
            timeout=1) # one second
    except socket.error as err:
        raise EnvironmentError(
            "Can't connect to MongoDB at {host}:{port} because: {err}"
            .format(**locals()))
    finally:
        if sock is not None:
            sock.close()

# elsewhere...
HOST = 'localhost'
PORT = 27017
throw_if_mongodb_is_unavailable(HOST, PORT)
import pymongo
conn = pymongo.MongoClient(HOST, PORT)
print(conn.admin.command('ismaster'))
# etc.

Существует множество проблем, которые не будут обнаружены, но если сервер не работает или недоступен, это сразу же будет показано.

    
ответ дан ESV 12.01.2018 в 23:02
0

serverSelectionTimeoutMS

This defines how long to block for server selection before throwing an exception. The default is 30,000 (milliseconds). It MUST be configurable at the client level. It MUST NOT be configurable at the level of a database object, collection object, or at the level of an individual query.

This default value was chosen to be sufficient for a typical server primary election to complete. As the server improves the speed of elections, this number may be revised downward.

Users that can tolerate long delays for server selection when the topology is in flux can set this higher. Users that want to "fail fast" when the topology is in flux can set this to a small number.

A serverSelectionTimeoutMS of zero MAY have special meaning in some drivers; zero's meaning is not defined in this spec, but all drivers SHOULD document the meaning of zero.

Ссылка

# pymongo 3.5.1
from pymongo import MongoClient
from pymongo.errors import ServerSelectionTimeoutError

client = MongoClient("mongodb://localhost:27000/", serverSelectionTimeoutMS=10, connectTimeoutMS=20000)

try:
    info = client.server_info() # Forces a call.
except ServerSelectionTimeoutError:
    print("server is down.")

# If connection create a new one with serverSelectionTimeoutMS=30000
    
ответ дан The Demz 12.10.2017 в 21:32