Python е мощен и гъвкав език за програмиране, използван от милиони разработчици по целия свят за изграждане на своите приложения. Не е изненадващо, че разработчиците на Python обикновено използват MongoDB хостинг, най-популярната NoSQL база данни, за тяхното внедряване поради гъвкавия му характер и липсата на изисквания за схема.
И така, какъв е най-добрият начин да използвате MongoDB с Python? PyMongo е дистрибуция на Python, съдържаща инструменти за работа с MongoDB и препоръчания драйвер на Python MongoDB. Това е доста зрял драйвер, който поддържа повечето от обичайните операции с базата данни.
Когато внедрявате в производствената среда, силно се препоръчва да настроите конфигурация на набор от реплики на MongoDB, така че данните ви да са географски разпределени за висока наличност. Също така се препоръчва SSL връзките да бъдат активирани за криптиране на трафика на клиентска база данни. Често предприемаме тестване на характеристики за отказване на различни драйвери MongoDB, за да ги квалифицираме за производствени случаи на употреба или когато клиентите ни ни помолят за съвет. В тази публикация ви показваме как да се свържете с набор от реплики на MongoDB с активиран SSL, конфигуриран със самоподписани сертификати с помощта на PyMongo, и как да тествате поведението на MongoDB при отказ в кода си.
Свързване към MongoDB SSL с помощта на самоподписани сертификати
Първата стъпка е да се уверите, че правилните версии на PyMongo и неговите зависимости са инсталирани. Това ръководство ви помага да подредите зависимостите, а матрицата за съвместимост на драйвери можете да намерите тук.
mongo_client.MongoClient параметрите, които ни интересуват са ssl и ss_ca_cert . За да се свържете с крайна точка MongoDB с активиран SSL, която използва самоподписан сертификат, ssl трябва да бъде зададено на Вярно и ss_ca_cert трябва да сочи към файла сертификат на CA.
Ако сте клиент на ScaleGrid, можете да изтеглите CA сертификат файла за вашите MongoDB клъстери от конзолата ScaleGrid, както е показано тук:
И така, фрагментът за връзка ще изглежда така:
>>> 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("Бази данни - " + str(client.list_database_names()))Бази данни - ['admin', 'local', 'test']>>> client.close()>>>
Ако използвате свои собствени самоподписани сертификати, където проверката на името на хост може да не успее, ще трябва също да зададете ssl_match_hostname параметър на False . Както се казва в документацията на драйвера, това не се препоръчва, тъй като прави връзката податлива на атаки от човек в средата.
Тестване на поведение при отказ
С внедряванията на MongoDB, отказите не се считат за големи събития, както при традиционните системи за управление на бази данни. Въпреки че повечето драйвери на MongoDB се опитват да абстрахират това събитие, разработчиците трябва да разберат и проектират приложенията си за такова поведение, тъй като приложенията трябва да очакват преходни мрежови грешки и да опитат отново, преди да разпределят грешките.
Можете да тествате устойчивостта на вашите приложения, като предизвиквате откази докато работното ви натоварване работи. Най-лесният начин да предизвикате отказ е да изпълните командата rs.stepDown():
RS-example-0:PRIMARY> rs.stepDown()2019-04-18T19:44:42.257+0530 E QUERY [thread1] Грешка:грешка при извършване на заявка:неуспешна:грешка в мрежата при опит за изпълнение на командата 'replSetStepDown' на хост „SG-example-1.servers.mongodirector.com:27017“ :DB.prototype.runCommand@src/mongo/shell/db.js:168:1DB.prototype.adminCommand@src/mongo/shell/db. js:185:1rs.stepDown@src/mongo/shell/utils.js:1305:12@(shell):1:12019-04-18T19:44:42.261+0530 I NETWORK [thread1] опитвам се да се свържа отново към SG-example -1.servers.mongodirector.com:27017 (X.X.X.X) неуспешно2019-04-18T19:44:43.267+0530 I NETWORK [thread1] reconnect SG-example-1.servers.mongodirector.com:27017 (X.X.ex.) 0:ВТОРИЧНО>
Един от начините, по които обичам да тествам поведението на драйверите, е като напиша просто „вечно“ приложение за писане. Това ще бъде прост код, който продължава да пише в базата данни, освен ако не бъде прекъснат от потребителя, и ще отпечата всички изключения, които среща, за да ни помогне да разберем драйвера и поведението на базата данни. Също така следя данните, които пише, за да гарантирам, че няма недекларирана загуба на данни в теста. Ето съответната част от тестовия код, която ще използваме, за да тестваме поведението на MongoDB при отказ:
import loggingimport 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("Опит за свързване.. .") client =pymongo.MongoClient(MONGO_URI, ssl =True, ssl_ca_certs ='path-to-cacert.pem') db =client['test'] collection =db['test'] i =0 докато True:опитайте :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("Записът е вмъкнат - id:" + str(id)) sleep(3) с изключение на pymongo.errors.ConnectionFailure като e:logger.error("ConnectionFailure видяно:" + str(e)) traceback.print_exc(file =sys.stdout) logger.info("Повторен опит...") logger.info("Готово...")с изключение като e:logger.error("Виждано изключение:" + str(e)) traceback.print_exc(file =sys. stdout) накрая:client.close()
Видът на записи, които това пише, изглежда така:
RS-example-0:PRIMARY> db.test.find(){ "_id" :ObjectId("5cb6d6269ece140f18d05438"), "idx" :0, "дата" :ISODate("2019-04-17T07:3 :46.533Z"), "text" :"400" }{ "_id" :ObjectId("5cb6d6299ece140f18d05439"), "idx" :1, "дата" :ISODate("2019-04-17T07:75:Z4" ), "text" :"X63" }{ "_id" :ObjectId("5cb6d62c9ece140f18d0543a"), "idx" :2, "date" :ISODate("2019-04-17T07:30:52.976Z"), "text" " :"5BX" }{ "_id" :ObjectId("5cb6d6329ece140f18d0543c"), "idx" :4, "дата" :ISODate("2019-04-17T07:30:58.001Z:"), "TGQ" " }{ "_id" :ObjectId("5cb6d63f9ece140f18d0543d"), "idx" :5, "дата" :ISODate("2019-04-17T07:31:11.417Z"), "текст" :"ZWA" }{ " _id" :ObjectId("5cb6d6429ece140f18d0543e"), "idx" :6, "дата" :ISODate("2019-04-17T07:31:14.654Z"), "текст":"WSR>" }..Обработка на изключението ConnectionFailure
Забележете, че улавяме изключението ConnectionFailure, за да се справим с всички проблеми, свързани с мрежата, които може да срещнем поради откази – ние отпечатваме изключението и продължаваме да се опитваме да пишем в базата данни. Документацията на драйвера препоръчва следното:
Ако дадена операция не успее поради грешка в мрежата, ConnectionFailure се повдига и клиентът се свързва отново във фонов режим. Кодът на приложението трябва да се справи с това изключение (признавайки, че операцията е неуспешна) и след това да продължи да се изпълнява.
Нека стартираме това и да извършим отказ на базата данни, докато се изпълнява. Ето какво се случва:
17.04.2019 г. 12:49:17 ч. ИНФО Опит за свързване... 17.04.2019 г. 12:49:20 ч. ИНФО Записът е вмъкнат - id:5cb6d3789ece145a2408cbc704/17/2049 INFO:213. Записът е вмъкнат - идентификатор:5cb6d37b9ece145a2408cbc804/17/2019 12:49:27 PM ИНФОРМАЦИЯ Записът е вмъкнат - идентификатор:5cb6d37e9ece145a2408cbc904/17/2019:en PM. insert_one(doc).inserted_id File "C:\Users\Random\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymongo\collection.py", ред 693, в сесията insert_one session=session), ... Файл "C:\Users\Random\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymongo\network.py", ред 173, в receive_message _receive_data_on_socket(sock, 16)) Файл "C:\Users\Random\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymongo\network.py", ред 238, в _receive_data_on_socket повиши AutoReconnect("connection closed")pymongo.errors. Автоматично повторно свързване:връзката е затворена17.04.201 9 12:49:30 ИНФО. Повторен опит... 17.04.2019 г. 12:49:42 ч. ИНФО Записът е вмъкнат - идентификатор:5cb6d3829ece145a2408cbcb 17.04.2019 г. 12:49:45 ИНФО Записът е вмъкнат - идентификатор:5cb6d3919ece145a2408cbcc04/17.04.2019 г. 12:49:49 ч. ИНФО Записът е вмъкнат - идентификационен номер:5cb6d3949c3949ece140201402000000000000000000000% :5cb6d3989ece145a2408cbceЗабележете, че на драйвера са необходими около 12 секунди, за да разбере новата топология, да се свърже с новата основна и да продължи да пише. Повдигнатото изключение е грешки . Автоматично повторно свързване което е подклас на ConnectionFailure .
Урок за PyMongo:Тестване на отказ на MongoDB във вашето Python AppClick To Tweet
Можете да направите още няколко проби, за да видите какви други изключения се виждат. Например, ето още една следа на изключение, която срещнах:
id =collection.insert_one(doc).inserted_id Файл "C:\Users\Random\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymongo\collection.py", ред 693, в insert_one session=session),... Файл "C:\Users\Randome\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymongo\network.py", ред 150, в команда parse_write_concern_error =parse_write_concern_error) Файл "C:\Users\Random\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymongo\helpers.py", ред 132, в _check_command_response отговор () NoterMastermsErrgo .errors.NotMasterError:не е главенТова изключение също е подклас на ConnectionFailure.
параметър ‘retryWrites’
Друга област за тестване на поведението при отказ на MongoDB би била да се види как други вариации на параметрите влияят на резултатите. Един параметър, който е от значение, е „retryWrites ‘:
retryWrites:(логично) Дали поддържаните операции за запис, изпълнени в рамките на този MongoClient, ще бъдат изпробвани веднъж след мрежова грешка в MongoDB 3.6+. По подразбиране е False.
Нека видим как работи този параметър с отказ. Единствената промяна в кода е:
client =pymongo.MongoClient(MONGO_URI, ssl =True, ssl_ca_certs ='path-to-cacert.pem', retryWrites =True)Нека го стартираме сега и след това направим отказ на системата за база данни:
04/18/2019 20:49:30 PM INFO Опит за свързване...04/18/2019 20:49:35 PM INFO Записът е вмъкнат - id:5cb895869ece146554010c7704/18/20419 INFO:38 Запис вмъкнато - ID:5CB8958A9ECE146554010C7804/18/2019 08:49:41 PM Запис на запис - ID:5CB8958D9ECE146554010C7904/18/2019 08:49:44 PM Запис 16:48 ИНФО Записът е вмъкнат - идентификатор:5cb895939ece146554010c7b <<<Отказ около това време 18.04.2019 г. 20:50:04 ИНФО Записът е вмъкнат - идентификатор:5cb895979ece146554 18.04.2019 г. 20:50:07 ИНФО Записът е вмъкнат - идентификатор:5cb895a79ece146554010c7d04/18.2019 г. 20:50:10 ч. ИНФО Записът е вмъкнат - идентификатор:5cb895a695a610800000000000000000000000000000000000000000000000000000000000000000000000000000000 0 :5cb895ad9ece146554010c7f...Забележете как вмъкването след отказ отнема около 12 секунди, но преминава успешно като retryWrites параметър гарантира повторен опит за неуспешното записване. Не забравяйте, че настройката на този параметър не ви освобождава от боравенето с ConnectionFailure изключение - трябва да се притеснявате за четения и други операции, чието поведение не се влияе от този параметър. Освен това не решава напълно проблема, дори и за поддържани операции – понякога приключването на отказ може да отнеме повече време и записва отново само по себе си няма да е достатъчно.
Конфигуриране на стойностите за изчакване на мрежата
rs.stepDown() предизвиква доста бързо преодоляване на отказ, тъй като първичният набор от реплика е инструктиран да стане вторичен, а вторичните провеждат избори, за да определят новия първичен. При производствените внедрявания натоварването на мрежата, дяловете и други подобни проблеми забавят откриването на недостъпност на основния сървър, като по този начин удължават времето ви за преодоляване на отказ. Също така често ще се сблъскате с грешки в PyMongo като errors.ServerSelectionTimeoutError , errors.NetworkTimeout, и т.н. по време на проблеми с мрежата и при отказ.
Ако това се случва много често, трябва да настроите параметрите за изчакване. Свързаният MongoClient параметрите за изчакване са serverSelectionTimeoutMS , connectTimeoutMS, и socketTimeoutMS . От тях избор на по-голяма стойност за serverSelectionTimeoutMS най-често помага при справяне с грешки по време на отказ:
serverSelectionTimeoutMS:(цяло число) Контролира колко време (в милисекунди) драйверът ще чака, за да намери наличен подходящ сървър за извършване на операция с база данни; докато чака, могат да се извършат множество операции за наблюдение на сървъра, всяка от които се контролира от connectTimeoutMS. По подразбиране е 30 000 (30 секунди).
Готови ли сте да използвате MongoDB в приложението си Python? Вижте нашата статия Първи стъпки с Python и MongoDB, за да видите как можете да започнете работа само с 5 лесни стъпки. ScaleGrid е единственият доставчик на MongoDB DBaaS, който ви дава пълен SSH достъп до вашите екземпляри, за да можете да стартирате своя Python сървър на същата машина като вашия сървър MongoDB. Автоматизирайте облачните си внедрявания на MongoDB в AWS, Azure или DigitalOcean със специални сървъри, висока наличност и възстановяване след бедствие, за да можете да се съсредоточите върху разработването на вашето Python приложение.