sql >> Database >  >> NoSQL >> MongoDB

PyMongo-zelfstudie:MongoDB-failover testen in uw Python-app

Python is een krachtige en flexibele programmeertaal die door miljoenen ontwikkelaars over de hele wereld wordt gebruikt om hun apps te bouwen. Het is geen verrassing dat Python-ontwikkelaars vaak gebruikmaken van MongoDB-hosting, de meest populaire NoSQL-database, voor hun implementaties vanwege het flexibele karakter en het ontbreken van schemavereisten.

Dus, wat is de beste manier om MongoDB met Python te gebruiken? PyMongo is een Python-distributie met tools voor het werken met MongoDB en de aanbevolen Python MongoDB-driver. Het is een redelijk volwassen stuurprogramma dat de meeste gebruikelijke bewerkingen met de database ondersteunt.

Bij implementatie in productie wordt het ten zeerste aanbevolen om in te stellen in een MongoDB-replicasetconfiguratie, zodat uw gegevens geografisch gedistribueerd zijn voor hoge beschikbaarheid. Het wordt ook aanbevolen om SSL-verbindingen in te schakelen om het client-databaseverkeer te versleutelen. We testen vaak de failover-kenmerken van verschillende MongoDB-stuurprogramma's om ze te kwalificeren voor gebruik in productie, of wanneer onze klanten ons om advies vragen. In dit bericht laten we u zien hoe u verbinding kunt maken met een MongoDB-replicaset met SSL-functionaliteit die is geconfigureerd met zelfondertekende certificaten met behulp van PyMongo, en hoe u MongoDB-failovergedrag in uw code kunt testen.

Verbinding maken met MongoDB SSL met behulp van zelfondertekende certificaten

De eerste stap is ervoor te zorgen dat de juiste versies van PyMongo en zijn afhankelijkheden zijn geïnstalleerd. Deze handleiding helpt u bij het uitzoeken van de afhankelijkheden en de compatibiliteitsmatrix voor stuurprogramma's vindt u hier.

De mongo_client.MongoClient parameters die voor ons van belang zijn, zijn ssl en ss_ca_cert . Om verbinding te maken met een SSL-enabled MongoDB-eindpunt dat gebruikmaakt van een zelfondertekend certificaat, ssl moet zijn ingesteld op True en ss_ca_cert moet verwijzen naar het CA-certificaatbestand.

Als u een ScaleGrid-klant bent, kunt u het CA-certificaatbestand voor uw MongoDB-clusters downloaden van de ScaleGrid-console, zoals hier wordt weergegeven:

Een verbindingsfragment ziet er dus als volgt uit:

>>> import pymongo
>>> MONGO_URI = 'mongodb://rwuser:@SG-example-0.servers.mongodirector.com:27017,SG-example-1.servers.mongodirector.com:27017,SG-example-2.servers.mongodirector.com:27017/admin?replicaSet=RS-example&ssl=true'
>>> client = pymongo.MongoClient(MONGO_URI, ssl = True, ssl_ca_certs = '')
>>> print("Databases - " + str(client.list_database_names()))
Databases - ['admin', 'local', 'test']
>>> client.close()
>>>

Als je je eigen zelfondertekende certificaten gebruikt waarbij de verificatie van de hostnaam kan mislukken, moet je ook de ssl_match_hostname parameter naar False . Zoals in de driverdocumentatie staat, wordt dit niet aanbevolen omdat het de verbinding vatbaar maakt voor man-in-the-middle-aanvallen.

Failover-gedrag testen

Bij MongoDB-implementaties worden failovers niet als grote gebeurtenissen beschouwd, zoals bij traditionele databasebeheersystemen. Hoewel de meeste MongoDB-stuurprogramma's deze gebeurtenis proberen te abstraheren, moeten ontwikkelaars hun applicaties voor dergelijk gedrag begrijpen en ontwerpen, aangezien applicaties tijdelijke netwerkfouten moeten verwachten en het opnieuw moeten proberen voordat fouten groter worden.

U kunt de veerkracht van uw apps testen door failovers teweeg te brengen terwijl uw werklast wordt uitgevoerd. De eenvoudigste manier om een ​​failover te induceren, is door de opdracht rs.stepDown() uit te voeren:

RS-example-0:PRIMARY> rs.stepDown()
2019-04-18T19:44:42.257+0530 E QUERY [thread1] Error: error doing query: failed: network error while attempting to run command 'replSetStepDown' on host 'SG-example-1.servers.mongodirector.com:27017' :
DB.prototype.runCommand@src/mongo/shell/db.js:168:1
DB.prototype.adminCommand@src/mongo/shell/db.js:185:1
rs.stepDown@src/mongo/shell/utils.js:1305:12
@(shell):1:1
2019-04-18T19:44:42.261+0530 I NETWORK [thread1] trying reconnect to SG-example-1.servers.mongodirector.com:27017 (X.X.X.X) failed
2019-04-18T19:44:43.267+0530 I NETWORK [thread1] reconnect SG-example-1.servers.mongodirector.com:27017 (X.X.X.X) ok
RS-example-0:SECONDARY>

Een van de manieren waarop ik het gedrag van stuurprogramma's graag test, is door een eenvoudige 'perpetual' writer-app te schrijven. Dit zou een eenvoudige code zijn die naar de database blijft schrijven, tenzij onderbroken door de gebruiker, en die alle uitzonderingen afdrukt die het tegenkomt om ons te helpen het stuurprogramma en het databasegedrag te begrijpen. Ik houd ook bij welke gegevens het schrijft om ervoor te zorgen dat er tijdens de test geen ongemeld gegevensverlies optreedt. Dit is het relevante deel van de testcode die we zullen gebruiken om ons MongoDB-failovergedrag te testen:

import logging
import traceback
...
import pymongo
...
logger = logging.getLogger("test")

MONGO_URI = 'mongodb://rwuser:@SG-example-0.servers.mongodirector.com:48273,SG-example-1.servers.mongodirector.com:27017,SG-example-2.servers.mongodirector.com:27017/admin?replicaSet=RS-example-0&ssl=true'

try:
    logger.info("Attempting to connect...")
    client = pymongo.MongoClient(MONGO_URI, ssl = True, ssl_ca_certs = 'path-to-cacert.pem')
    db = client['test']
    collection = db['test']
    i = 0
    while True:
        try:
            text = ''.join(random.choices(string.ascii_uppercase + string.digits, k = 3))
            doc = { "idx": i, "date" : datetime.utcnow(), "text" : text}
            i += 1
            id = collection.insert_one(doc).inserted_id
            logger.info("Record inserted - id: " + str(id))
            sleep(3)
        except pymongo.errors.ConnectionFailure as e:
            logger.error("ConnectionFailure seen: " + str(e))
            traceback.print_exc(file = sys.stdout)
            logger.info("Retrying...")

    logger.info("Done...")
except Exception as e:
    logger.error("Exception seen: " + str(e))
    traceback.print_exc(file = sys.stdout)
finally:
    client.close()

Het soort items dat dit schrijft, ziet er als volgt uit:

RS-example-0:PRIMARY> db.test.find()
{ "_id" : ObjectId("5cb6d6269ece140f18d05438"), "idx" : 0, "date" : ISODate("2019-04-17T07:30:46.533Z"), "text" : "400" }
{ "_id" : ObjectId("5cb6d6299ece140f18d05439"), "idx" : 1, "date" : ISODate("2019-04-17T07:30:49.755Z"), "text" : "X63" }
{ "_id" : ObjectId("5cb6d62c9ece140f18d0543a"), "idx" : 2, "date" : ISODate("2019-04-17T07:30:52.976Z"), "text" : "5BX" }
{ "_id" : ObjectId("5cb6d6329ece140f18d0543c"), "idx" : 4, "date" : ISODate("2019-04-17T07:30:58.001Z"), "text" : "TGQ" }
{ "_id" : ObjectId("5cb6d63f9ece140f18d0543d"), "idx" : 5, "date" : ISODate("2019-04-17T07:31:11.417Z"), "text" : "ZWA" }
{ "_id" : ObjectId("5cb6d6429ece140f18d0543e"), "idx" : 6, "date" : ISODate("2019-04-17T07:31:14.654Z"), "text" : "WSR" }
..

De ConnectionFailure-uitzondering afhandelen

Merk op dat we de ConnectionFailure-uitzondering opvangen om alle netwerkgerelateerde problemen op te lossen die we kunnen tegenkomen als gevolg van failovers - we drukken de uitzondering af en blijven proberen naar de database te schrijven. De driverdocumentatie beveelt het volgende aan:

Als een bewerking mislukt vanwege een netwerkfout, wordt ConnectionFailure geactiveerd en maakt de client opnieuw verbinding op de achtergrond. Applicatiecode moet deze uitzondering afhandelen (erkennend dat de bewerking is mislukt) en vervolgens doorgaan met uitvoeren.

Laten we dit uitvoeren en een database-failover doen terwijl het wordt uitgevoerd. Dit is wat er gebeurt:

04/17/2019 12:49:17 PM INFO Attempting to connect...
04/17/2019 12:49:20 PM INFO Record inserted - id: 5cb6d3789ece145a2408cbc7
04/17/2019 12:49:23 PM INFO Record inserted - id: 5cb6d37b9ece145a2408cbc8
04/17/2019 12:49:27 PM INFO Record inserted - id: 5cb6d37e9ece145a2408cbc9
04/17/2019 12:49:30 PM ERROR PyMongoError seen: connection closed
Traceback (most recent call last):
    id = collection.insert_one(doc).inserted_id
  File "C:\Users\Random\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymongo\collection.py", line 693, in insert_one
    session=session),
...
  File "C:\Users\Random\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymongo\network.py", line 173, in receive_message
    _receive_data_on_socket(sock, 16))
  File "C:\Users\Random\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymongo\network.py", line 238, in _receive_data_on_socket
    raise AutoReconnect("connection closed")
pymongo.errors.AutoReconnect: connection closed
04/17/2019 12:49:30 PM INFO Retrying...
04/17/2019 12:49:42 PM INFO Record inserted - id: 5cb6d3829ece145a2408cbcb
04/17/2019 12:49:45 PM INFO Record inserted - id: 5cb6d3919ece145a2408cbcc
04/17/2019 12:49:49 PM INFO Record inserted - id: 5cb6d3949ece145a2408cbcd
04/17/2019 12:49:52 PM INFO Record inserted - id: 5cb6d3989ece145a2408cbce

Merk op dat het stuurprogramma ongeveer 12 seconden nodig heeft om de nieuwe topologie te begrijpen, verbinding te maken met de nieuwe primaire en door te gaan met schrijven. De opgeworpen uitzondering is fouten . Automatisch opnieuw verbinden wat een subklasse is van ConnectionFailure .

PyMongo-zelfstudie:MongoDB-failover testen in uw Python-appKlik om te tweeten

Je zou nog een paar runs kunnen doen om te zien welke andere uitzonderingen er zijn. Hier is bijvoorbeeld nog een uitzonderingsspoor dat ik tegenkwam:

    id = collection.insert_one(doc).inserted_id
  File "C:\Users\Random\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymongo\collection.py", line 693, in insert_one
    session=session),
...
  File "C:\Users\Randome\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymongo\network.py", line 150, in command
    parse_write_concern_error=parse_write_concern_error)
  File "C:\Users\Random\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymongo\helpers.py", line 132, in _check_command_response
    raise NotMasterError(errmsg, response)
pymongo.errors.NotMasterError: not master

Deze uitzondering is ook een subklasse van ConnectionFailure.

'retryWrites'-parameter

Een ander gebied om het MongoDB-failovergedrag te testen, is kijken hoe andere parametervariaties de resultaten beïnvloeden. Een parameter die relevant is, is 'retryWrites ‘:

retryWrites:(boolean) Of ondersteunde schrijfbewerkingen die binnen deze MongoClient worden uitgevoerd, één keer opnieuw worden geprobeerd na een netwerkfout op MongoDB 3.6+. Standaard ingesteld op False.

Laten we eens kijken hoe deze parameter werkt met een failover. De enige wijziging in de code is:

client = pymongo.MongoClient(MONGO_URI, ssl = True, ssl_ca_certs = 'path-to-cacert.pem', retryWrites = True)

Laten we het nu uitvoeren en dan een databasesysteemfailover uitvoeren:

04/18/2019 08:49:30 PM INFO Attempting to connect...
04/18/2019 08:49:35 PM INFO Record inserted - id: 5cb895869ece146554010c77
04/18/2019 08:49:38 PM INFO Record inserted - id: 5cb8958a9ece146554010c78
04/18/2019 08:49:41 PM INFO Record inserted - id: 5cb8958d9ece146554010c79
04/18/2019 08:49:44 PM INFO Record inserted - id: 5cb895909ece146554010c7a
04/18/2019 08:49:48 PM INFO Record inserted - id: 5cb895939ece146554010c7b <<< Failover around this time
04/18/2019 08:50:04 PM INFO Record inserted - id: 5cb895979ece146554010c7c
04/18/2019 08:50:07 PM INFO Record inserted - id: 5cb895a79ece146554010c7d
04/18/2019 08:50:10 PM INFO Record inserted - id: 5cb895aa9ece146554010c7e
04/18/2019 08:50:14 PM INFO Record inserted - id: 5cb895ad9ece146554010c7f
...

Zie hoe het invoegen na de failover ongeveer 12 seconden duurt, maar met succes doorgaat als de retryWrites parameter zorgt ervoor dat de mislukte schrijfactie opnieuw wordt geprobeerd. Onthoud dat het instellen van deze parameter u niet ontslaat van het afhandelen van de ConnectionFailure uitzondering:u moet zich zorgen maken over leesbewerkingen en andere bewerkingen waarvan het gedrag niet wordt beïnvloed door deze parameter. Het lost het probleem ook niet volledig op, zelfs niet voor ondersteunde bewerkingen - soms kan het langer duren voordat failovers zijn voltooid en retryWrites alleen is niet genoeg.

De netwerktime-outwaarden configureren

rs.stepDown() induceert een vrij snelle failover, aangezien de replicaset-primair wordt geïnstrueerd om een ​​secundaire te worden, en de secundairen een verkiezing houden om de nieuwe primaire te bepalen. Bij productie-implementaties vertragen netwerkbelasting, partitie en andere soortgelijke problemen de detectie van onbeschikbaarheid van de primaire server, waardoor uw failover-tijd wordt verlengd. U zou ook vaak PyMongo-fouten tegenkomen, zoals errors.ServerSelectionTimeoutError , fouten.NetworkTimeout, enz. tijdens netwerkproblemen en failovers.

Als dit heel vaak voorkomt, moet je kijken naar het aanpassen van de time-outparameters. De gerelateerde MongoClient time-outparameters zijn serverSelectionTimeoutMS , connectTimeoutMS, en socketTimeoutMS . Hiervan selecteert u een grotere waarde voor serverSelectionTimeoutMS helpt meestal bij het omgaan met fouten tijdens failovers:

serverSelectionTimeoutMS:(integer) Bepaalt hoe lang (in milliseconden) het stuurprogramma zal wachten om een ​​beschikbare, geschikte server te vinden om een ​​databasebewerking uit te voeren; terwijl het wacht, kunnen meerdere serverbewakingsbewerkingen worden uitgevoerd, elk gecontroleerd door connectTimeoutMS. Standaard ingesteld op 30000 (30 seconden).

Klaar om MongoDB te gebruiken in je Python-app? Bekijk ons ​​artikel Aan de slag met Python en MongoDB om te zien hoe u in slechts vijf eenvoudige stappen aan de slag kunt. ScaleGrid is de enige MongoDB DBaaS-provider die u volledige SSH-toegang tot uw instanties geeft, zodat u uw Python-server op dezelfde machine kunt uitvoeren als uw MongoDB-server. Automatiseer uw MongoDB-cloudimplementaties op AWS, Azure of DigitalOcean met speciale servers, hoge beschikbaarheid en noodherstel, zodat u zich kunt concentreren op het ontwikkelen van uw Python-toepassing.


  1. Redis Enterprise Clustering-opdrachtfout 'CLUSTER'

  2. Spring Boot en hoe verbindingsdetails met MongoDB te configureren?

  3. 3 manieren om een ​​index te maken in MongoDB

  4. $ slice-operator gebruiken om het laatste element van de array te krijgen