sql >> Database teknologi >  >> NoSQL >> MongoDB

PyMongo-tutorial:Test af MongoDB-failover i din Python-app

Python er et kraftfuldt og fleksibelt programmeringssprog, der bruges af millioner af udviklere rundt om i verden til at bygge deres applikationer. Det kommer ikke som nogen overraskelse, at Python-udviklere almindeligvis udnytter MongoDB-hosting, den mest populære NoSQL-database, til deres implementeringer på grund af dens fleksible karakter og mangel på skemakrav.

Så hvad er den bedste måde at bruge MongoDB med Python? PyMongo er en Python-distribution, der indeholder værktøjer til at arbejde med MongoDB og den anbefalede Python MongoDB-driver. Det er en ret moden driver, der understøtter de fleste almindelige operationer med databasen.

Når du implementerer i produktion, anbefales det stærkt at opsætte i en MongoDB-repliksætkonfiguration så dine data er geografisk fordelt for høj tilgængelighed. Det anbefales også, at SSL-forbindelser aktiveres for at kryptere klientdatabasetrafikken. Vi udfører ofte test af failover-karakteristika for forskellige MongoDB-drivere for at kvalificere dem til produktionsbrug, eller når vores kunder spørger os til råds. I dette indlæg viser vi dig, hvordan du opretter forbindelse til et SSL-aktiveret MongoDB-repliksæt, der er konfigureret med selvsignerede certifikater ved hjælp af PyMongo, og hvordan du tester MongoDB failover-adfærd i din kode.

Opret forbindelse til MongoDB SSL ved hjælp af selvsignerede certifikater

Det første skridt er at sikre, at de rigtige versioner af PyMongo og dets afhængigheder er installeret. Denne vejledning hjælper dig med at sortere afhængighederne, og driverkompatibilitetsmatrixen kan findes her.

mongo_client.MongoClient parametre, der er af interesse for os, er ssl og ss_ca_cert . For at oprette forbindelse til et SSL-aktiveret MongoDB-slutpunkt, der bruger et selvsigneret certifikat, ssl skal indstilles til True og ss_ca_cert skal pege på CA-certifikatfilen.

Hvis du er en ScaleGrid-kunde, kan du downloade CA-certifikatfilen til dine MongoDB-klynger fra ScaleGrid-konsollen som vist her:

Så et forbindelsesuddrag ville se sådan ud:

>>> 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()
>>>

Hvis du bruger dine egne selvsignerede certifikater, hvor verifikation af værtsnavn muligvis mislykkes, skal du også indstille ssl_match_hostname parameter til False . Som driverdokumentationen siger, anbefales dette ikke, da det gør forbindelsen modtagelig for man-in-the-middle-angreb.

Test failover-adfærd

Med MongoDB-implementeringer betragtes failovers ikke som store begivenheder, som de var med traditionelle databasestyringssystemer. Selvom de fleste MongoDB-drivere forsøger at abstrahere denne hændelse, bør udviklere forstå og designe deres applikationer til en sådan adfærd, da applikationer bør forvente forbigående netværksfejl og prøve igen, før de trænger fejl op.

Du kan teste dine applikationers modstandsdygtighed ved at inducere failovers, mens din arbejdsbyrde kører. Den nemmeste måde at fremkalde failover på er at køre kommandoen rs.stepDown():

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>

En af de måder, jeg kan lide at teste chaufførers adfærd på, er ved at skrive en simpel 'evig' forfatter-app. Dette ville være simpel kode, der bliver ved med at skrive til databasen, medmindre den bliver afbrudt af brugeren, og som vil udskrive alle undtagelser, den støder på, for at hjælpe os med at forstå driveren og databasens adfærd. Jeg holder også styr på de data, den skriver for at sikre, at der ikke er noget urapporteret datatab i testen. Her er den relevante del af testkoden, vi vil bruge til at teste vores MongoDB failover-adfærd:

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()

Den slags poster, som dette skriver, ser ud som:

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" }
..

Håndtering af forbindelsesfejl-undtagelsen

Bemærk, at vi fanger ConnectionFailure-undtagelsen for at håndtere alle netværksrelaterede problemer, vi kan støde på på grund af failovers – vi udskriver undtagelsen og fortsætter med at forsøge at skrive til databasen. Driverdokumentationen anbefaler, at:

Hvis en handling mislykkes på grund af en netværksfejl, hæves ConnectionFailure, og klienten genopretter forbindelse i baggrunden. Applikationskoden skal håndtere denne undtagelse (i erkendelse af, at handlingen mislykkedes) og derefter fortsætte med at udføre.

Lad os køre dette og lave en database-failover, mens den udføres. Her er, hvad der sker:

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

Bemærk, at driveren tager omkring 12 sekunder på at forstå den nye topologi, oprette forbindelse til den nye primære og fortsætte med at skrive. Undtagelsen er fejl . Autogenopret forbindelse som er en underklasse af ConnectionFailure .

PyMongo-tutorial:Test af MongoDB-failover i din Python-appKlik for at tweete

Du kan køre et par flere kørsler for at se, hvilke andre undtagelser der ses. For eksempel, her er et andet undtagelsesspor, jeg stødte på:

    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

Denne undtagelse er også en underklasse af ConnectionFailure.

'retryWrites'-parameter

Et andet område at teste MongoDB failover-adfærd ville være at se, hvordan andre parametervariationer påvirker resultaterne. En parameter, der er relevant, er 'retryWrites ‘:

retryWrites:(boolean) Hvorvidt understøttede skriveoperationer, der udføres i denne MongoClient, vil blive gentaget én gang efter en netværksfejl på MongoDB 3.6+. Standard er Falsk.

Lad os se, hvordan denne parameter fungerer med en failover. Den eneste ændring af koden er:

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

Lad os køre det nu, og derefter lave en databasesystem-failover:

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
...

Bemærk, hvordan indsættelsen efter failover tager omkring 12 sekunder, men går igennem med succes, mens genforsøg parameter sikrer, at den mislykkede skrivning forsøges igen. Husk, at indstilling af denne parameter ikke fritager dig fra at håndtere Forbindelsesfejl undtagelse – du skal bekymre dig om læsninger og andre handlinger, hvis adfærd ikke påvirkes af denne parameter. Det løser heller ikke helt problemet, selv for understøttede operationer – nogle gange kan failovers tage længere tid at fuldføre og genforsøg alene vil ikke være nok.

Konfiguration af netværkstimeoutværdierne

rs.stepDown() inducerer en ret hurtig failover, da replikasættet primært instrueres til at blive et sekundært, og sekundærerne afholder et valg for at bestemme det nye primærvalg. I produktionsimplementeringer forsinker netværksbelastning, partitioner og andre sådanne problemer registreringen af ​​utilgængelighed af den primære server, hvilket forlænger din failover-tid. Du vil også ofte støde på PyMongo-fejl som errors.ServerSelectionTimeoutError , errors.NetworkTimeout, osv. under netværksproblemer og failovers.

Hvis dette sker meget ofte, skal du prøve at justere timeout-parametrene. Den relaterede MongoClient timeout-parametre er serverSelectionTimeoutMS , connectTimeoutMS, og socketTimeoutMS . Af disse vælges en større værdi for serverSelectionTimeoutMS hjælper oftest med at håndtere fejl under failovers:

serverSelectionTimeoutMS:(heltal) Styrer hvor længe (i millisekunder) driveren vil vente på at finde en tilgængelig, passende server til at udføre en databaseoperation; mens den venter, kan der udføres flere serverovervågningsoperationer, hver styret af connectTimeoutMS. Standard er 30000 (30 sekunder).

Klar til at bruge MongoDB i din Python-applikation? Se vores Kom godt i gang med Python og MongoDB-artikel for at se, hvordan du kan komme i gang med kun 5 nemme trin. ScaleGrid er den eneste MongoDB DBaaS-udbyder, som giver dig fuld SSH-adgang til dine forekomster, så du kan køre din Python-server på den samme maskine som din MongoDB-server. Automatiser dine MongoDB cloud-implementeringer på AWS, Azure eller DigitalOcean med dedikerede servere, høj tilgængelighed og katastrofegendannelse, så du kan fokusere på at udvikle din Python-applikation.


  1. Redis adgang fra MS Excel?

  2. MongoError:tilslut ECONNREFUSED 127.0.0.1:27017

  3. Hvad er forskellen mellem Spring Data MongoDB og Hibernate OGM for MongoDB?

  4. Næste stop – Opbygning af en datapipeline fra Edge til Insight