{"id":376933,"date":"2024-05-24T09:00:20","date_gmt":"2024-05-24T09:00:20","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=376933"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=376933","title":{"rendered":"<span>\u0424\u043e\u043d\u043e\u0432\u044b\u0435 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 FastAPI \u0438 \u0438\u0445 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/313\/a09\/c6c\/313a09c6cb3c513aae5119ab8494fe1b.jpeg\" width=\"1600\" height=\"898\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/313\/a09\/c6c\/313a09c6cb3c513aae5119ab8494fe1b.jpeg\" data-blurred=\"true\"\/><\/figure>\n<p>\u041f\u0440\u0438\u0432\u0435\u0442! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0418\u0432\u0430\u043d, \u044f \u0431\u044d\u043a\u0435\u043d\u0434-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432 <a href=\"https:\/\/kts.tech\">KTS.<\/a><\/p>\n<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u0432 FastAPI \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0444\u043e\u043d\u043e\u0432\u044b\u043c\u0438 \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0438\u0445 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433 \u0432 Prometheus.<\/p>\n<p>\u0412 \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0430\u0445 \u0434\u043b\u044f \u0444\u043e\u043d\u043e\u0432\u044b\u0445 \u0437\u0430\u0434\u0430\u0447 \u0432 FastAPI \u043e\u0431\u044b\u0447\u043d\u043e \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e\u0442 celery \u0438 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 <u>\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439<\/u> \u043a\u043e\u0434. \u041d\u043e \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0439 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u0442\u0430\u043a\u043e\u0435 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0435\u0442\u0441\u044f \u0440\u0435\u0434\u043a\u043e, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u043e\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u0432 \u0444\u043e\u043d\u043e\u0432\u044b\u0445 \u0437\u0430\u0434\u0430\u0447\u0430\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <u>\u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439<\/u> \u043a\u043e\u0434.<\/p>\n<p>\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u043e\u043f\u0438\u0448\u0443 5 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432: \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 \u0432 FastAPI Background Tasks \u0438 4 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u2014 ARQ, SAQ, FastStream, \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043a \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u043c\u0443 \u043a\u043e\u0434\u0443 Celery. <\/p>\n<p>\u0412 \u043a\u043e\u043d\u0446\u0435 \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u0442\u044c \u0444\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438.<\/p>\n<p><strong>\u041e\u0433\u043b\u0430\u0432\u043b\u0435\u043d\u0438\u0435<\/strong><\/p>\n<ul>\n<li>\n<p><a href=\"#1\">\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 \u0444\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#2\">\u041f\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#3\">\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#4\">FasAPI Background Tasks<\/a><\/p>\n<ul>\n<li>\n<p><a href=\"#5\">\u041f\u043b\u044e\u0441\u044b<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#6\">\u041c\u0438\u043d\u0443\u0441\u044b<\/a><\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><a href=\"#7\">ARQ<\/a><\/p>\n<ul>\n<li>\n<p><a href=\"#8\">\u041f\u043b\u044e\u0441\u044b<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#9\">\u041c\u0438\u043d\u0443\u0441\u044b<\/a><\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><a href=\"#10\">SAQ<\/a><\/p>\n<ul>\n<li>\n<p><a href=\"#11\">\u041f\u043b\u044e\u0441\u044b<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#12\">\u041c\u0438\u043d\u0443\u0441\u044b<\/a><\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><a href=\"#13\">FastStream<\/a><\/p>\n<ul>\n<li>\n<p><a href=\"#14\">\u041f\u043b\u044e\u0441\u044b<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#15\">\u041c\u0438\u043d\u0443\u0441\u044b<\/a><\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><a href=\"#16\">Celery<\/a><\/p>\n<ul>\n<li>\n<p><a href=\"#17\">\u041f\u043b\u044e\u0441\u044b<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#18\">\u041c\u0438\u043d\u0443\u0441\u044b<\/a><\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><a href=\"#19\">\u041a\u0430\u043a\u0438\u0435 \u0435\u0449\u0451 \u0435\u0441\u0442\u044c \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0444\u043e\u043d\u043e\u0432\u044b\u043c\u0438 \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#20\">\u041c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433 \u0444\u043e\u043d\u043e\u0432\u044b\u0445 \u0437\u0430\u0434\u0430\u0447<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#21\">\u041d\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#22\">\u0422\u0430\u0431\u043b\u0438\u0446\u044b \u0438 \u0433\u0440\u0430\u0444\u0438\u043a\u0438<\/a><\/p>\n<ul>\n<li>\n<p><a href=\"#23\">\u041a\u0430\u043a Background Task \u0432\u043b\u0438\u044f\u0435\u0442 \u043d\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c FastAPI<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#24\">\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438 FastAPI \u043f\u0440\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 \u0444\u043e\u043d\u043e\u0432\u044b\u0445 \u0437\u0430\u0434\u0430\u0447 \u0432 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u0435\u0440\u0432\u0438\u0441<\/a><\/p>\n<ul>\n<li>\n<p><a href=\"#25\">ARQ<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#26\">SAQ<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#27\">FastStream<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#28\">Celery<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#29\">\u0421\u0440\u0430\u0432\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#30\">\u0421\u0440\u0430\u0432\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u0441\u043b\u0435 \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u0438\u044f \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432<\/a><\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><a href=\"#31\">\u0418\u0442\u043e\u0433\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#32\">\u0412\u044b\u0432\u043e\u0434\u044b<\/a><\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"1\" id=\"1\"><\/a><\/p>\n<h2>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 \u0444\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438<\/h2>\n<p>\u0414\u043b\u044f \u0443\u0441\u043a\u043e\u0440\u0435\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0447\u0430\u0441\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0438 \u0447\u0430\u0441\u0442\u043e \u0432\u044b\u043d\u043e\u0441\u0438\u0442\u0441\u044f \u0432 \u0444\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438:\u00a0<\/p>\n<ul>\n<li>\n<p>\u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u043f\u043e <a href=\"https:\/\/crontab.guru\"><u>cron<\/u><\/a><\/p>\n<\/li>\n<li>\n<p>\u0437\u0430\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 (\u043e\u0442\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435) \u0437\u0430\u0434\u0430\u0447\u0438<\/p>\n<\/li>\n<li>\n<p>\u0442\u044f\u0436\u0451\u043b\u044b\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b:<\/p>\n<ul>\n<li>\n<p>\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043a \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u043c \u2014 CPU-bound<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u2014 \u043a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, I\/O-bound<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440\u044b:<\/p>\n<ul>\n<li>\n<p><strong>\u041e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u043f\u0438\u0441\u0435\u043c \u043d\u0430 \u043f\u043e\u0447\u0442\u0443.<\/strong> \u041c\u043e\u0436\u043d\u043e \u0441\u0440\u0430\u0437\u0443 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043e\u0442\u0432\u0435\u0442 \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0443 \u043f\u0438\u0441\u044c\u043c\u0430 \u0432 \u0444\u043e\u043d\u0435. \u042d\u0442\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043d\u044f\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u043a\u0443\u043d\u0434, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043b\u0443\u0447\u0448\u0435 \u043d\u0435 \u0437\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0436\u0434\u0430\u0442\u044c, \u043f\u043e\u043a\u0430 \u043f\u0438\u0441\u044c\u043c\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0441\u044f<\/p>\n<\/li>\n<li>\n<p><strong>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445.<\/strong> \u041f\u0440\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u043e\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043e\u0442\u0432\u0435\u0442 &#171;Accepted&#187; (HTTP 202) \u0438 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0444\u0430\u0439\u043b\u043e\u043c \u0432 \u0444\u043e\u043d\u043e\u0432\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 I\/O-bound \u0438 CPU-bound \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438<\/summary>\n<div class=\"spoiler__content\">\n<p><strong>I\/O-bound \u2014 <\/strong>\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f, \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0430 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c\u044e \u043f\u043e\u0434\u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0432\u0432\u043e\u0434\u0430-\u0432\u044b\u0432\u043e\u0434\u0430. \u041a \u0442\u0430\u043a\u0438\u043c \u0437\u0430\u0434\u0430\u0447\u0430\u043c \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043d\u0435\u0441\u0442\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043f\u043e \u0441\u0435\u0442\u0438, \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0441 \u0431\u0430\u0437\u043e\u0439, \u0447\u0442\u0435\u043d\u0438\u0435\/\u0437\u0430\u043f\u0438\u0441\u044c \u0444\u0430\u0439\u043b\u0430 \u043d\u0430 \u0434\u0438\u0441\u043a.\u00a0<\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, HTTP-\u0437\u0430\u043f\u0440\u043e\u0441:<\/p>\n<p>r = requests.get(&#8216;<a href=\"https:\/\/api.github.com\/user\">https:\/\/api.github.com\/user<\/a>&#8216;, auth=(&#8216;user&#8217;, &#8216;pass&#8217;))<\/p>\n<p><strong>CPU-bound<\/strong> \u2014 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f, \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0430 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c\u044e CPU. \u0412 web-\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445 \u044d\u0442\u043e \u043e\u0431\u044b\u0447\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u043c\u0435\u0434\u0438\u0430: \u043e\u0431\u0440\u0435\u0437\u043a\u0430, \u0440\u0435\u0441\u0430\u0439\u0437, \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u044f \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a.<\/p>\n<\/div>\n<\/details>\n<p><a class=\"anchor\" name=\"2\" id=\"2\"><\/a><\/p>\n<h2>\u041f\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0443 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435, \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0431\u0440\u043e\u043a\u0435\u0440 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u2014 \u043c\u044b \u0432\u043e\u0437\u044c\u043c\u0451\u043c Redis, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043e\u043d \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0447\u0435\u0442\u044b\u0440\u0451\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a.<\/p>\n<p>\u0415\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0444\u043e\u043d\u043e\u0432\u044b\u0445 \u0437\u0430\u0434\u0430\u0447, \u043d\u043e \u0434\u043b\u044f \u0443\u043d\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u043c <a href=\"https:\/\/www.starlette.io\/background\/\"><u>\u0441 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0435\u0439 \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u0430 BackgroundTask \u0432 JSONResponse<\/u><\/a>:<\/p>\n<pre><code class=\"python\">@router.post(\"\") async def simple() -> JSONResponse:    task = BackgroundTask(func, *args, **kwargs)    return JSONResponse({\"status\": \"ok\"}, background=task)<\/code><\/pre>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 func \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0446\u0435\u043b\u0435\u0432\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e (\u0434\u043b\u044f BackgroundTask), \u043b\u0438\u0431\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442 \u0437\u0430\u0434\u0430\u0447\u0443 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435.<\/p>\n<p><a class=\"anchor\" name=\"3\" id=\"3\"><\/a><\/p>\n<p>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0442\u0443\u0442: <a href=\"https:\/\/github.com\/ipakeev\/fastapi-async-tasks\">https:\/\/github.com\/ipakeev\/fastapi-async-tasks<\/a><\/p>\n<p>\u0412 \u043d\u0430\u0448\u0435\u0439 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u044f Store, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0435\u0434\u0438\u043d\u043e\u0439 \u0442\u043e\u0447\u043a\u043e\u0439 \u0432\u0445\u043e\u0434\u0430 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0430\u043a\u0441\u0435\u0441\u0441\u043e\u0440\u043e\u0432. \u041f\u0440\u043e\u0435\u043a\u0442 \u0438\u043c\u0435\u0435\u0442 \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u0430\u043a\u0443\u044e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from contextlib import asynccontextmanager from typing import Annotated   from fastapi import Depends     class BaseAccessor:    def __init__(self, store: \"Store\") -> None:        self.store = store        self.config = settings      async def connect(self) -> None:        pass      async def disconnect(self) -> None:        pass     class Store:    def __init__(self) -> None:        # \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0446\u0438\u043a\u043b\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0438\u043c\u043f\u043e\u0440\u0442        from app.core.accessor import CoreAccessor        from app.redis.accessor import RedisAccessor        from app.worker.accessor import WorkerAccessor          self.core = CoreAccessor(self)        self.redis = RedisAccessor(self)        self.worker = WorkerAccessor(self)      async def connect(self) -> None:        await self.redis.connect()        await self.worker.connect()        await self.core.connect()      async def disconnect(self) -> None:        await self.core.disconnect()        await self.worker.disconnect()        await self.redis.disconnect()     _store: Store | None = None     def get_store() -> Store:    assert _store, \"Store is not initialized\"    return _store     async def connect_to_store() -> Store:    global _store      if not _store:        _store = Store()        await _store.connect()      return _store     async def disconnect_from_store() -> None:    global _store      if _store:        await _store.disconnect()        _store = None     @asynccontextmanager async def store_lifespan() -> AsyncGenerator[Store, None]:    await connect_to_store()    try:        yield get_store()    finally:        await disconnect_from_store()     StoreDep = Annotated[Store, Depends(get_store)] <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0414\u043b\u044f \u0434\u0435\u043a\u043b\u0430\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <a href=\"https:\/\/github.com\/ets-labs\/python-dependency-injector\"><u>dependency-injector<\/u><\/a>.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0432\u0441\u0435 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0444\u043e\u043d\u043e\u0432\u044b\u043c \u0437\u0430\u0434\u0430\u0447\u0438 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430.<\/p>\n<p><a class=\"anchor\" name=\"4\" id=\"4\"><\/a><\/p>\n<h3>FastAPI Background Tasks<\/h3>\n<p><a href=\"https:\/\/fastapi.tiangolo.com\/tutorial\/background-tasks\/\"><em>\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 GitHub<\/em><\/a><\/p>\n<p>\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 \u0432 FastAPI \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0444\u043e\u043d\u043e\u0432\u044b\u0445 \u0437\u0430\u0434\u0430\u0447. \u041e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f <em>\u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u043b\u0435<\/em> \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u0435\u0440\u043d\u0443\u043b \u043e\u0442\u0432\u0435\u0442. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u043d\u0435\u0442 \u043d\u0438\u043a\u0430\u043a\u043e\u0439 \u043e\u0447\u0435\u0440\u0435\u0434\u0438, \u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043d\u0438\u0433\u0434\u0435 \u043d\u0435 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f.<\/p>\n<pre><code class=\"python\">from starlette.background import BackgroundTask from starlette.responses import JSONResponse     # \u0444\u043e\u043d\u043e\u0432\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 async def send_email(email: str, message: str = \"Hello\") -> None:    ...     @router.post(\"\") async def hello() -> JSONResponse:    task = BackgroundTask(send_email, \"support@mail.ru\", message=\"Hello\")    return JSONResponse({\"status\": \"ok\"}, background=task) <\/code><\/pre>\n<p><a class=\"anchor\" name=\"5\" id=\"5\"><\/a><\/p>\n<h4>\u041f\u043b\u044e\u0441\u044b<\/h4>\n<p>&#8212; \u0423\u0434\u043e\u0431\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c<\/p>\n<p>&#8212; \u041e\u0447\u0435\u043d\u044c \u0443\u0434\u043e\u0431\u043d\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043d\u0435 \u043d\u0443\u0436\u043d\u044b \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0446\u0438\u0438<\/p>\n<p>&#8212; \u041d\u0435 \u043d\u0443\u0436\u043d\u043e \u0440\u0430\u0437\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0442\u044c \u0431\u0440\u043e\u043a\u0435\u0440 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439<\/p>\n<p>&#8212; \u041d\u0435 \u043d\u0443\u0436\u043d\u043e \u0440\u0430\u0437\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 (worker)<\/p>\n<p><a class=\"anchor\" name=\"6\" id=\"6\"><\/a><\/p>\n<h4>\u041c\u0438\u043d\u0443\u0441\u044b<\/h4>\n<p>&#8212; \u041f\u0440\u0438 \u0431\u043e\u043b\u044c\u0448\u043e\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0435 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 I\/O-bound \u0437\u0430\u0434\u0430\u0447 \u0438 \u0434\u0430\u0436\u0435 \u043f\u0440\u0438 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0435 CPU-bound \u0437\u0430\u0434\u0430\u0447 \u0441\u0435\u0440\u0432\u0438\u0441 \u0434\u0435\u0433\u0440\u0430\u0434\u0438\u0440\u0443\u0435\u0442: \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0432\u0440\u0435\u043c\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u0441\u043d\u0438\u0436\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e RPS.<\/p>\n<p>&#8212; \u041f\u0440\u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438\/\u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043d\u0435\u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043d\u043d\u044b\u0435 \u0438 \u043d\u0435 \u043d\u0430\u0447\u0430\u0442\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0431\u0443\u0434\u0443\u0442 \u0443\u0442\u0435\u0440\u044f\u043d\u044b<\/p>\n<p><a class=\"anchor\" name=\"7\" id=\"7\"><\/a><\/p>\n<h3>ARQ<\/h3>\n<p><a href=\"https:\/\/github.com\/samuelcolvin\/arq\"><em>\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 GitHub<\/em><\/a><\/p>\n<p>\u041b\u0435\u0433\u043a\u043e\u0432\u0435\u0441\u043d\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430, \u0437\u0430\u0434\u0443\u043c\u0430\u043d\u043d\u0430\u044f \u043a\u0430\u043a \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 <a href=\"https:\/\/python-rq.org\/\"><u>RQ<\/u><\/a>.<\/p>\n<p>\u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438:<\/p>\n<ul>\n<li>\n<p>\u0417\u0430\u0434\u0430\u0447\u0430 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043e\u043b\u0436\u043d\u0430 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u043f\u0435\u0440\u0432\u044b\u043c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0447\u0442\u043e \u0443\u0433\u043e\u0434\u043d\u043e<\/p>\n<\/li>\n<li>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0443 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0435\u0451 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import arq import arq.connections     # \u0444\u043e\u043d\u043e\u0432\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430: \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u044d\u043b. \u043f\u043e\u0447\u0442\u044b \u0432 \u0444\u043e\u043d\u0435 async def send_email(ctx: dict, email: str, message: str = \"Hello\") -> None:    store: Store = ctx[\"store\"]    \u2026     # \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0444\u043e\u043d\u043e\u0432\u044b\u043c \u0437\u0430\u0434\u0430\u0447\u0430\u043c class WorkerAccessor(AbstractTaskAccessor):      def __init__(self, *args: Any, **kwargs: Any) -> None:        super().__init__(*args, **kwargs)     # \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043f\u0443\u043b \u0437\u0430\u0434\u0430\u0447 arq        self.job_pool: arq.ArqRedis | None = None      async def connect(self) -> None:   # \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 Redis        self.job_pool = await arq.create_pool(            arq.connections.RedisSettings( # \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f                host=self.config.REDIS_DSN.host,                port=self.config.REDIS_DSN.port,                username=self.config.REDIS_DSN.username,                password=self.config.REDIS_DSN.password,                database=self.config.REDIS_DSN.path.removeprefix(\"\/\"),            )        )      async def disconnect(self) -> None:        \"\"\"\u0414\u043b\u044f \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\"\"\"        if self.job_pool:            await self.job_pool.close()      async def run(self) -> None:        \"\"\"\u0414\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0432\u043e\u0440\u043a\u0435\u0440\u0430 \u0432 \u0434\u0440\u0443\u0433\u043e\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0435\"\"\"               async def on_startup(ctx: dict) -> None:  # \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0432 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435            ctx[\"store\"] = self.store          worker = arq.Worker(  # \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0444\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438            functions=[send_email],            on_startup=on_startup,            max_jobs=self.config.ARQ_CONCURRENCY,            health_check_interval=60,            handle_signals=False,            redis_pool=self.job_pool,        )        try:  # \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0432\u043e\u0440\u043a\u0435\u0440            await worker.main()        finally:  # \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0432\u043e\u0440\u043a\u0435\u0440            await worker.close()               async def send_email(self, email: str, message: str = \"Hello\") -> None:        # \u0441\u0442\u0430\u0432\u0438\u043c \u0437\u0430\u0434\u0430\u0447\u0443 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435        await self.job_pool.enqueue_job(send_email.__name__, email, message=message)  # \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0444\u043e\u043d\u043e\u0432\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u044d\u043b. \u043f\u043e\u0447\u0442\u044b, \u0432\u0430\u0440\u0438\u0430\u043d\u0442 1 @router.post(\"\") async def hello(store: StoreDep) -> JSONResponse:    task = BackgroundTask(store.worker.arq.send_email, \"support@mail.ru\", message=\"Hello\")    return JSONResponse({\"status\": \"ok\"}, background=task)     # \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0444\u043e\u043d\u043e\u0432\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u044d\u043b. \u043f\u043e\u0447\u0442\u044b, \u0432\u0430\u0440\u0438\u0430\u043d\u0442 2 @router.post(\"\") async def hello(store: StoreDep) -> dict:    await store.worker.arq.send_email(\"support@mail.ru\", message=\"Hello\")    return {\"status\": \"ok\"}<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0432\u043e\u0440\u043a\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u0442\u0430\u043a\u0438\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c:<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">async def main() -> None:    async with store_lifespan() as store:        await store.worker.arq.run()     if __name__ == \"__main__\":    asyncio.run(main()) <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p><a class=\"anchor\" name=\"8\" id=\"8\"><\/a><\/p>\n<h4>\u041f\u043b\u044e\u0441\u044b<\/h4>\n<ul>\n<li>\n<p>\u0423\u0434\u043e\u0431\u043d\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c (\u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 <a href=\"https:\/\/github.com\/cunla\/fakeredis-py\"><u>fakeredis<\/u><\/a>)<\/p>\n<\/li>\n<li>\n<p>\u041c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0442\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438\u00a0<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a \u0437\u0430\u0434\u0430\u0447 \u043f\u043e \u0440\u0430\u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044e (cron)<\/p>\n<\/li>\n<li>\n<p>\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430 (retrying) \u0438 \u043e\u0442\u043c\u0435\u043d\u044b \u0437\u0430\u0434\u0430\u0447\u0438<\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"9\" id=\"9\"><\/a><\/p>\n<h4>\u041c\u0438\u043d\u0443\u0441\u044b<\/h4>\n<ul>\n<li>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u0440\u043e\u043a\u0435\u0440\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e Redis<\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"10\" id=\"10\"><\/a><\/p>\n<h3>SAQ<\/h3>\n<p><a href=\"https:\/\/github.com\/tobymao\/saq\"><em>\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 GitHub<\/em><\/a><\/p>\n<p>\u0410\u043d\u0430\u043b\u043e\u0433 ARQ \u0441\u043e \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u043c \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u043e\u043c.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import saq   from aiohttp.web_runner import AppRunner, TCPSite from redis.asyncio import Redis from saq.web.aiohttp import create_app     # \u0444\u043e\u043d\u043e\u0432\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 async def send_email(ctx: dict, email: str, message: str = \"Hello\") -> None:    store: Store = ctx[\"store\"]    \u2026     class WorkerkAccessor(BaseAccessor):    def __init__(self, *args: Any, **kwargs: Any) -> None:        super().__init__(*args, **kwargs)          self.queue: saq.Queue | None = None      async def connect(self) -> None:        client = Redis.from_url(self.config.REDIS_DSN.__str__())        self.queue = saq.Queue(client)      async def disconnect(self) -> None:        if self.queue:            await self.queue.disconnect()      async def run(self, web: bool = False, port: int = 8090) -> None:       \"\"\"\u0414\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0432\u043e\u0440\u043a\u0435\u0440\u0430 \u0432 \u0434\u0440\u0443\u0433\u043e\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0435\"\"\"          async def on_startup(ctx: dict) -> None:            ctx[\"store\"] = self.store          worker = saq.Worker(            self.queue,            functions=[send_email],  # \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0444\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438            startup=on_startup,            concurrency=self.config.SAQ_CONCURRENCY,        )          if web:            # \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0434\u0430\u0448\u0431\u043e\u0440\u0434            app = create_app([self.queue])            task = asyncio.create_task(worker.start())              try:                runner = AppRunner(app)                await runner.setup()                site = TCPSite(runner, port=port)                await site.start()                  await asyncio.Event().wait()            finally:                await worker.stop()                task.cancel()        else:            await worker.start()      async def send_email(self, email: str, message: str = \"Hello\") -> None:        # \u0441\u0442\u0430\u0432\u0438\u043c \u0437\u0430\u0434\u0430\u0447\u0443 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435        await self.queue.enqueue(send_email.__name__, email=email, message=message)       @router.post(\"\") async def hello(store: StoreDep) -> JSONResponse:    task = BackgroundTask(store.worker.saq.send_email, \"support@mail.ru\", message=\"Hello\")    return JSONResponse({\"status\": \"ok\"}, background=task)     # \u0438\u043b\u0438 \u0442\u0430\u043a @router.post(\"\") async def hello(store: StoreDep) -> dict:    await store.worker.saq.send_email(\"support@mail.ru\", message=\"Hello\")    return {\"status\": \"ok\"}<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0432\u043e\u0440\u043a\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u0442\u0430\u043a\u0438\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c:<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">async def main() -> None:    async with store_lifespan() as store:        await store.worker.saq.run(web=True)     if __name__ == \"__main__\":    asyncio.run(main()) <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p><a class=\"anchor\" name=\"11\" id=\"11\"><\/a><\/p>\n<h4>\u041f\u043b\u044e\u0441\u044b<\/h4>\n<ul>\n<li>\n<p>\u0423\u0434\u043e\u0431\u043d\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 <a href=\"https:\/\/github.com\/cunla\/fakeredis-py\"><u>fakeredis<\/u><\/a><\/p>\n<\/li>\n<li>\n<p>\u041c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0442\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438\u00a0<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a \u0437\u0430\u0434\u0430\u0447 \u043f\u043e \u0440\u0430\u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044e (cron)<\/p>\n<\/li>\n<li>\n<p>\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430 (retrying)<\/p>\n<\/li>\n<li>\n<p>\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 web \u0434\u0430\u0448\u0431\u043e\u0440\u0434 \u0434\u043b\u044f \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430 \u0437\u0430\u0434\u0430\u0447:<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/481\/38f\/8b9\/48138f8b93f1d3e5b233dab27d98effd.png\" width=\"1315\" height=\"862\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/481\/38f\/8b9\/48138f8b93f1d3e5b233dab27d98effd.png\"\/><\/figure>\n<p><a class=\"anchor\" name=\"12\" id=\"12\"><\/a><\/p>\n<h4>\u041c\u0438\u043d\u0443\u0441\u044b<\/h4>\n<ul>\n<li>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u0440\u043e\u043a\u0435\u0440\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e Redis<\/p>\n<\/li>\n<li>\n<p>\u041d\u0435 \u043e\u0447\u0435\u043d\u044c \u0443\u0434\u0430\u0447\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f: \u0432 \u0437\u0430\u0434\u0430\u0447\u0443 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u043a\u0438\u0434\u044b\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e kwargs, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0434\u0443\u0442 \u0432\u043f\u0435\u0440\u0435\u043c\u0435\u0448\u043a\u0443 \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0437\u0430\u0434\u0430\u0447\u0438<\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"13\" id=\"13\"><\/a><\/p>\n<h3>FastStream<\/h3>\n<p><a href=\"https:\/\/github.com\/airtai\/faststream\"><em>\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 GitHub<\/em><\/a><\/p>\n<p>\u041e\u0447\u0435\u043d\u044c \u043f\u0435\u0440\u0441\u043f\u0435\u043a\u0442\u0438\u0432\u043d\u044b\u0439 \u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a, \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0438\u0434\u0435\u0439\u043d\u044b\u043c \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u0435\u043c <a href=\"https:\/\/github.com\/airtai\/fastkafka\"><u>FastKafka<\/u><\/a> \u0438 <a href=\"https:\/\/github.com\/lancetnik\/propan\"><u>Propan<\/u><\/a>. \u041e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u0434\u0440\u0443\u0433\u0438\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u0432 \u0441\u0442\u0430\u0442\u044c\u0435 \u0442\u0435\u043c, \u0447\u0442\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u0431\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443 pub\/sub.\u00a0<\/p>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u044c\u0438 \u0434\u043b\u044f \u043e\u0431\u044b\u0447\u043d\u043e\u0439 \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u0437\u0430\u0434\u0430\u0447 \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u043f\u043e\u0434\u0432\u043e\u0434\u043d\u044b\u0435 \u043a\u0430\u043c\u043d\u0438:\u00a0\u00a0<\/p>\n<ul>\n<li>\n<p>\u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043e\u0434\u043d\u043e\u0433\u043e \u0432\u043e\u0440\u043a\u0435\u0440\u0430 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438<\/p>\n<\/li>\n<li>\n<p>\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043e\u0434\u043d\u043e\u0433\u043e \u0442\u043e\u043f\u0438\u043a\u0430 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0441\u0442\u0440\u043e\u0433\u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u2014 \u043d\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0443\u043a\u0430\u0437\u0430\u0442\u044c concurrency<\/p>\n<\/li>\n<li>\n<p>\u0435\u0441\u043b\u0438 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0432\u043e\u0440\u043a\u0435\u0440\u043e\u0432 \u0431\u043e\u043b\u044c\u0448\u0435 1, \u0442\u043e \u043a\u0430\u0436\u0434\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0441\u0442\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437, \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u0442\u044c \u0432\u043e\u0440\u043a\u0435\u0440\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u043e \u0440\u0430\u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044e \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441 Taskiq: <a href=\"https:\/\/github.com\/taskiq-python\/taskiq-faststream\"><u>Taskiq &#8212; FastStream<\/u><\/a><\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0438\u043c\u0435\u0435\u0442 \u043c\u043d\u043e\u0433\u043e \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432, \u043e \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043d\u0438\u0436\u0435. \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u2014 \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0437\u0430\u043f\u0443\u0441\u043a \u0444\u043e\u043d\u043e\u0432\u044b\u0445 \u0437\u0430\u0434\u0430\u0447:<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import faststream from faststream.redis import RedisBroker     class FastStream(faststream.FastStream):    def __init__(self, *args: Any, **kwargs: Any) -> None:        super().__init__(*args, **kwargs)          self.on_startup(self.connect)        self.on_shutdown(self.disconnect)      async def connect(self, *_: Any, **__: Any) -> None:        await connect_to_store()          await self.broker.connect(url=settings.REDIS_DSN.__str__())      async def disconnect(self, *_: Any, **__: Any) -> None:        await disconnect_from_store()          await self.broker.close()       broker = RedisBroker()   # \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0432\u043e\u0440\u043a\u0435\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b faststream_app = FastStream(broker)   # \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0444\u043e\u043d\u043e\u0432\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 SEND_EMAIL_TOPIC = \"send_email\"     # \u0444\u043e\u043d\u043e\u0432\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 @broker.subscriber(SEND_EMAIL_TOPIC) async def send_email(email: str, message: str) -> None:    \u2026     class WorkerAccessor(BaseAccessor):    async def connect(self) -> None:        await broker.connect(url=self.config.REDIS_DSN.__str__())      async def disconnect(self) -> None:        await broker.close()      async def send_email(self, email: str, message: str = \"Hello\") -> None:        # \u0441\u0442\u0430\u0432\u0438\u043c \u0437\u0430\u0434\u0430\u0447\u0443 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435        await broker.publish(            message={\"email\": email, \"message\": message},            channel=SEND_EMAIL_TOPIC,        )     @router.post(\"\") async def hello(store: StoreDep) -> JSONResponse:    task = BackgroundTask(store.worker.faststream.send_email, \"support@mail.ru\", message=\"Hello\")    return JSONResponse({\"status\": \"ok\"}, background=task)     # \u0438\u043b\u0438 \u0442\u0430\u043a @router.post(\"\") async def hello(store: StoreDep) -> dict:    await store.worker.faststream.send_email(\"support@mail.ru\", message=\"Hello\")    return {\"status\": \"ok\"}<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0412\u043e\u0440\u043a\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u0432\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0432 lifespan FastAPI \u043b\u0438\u0431\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439 \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0435:<\/summary>\n<div class=\"spoiler__content\">\n<pre><code>faststream run app.worker.faststream:faststream_app \u2013workers=1<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c AsyncAPI:<\/summary>\n<div class=\"spoiler__content\">\n<pre><code>faststream docs serve app.worker.faststream:faststream_app<\/code><\/pre>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/0c9\/ddb\/b95\/0c9ddbb95487c168828a95f8e748d7cb.png\" width=\"1600\" height=\"835\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/0c9\/ddb\/b95\/0c9ddbb95487c168828a95f8e748d7cb.png\"\/><\/figure>\n<\/p>\n<\/div>\n<\/details>\n<p><a class=\"anchor\" name=\"14\" id=\"14\"><\/a><\/p>\n<h4>\u041f\u043b\u044e\u0441\u044b<\/h4>\n<ul>\n<li>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u0440\u043e\u043a\u0435\u0440\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c Apache Kafka, RabbitMQ, NATS \u0438 Redis<\/p>\n<\/li>\n<li>\n<p>\u0410\u0432\u0442\u043e\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 (AsyncAPI)<\/p>\n<\/li>\n<li>\n<p>\u0423\u0434\u043e\u0431\u043d\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c (in-memory \u0431\u0440\u043e\u043a\u0435\u0440)<\/p>\n<\/li>\n<li>\n<p>\u0422\u0435\u0441\u043d\u0430\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441 Pydantic \u0438 Dependency injection<\/p>\n<\/li>\n<li>\n<p>\u041e\u0447\u0435\u043d\u044c \u0442\u0435\u0441\u043d\u0430\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441 FastAPI, \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0432 lifespan \u0438 \u043d\u0435 \u0434\u0435\u043f\u043b\u043e\u0438\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u2014 \u043f\u043e\u043b\u0435\u0437\u043d\u043e \u043f\u0440\u0438 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435<\/p>\n<\/li>\n<li>\n<p>\u041b\u0451\u0433\u043a\u0430\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u043c\u0438 web-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430\u043c\u0438: Aiohttp, Django<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/faststream-gen.airt.ai\/\"><u>\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u043a\u043e\u0434\u043e\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u044f\u0437\u044b\u043a\u043e\u0432\u044b\u0445 \u043c\u043e\u0434\u0435\u043b\u0435\u0439<\/u><\/a><\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"15\" id=\"15\"><\/a><\/p>\n<h4>\u041c\u0438\u043d\u0443\u0441<\/h4>\n<ul>\n<li>\n<p>\u0424\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0432\u043f\u0435\u0440\u0432\u044b\u0435 \u0432\u044b\u0448\u0435\u043b \u0432 \u0440\u0435\u043b\u0438\u0437 \u0432 \u0441\u0435\u043d\u0442\u044f\u0431\u0440\u0435 2023 \u0433\u043e\u0434\u0430 \u0438 \u043f\u043e\u043a\u0430 \u043d\u0435 \u043f\u0440\u043e\u0448\u0451\u043b \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u0443\u0441\u043f\u0435\u043b \u043e\u0431\u0440\u0435\u0441\u0442\u0438 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e\u0441\u0442\u044c \u0438 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430 \u0441\u0442\u0430\u0434\u0438\u0438 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438<\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"16\" id=\"16\"><\/a><\/p>\n<h3>Celery<\/h3>\n<p><a href=\"https:\/\/github.com\/celery\/celery\"><em>\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 GitHub<\/em><\/a><\/p>\n<p>Celery \u2014 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0439 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u0447\u0435\u043d\u044c \u0433\u0438\u0431\u043a\u043e \u043e\u043f\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0444\u043e\u043d\u043e\u0432\u044b\u043c\u0438 \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438. \u041e\u043d \u043e\u0431\u043b\u0430\u0434\u0430\u0435\u0442 \u0448\u0438\u0440\u043e\u043a\u0438\u043c\u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044f\u043c\u0438, \u043d\u043e \u043f\u043e\u0440\u043e\u0433 \u0432\u0445\u043e\u0434\u0430 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432\u044b\u0448\u0435, \u0447\u0435\u043c \u0443 ARQ \u0438\u043b\u0438 SAQ.\u00a0<\/p>\n<p>\u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u0434\u043e \u0441\u0438\u0445 \u043f\u043e\u0440 \u043d\u0435\u0442 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0439 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430, \u043d\u043e \u043e\u0431\u0435\u0449\u0430\u044e\u0442 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 <a href=\"https:\/\/github.com\/celery\/celery\/issues\/7874#event-10201939571\"><u>Celery 6.0<\/u><\/a>. \u0416\u0434\u0451\u043c. \u0410 \u043f\u043e\u043a\u0430 \u043c\u043e\u0436\u0435\u043c \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c Celery \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430. <\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import celery from celery import signals from functools import wraps     class Celery(celery.Celery):    def __init__(self, *args: Any, **kwargs: Any) -> None:        super().__init__(*args, **kwargs)               # \u0442\u0443\u0442 \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f (eager execution)        self.functions: dict[str, Callable[..., Any]] = {}               # \u0446\u0438\u043a\u043b \u0441\u043e\u0431\u044b\u0442\u0438\u0438\u0306, \u0447\u0442\u043e\u0431\u044b \u0438\u0437 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0443\u044e        self.loop = asyncio.get_event_loop()      def connect(self, *_: Any, **__: Any) -> None:        self.loop.run_until_complete(connect_to_store())      def disconnect(self, *_: Any, **__: Any) -> None:        self.loop.run_until_complete(disconnect_from_store())      def task(        self,        task: Callable[..., Awaitable[T]] | None = None,        **opts: Any,    ) -> Callable:        # \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 \u043e\u0442 celery        create_task = super().task          def decorator(func: Callable[..., Awaitable[T]]) -> Callable[..., T]:            @create_task(**opts)  # \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0437\u0430\u0434\u0430\u0447\u0443            @wraps(func)            def wrapper(*args: Any, loop: AbstractEventLoop | None = None, **kwargs: Any) -> T: # \u0434\u043b\u044f \u0441\u043b\u0443\u0447\u0430\u0435\u0432, \u043a\u043e\u0433\u0434\u0430 \u043e\u0447\u0435\u043d\u044c \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0443 \u0441\u0440\u0430\u0437\u0443 (\u0431\u0435\u0437 apply_async)    loop = loop or self.loop                  # \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0432 \u0446\u0438\u043a\u043b\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u0439                return loop.run_until_complete(func(*args, **kwargs))                       # \u0437\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f            self.functions[wrapper.name] = func              return wrapper          if task:            return decorator(task)          return decorator     celery_app = Celery(\"celery\", broker=settings.REDIS_DSN.__str__()) celery_app.autodiscover_tasks(packages=[\"app.worker.async_celery.tasks\"])   # \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c store signals.worker_process_init.connect(celery_app.connect) signals.worker_process_shutdown.connect(celery_app.disconnect) <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0417\u0430\u043f\u0443\u0441\u043a \u0437\u0430\u0434\u0430\u0447\u0438 \u043d\u0435 \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u043e\u0431\u044b\u0447\u043d\u043e\u0433\u043e \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430 (delay, apply_async \u0438 \u043f\u0440.):<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\"># \u0444\u043e\u043d\u043e\u0432\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 @celery_app.task async def send_email(email: str, message: str) -> None:    \u2026     @router.post(\"\") async def hello() -> JSONResponse:    task = BackgroundTask(send_email.delay, \"support@mail.ru\", message=\"Hello\")    return JSONResponse({\"status\": \"ok\"}, background=task)     # \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b: @router.post(\"\") async def hello() -> dict:    # \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u043f\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435    send_email.delay(\"support@mail.ru\", message=\"Hello\")    send_email.apply_async((\"support@mail.ru\",), {\"message\": \"Hello\"}, countdown=5)    send_email.s(\"support@mail.ru\", message=\"Hello\").apply_async(countdown=5)      # \u043f\u0440\u0438\u043c\u0435\u0440 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435    send_email(\"support@mail.ru\", message=\"Hello\", loop=asyncio.get_running_loop())      return {\"status\": \"ok\"}<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0414\u043b\u044f eager execution \u0432 \u0442\u0435\u0441\u0442\u0430\u0445 \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u043f\u0440\u0438\u0431\u0435\u0433\u043d\u0443\u0442\u044c \u043a \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044e nest-asyncio (conftest.py):<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from contextlib import ExitStack   import nest_asyncio import pytest     # \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u043b\u043e\u0432\u0438\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0438 \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u0446\u0438\u043a\u043b \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0443\u0436\u0435 \u0437\u0430\u043f\u0443\u0449\u0435\u043d nest_asyncio.apply()     @pytest.fixture(scope=\"session\", autouse=True) def celery_eager_execution(store: Store) -> Generator[None, None, None]:    # \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0432\u0441\u0435 \u0444\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438    from app.worker.async_celery import tasks  # noqa: F401      def execute_task(name: str) -> Callable[..., Any]:        # \u0434\u043e\u0441\u0442\u0430\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0434\u043b\u044f \u044d\u0442\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0435\u0435 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 side_effect \u0443 \u043c\u043e\u043a\u0430        func = celery_app.functions[name]          def wrapper(args: Any, kwargs: Any, **_: Any) -> Any:  loop = asyncio.get_running_loop()            return loop.run_until_complete(func(*args, **kwargs))          return wrapper       # \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043c\u043e\u043a\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438    patches = [        patch.object(task, \"apply_async\", side_effect=execute_task(name))        for name, task in celery_app.tasks.items()    ]       # \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0432\u0441\u0435 \u043c\u043e\u043a\u0438    with ExitStack() as stack:        for p in patches:            stack.enter_context(p)        yield<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0412\u043e\u0440\u043a\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439 \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0435:<\/summary>\n<div class=\"spoiler__content\">\n<pre><code>celery -A app.worker.async_celery.app:celery_app worker \u2013concurrency=3<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>Flower \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439 \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0435:<\/summary>\n<div class=\"spoiler__content\">\n<pre><code>celery -A app.worker.async_celery.app:celery_app flower<\/code><\/pre>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/db1\/b92\/d8d\/db1b92d8d60e3e75249610542794f41e.png\" width=\"1600\" height=\"705\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/db1\/b92\/d8d\/db1b92d8d60e3e75249610542794f41e.png\"\/><\/figure>\n<\/p>\n<\/div>\n<\/details>\n<p><a class=\"anchor\" name=\"17\" id=\"17\"><\/a><\/p>\n<h4>\u041f\u043b\u044e\u0441\u044b<\/h4>\n<ul>\n<li>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u0435\u043d\u043d\u0430\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u043e\u0439 \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u0437\u0430\u0434\u0430\u0447<\/p>\n<\/li>\n<li>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u0440\u043e\u043a\u0435\u0440\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c RabbitMQ, Redis \u0438 Amazon SQS<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a \u0437\u0430\u0434\u0430\u0447 \u043f\u043e cron<\/p>\n<\/li>\n<li>\n<p>\u041e\u0433\u0440\u043e\u043c\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u043e \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044e \u0438 \u0436\u043e\u043d\u0433\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044f\u043c\u0438 \u0438 \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438<\/p>\n<\/li>\n<li>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0438<\/p>\n<\/li>\n<li>\n<p>\u0412 \u0441\u0432\u044f\u0437\u043a\u0435 \u0441 <a href=\"https:\/\/github.com\/mher\/flower\"><u>Flower<\/u><\/a> \u043b\u0435\u0433\u043a\u043e \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438 \u0438 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u0432 Prometheus<\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"18\" id=\"18\"><\/a><\/p>\n<h4>\u041c\u0438\u043d\u0443\u0441\u044b<\/h4>\n<ul>\n<li>\n<p>\u0412\u043d\u0443\u0442\u0440\u0438 \u0444\u043e\u043d\u043e\u0432\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u043d\u0435\u043b\u044c\u0437\u044f \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0432 ProcessPoolExecutor<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u043a\u0430 \u043d\u0435\u0442 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0439 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0437\u0434\u0435\u0440\u0436\u043a\u0438 \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438<\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"19\" id=\"19\"><\/a><\/p>\n<h2>\u041a\u0430\u043a\u0438\u0435 \u0435\u0449\u0451 \u0435\u0441\u0442\u044c \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0444\u043e\u043d\u043e\u0432\u044b\u043c\u0438 \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438<\/h2>\n<ul>\n<li>\n<p><a href=\"https:\/\/github.com\/taskiq-python\/taskiq\"><u>Taskiq<\/u><\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/agronholm\/apscheduler\"><u>APScheduler<\/u><\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/Miksus\/rocketry\"><u>Rocketry<\/u><\/a><\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0437\u043d\u0430\u0435\u0442\u0435 \u0434\u0440\u0443\u0433\u0438\u0435, \u043d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445<\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"20\" id=\"20\"><\/a><\/p>\n<h2>\u041c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433<\/h2>\n<p>\u0414\u043b\u044f FastAPI \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e \u2014 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <a href=\"https:\/\/github.com\/trallnag\/prometheus-fastapi-instrumentator\"><u>prometheus-fastapi-instrumentator<\/u><\/a>.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from fastapi import FastAPI from prometheus_fastapi_instrumentator import Instrumentator   from app.api.router import router   app = FastAPI(title=\"FastAPI async tasks\") app.include_router(router)   # \u0432\u0441\u0435 \u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u0431\u0443\u0434\u0443\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u043f\u043e \u043f\u0443\u0442\u0438 \/metrics Instrumentator().instrument(app).expose(app) <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0410 \u0432\u043e\u0442 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u0438\u043c\u0435\u043d\u043d\u043e \u0444\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u2014 \u0437\u0430\u0434\u0430\u0447\u0430 \u043d\u0435\u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u0430\u044f. \u041e\u043d\u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0432 \u0434\u0440\u0443\u0433\u043e\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0438\u043b\u0438 \u0434\u0430\u0436\u0435 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0435 \u0438 \u043d\u0438\u043a\u0430\u043a \u043d\u0435 \u0441\u0432\u044f\u0437\u0430\u043d\u044b \u0441 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u043c web-\u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c.\u00a0<\/p>\n<p>\u0414\u043b\u044f \u0438\u0445 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u043e\u0439 <a href=\"https:\/\/github.com\/hynek\/prometheus-async\"><u>prometheus-async<\/u><\/a>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0441\u0431\u043e\u0440 \u043c\u0435\u0442\u0440\u0438\u043a \u0438 \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c web-\u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e \u043f\u0443\u0442\u0438 \/metrics \u0431\u0443\u0434\u0435\u0442 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0438 \u043c\u0435\u0442\u0440\u0438\u043a\u0438.<\/p>\n<p>\u0414\u043b\u044f \u0421elery \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 prometheus-async \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e Flower \u0438\u043c\u0435\u0435\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0443\u044e <a href=\"https:\/\/flower.readthedocs.io\/en\/latest\/prometheus-integration.html\"><u>\u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u0441 Prometheus<\/u><\/a>.<\/p>\n<details class=\"spoiler\">\n<summary>\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 export_task_metrics, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u043c \u043d\u0430\u0432\u0435\u0448\u0438\u0432\u0430\u0442\u044c \u043d\u0430 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0449\u0443\u044e \u043d\u0430\u0441 \u0444\u0443\u043d\u043a\u0446\u0438\u044e:<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from prometheus_async.aio import count_exceptions, time from prometheus_client import Counter, Histogram   EXECUTION_TIME = Histogram(    \"task_execution_seconds\",    \"Task execution time\",    labelnames=[\"task_name\"],  # \u0447\u0442\u043e\u0431\u044b \u0432 \u043c\u0435\u0442\u0440\u0438\u043a\u0430\u0445 \u0432\u0438\u0434\u0435\u0442\u044c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0444\u043e\u043d\u043e\u0432\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 ) TASKS_FAILED = Counter(    \"tasks_failed\",    \"Tasks failed\",    labelnames=[\"task_name\"],  # \u0447\u0442\u043e\u0431\u044b \u0432 \u043c\u0435\u0442\u0440\u0438\u043a\u0430\u0445 \u0432\u0438\u0434\u0435\u0442\u044c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0444\u043e\u043d\u043e\u0432\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 )     # \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u0441\u0431\u043e\u0440\u0430 \u043c\u0435\u0442\u0440\u0438\u043a def export_task_metrics(func: Callable) -> Callable:    task_name = func.__name__       @count_exceptions(TASKS_FAILED.labels(task_name=task_name))  # \u043f\u043e\u0434\u0441\u0447\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u043e\u0448\u0438\u0431\u043e\u043a    @time(EXECUTION_TIME.labels(task_name=task_name))  # \u043f\u043e\u0434\u0441\u0447\u0435\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f    @wraps(func)    def wrapper(*args: Any, **kwargs: Any) -> Any:        return func(*args, **kwargs)      return wrapper<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440:<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">@export_task_metrics async def send_email(email: str, message: str) -> None:    ... <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438 \u0441\u0442\u0430\u0440\u0442\u0435 \u0432\u043e\u0440\u043a\u0435\u0440\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c web-\u0441\u0435\u0440\u0432\u0435\u0440 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u0442\u0440\u0435\u0434\u0435, \u0447\u0442\u043e\u0431\u044b \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u043f\u043e  \u0444\u043e\u043d\u043e\u0432\u044b\u043c \u0437\u0430\u0434\u0430\u0447\u0430\u043c (\u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 ARQ):<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from prometheus_async.aio.web import start_http_server_in_thread     async def main() -> None:    start_http_server_in_thread(port=settings.ARQ_EXPORTER_PORT)      async with store_lifespan() as store:        await store.worker.arq.run()     if __name__ == \"__main__\":    asyncio.run(main()) <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p><strong>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435,<\/strong> \u0447\u0442\u043e \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u0432\u043e\u0440\u043a\u0435\u0440\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0441\u0432\u043e\u0439 web-\u0441\u0435\u0440\u0432\u0435<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u0435\u0439\u0447\u0430\u0441 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u0435 4 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c Prometheus \u0434\u043b\u044f \u0441\u0431\u043e\u0440\u0430 \u0432\u0441\u0435\u0445 \u043c\u0435\u0442\u0440\u0438\u043a:<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"bash\"># prometheus.yml<\/code><\/pre>\n<pre><code class=\"python\">global:  scrape_interval: 15s   scrape_configs:  - job_name: \"fastapi\"    static_configs:      - targets: [\"fastapi:8000\"]    - job_name: \"arq\"    static_configs:      - targets: [\"arq:8001\"]    - job_name: \"saq\"    static_configs:      - targets: [\"saq:8002\"]    - job_name: \"faststream\"    static_configs:      - targets: [\"faststream:8003\"]    - job_name: \"async-celery\"    static_configs:      - targets: [\"async-celery-flower:5555\"]<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0421\u043e\u0431\u0435\u0440\u0451\u043c \u043e\u0431\u0440\u0430\u0437 \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"bash\">docker build -t fastapi-async-tasks .<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0418 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u0432 docker compose. \u0412\u0441\u0435 \u0432\u043e\u0440\u043a\u0435\u0440\u044b \u0431\u0443\u0434\u0443\u0442 \u0438\u043c\u0435\u0442\u044c \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u043e \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b: <\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"bash\"># docker-compose.yml<\/code><\/pre>\n<pre><code class=\"python\">version: \"3.8\"   x-tasks-common: &amp;tasks-common    image: fastapi-async-tasks    volumes:        - .:\/app  # \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438    depends_on:        - fastapi    restart: on-failure    networks:        - default    deploy:        resources:            limits:                memory: 200M                cpus: \"0.2\"   services:    redis:        image: redis:7.2.4        ports:            - \"6379:6379\"        restart: on-failure        networks:            - default      prometheus:        image: prom\/prometheus        ports:            - \"9090:9090\"        volumes:            - .prometheus\/prometheus.yml:\/etc\/prometheus\/prometheus.yml        command:            -  --config.file=\/etc\/prometheus\/prometheus.yml        restart: on-failure        networks:            - default      grafana:        image: grafana\/grafana        ports:            - \"3000:3000\"        volumes:            - type: volume              source: grafana              target: \/var\/lib\/grafana        environment:            - GF_SECURITY_ADMIN_USER=admin            - GF_SECURITY_ADMIN_PASSWORD=admin            - GF_INSTALL_PLUGINS=redis-app        user: \"0\"        restart: on-failure        networks:            - default      fastapi:        image: fastapi-async-tasks        ports:            - \"8000:8000\"        volumes:            - .:\/app        depends_on:            - redis            - prometheus            - grafana        restart: on-failure        networks:            - default        deploy:            resources:                limits:                    memory: 200M                    cpus: \"0.2\"        command:            - uvicorn            - app.main:app            - --host=0.0.0.0            - --port=8000            - --reload            - --workers=${FASTAPI_WORKERS}      arq:        &lt;&lt;: *tasks-common        ports:            - \"8001:8001\"  # metrics        command:            - python            - -m            - worker_arq      saq:        &lt;&lt;: *tasks-common        ports:            - \"8082:8082\"  # web dashboard            - \"8002:8002\"  # metrics        command:            - python            - -m            - worker_saq      faststream:        &lt;&lt;: *tasks-common        ports:            - \"8003:8003\"  # metrics        command:            - faststream            - run            - app.worker.faststream.app:faststream_app            - --workers=${FASTSTREAM_CONCURRENCY}      async-celery:        &lt;&lt;: *tasks-common        command:            - celery            - -A            - app.worker.async_celery.app:async_celery_app            - worker            - --loglevel=info            - --concurrency=${ASYNC_CELERY_CONCURRENCY}      async-celery-flower:        image: fastapi-async-tasks        ports:            - \"5555:5555\"        volumes:            - .:\/app        depends_on:            - async-celery        restart: on-failure        networks:            - default        command:            - celery            - -A            - app.worker.async_celery.app:async_celery_app            - flower   volumes:    grafana:   networks:    default:        driver: bridge<\/code><\/pre>\n<pre><code class=\"bash\">docker-compose up -d<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p><a class=\"anchor\" name=\"21\" id=\"21\"><\/a><\/p>\n<h2>\u041d\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h2>\n<p>\u0427\u0442\u043e\u0431\u044b \u043d\u0435 \u0434\u0435\u043f\u043b\u043e\u0438\u0442\u044c \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e, \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0441\u0442\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u0438\u043d\u043a\u0440\u0435\u043c\u0435\u043d\u0442\u0430 \u0447\u0438\u0441\u043b\u0430 \u0432 Redis. \u041e\u0434\u043d\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u2014 \u043e\u0434\u0438\u043d \u0438\u043d\u043a\u0440\u0435\u043c\u0435\u043d\u0442. \u0424\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <a href=\"https:\/\/grafana.com\/grafana\/plugins\/redis-datasource\/\"><u>\u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0432 Grafana<\/u><\/a>, \u044d\u0442\u043e \u043f\u043e\u0441\u043b\u0443\u0436\u0438\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0435\u043c \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u0432\u0441\u0435 \u0444\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e.<\/p>\n<p>Redis \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0431\u044b\u0441\u0442\u0440\u043e, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432 \u0441\u0440\u0430\u0432\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u043c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u044b \u0438\u043c\u0435\u043d\u043d\u043e \u0441\u0430\u043c\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 (\u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0443 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u0434\u043e\u0441\u0442\u0430\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0443 \u0438\u0437 \u043e\u0447\u0435\u0440\u0435\u0434\u0438).<\/p>\n<p>\u0412\u043e \u0432\u0441\u0435\u0445 \u0442\u0435\u0441\u0442\u0430\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f 1 \u0440\u0435\u043f\u043b\u0438\u043a\u0430 FastAPI.<\/p>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c 5 \u0440\u0430\u0437\u043d\u044b\u0445 \u044d\u043d\u0434\u043f\u043e\u0439\u043d\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u044e\u0442 \u0441\u0432\u043e\u0438 \u0444\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438:<\/p>\n<ul>\n<li>\n<p>io\/simple: \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 redis<\/p>\n<\/li>\n<li>\n<p>io\/sync: \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 redis<\/p>\n<\/li>\n<li>\n<p>io\/thread: \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 redis \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u0442\u0440\u0435\u0434\u0435 <code>ThreadPoolExecutor(max_workers=3)<\/code><\/p>\n<\/li>\n<li>\n<p>cpu\/simple: \u0447\u0438\u0441\u043b\u043e\u0434\u0440\u043e\u0431\u0438\u043b\u044c\u043d\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430 + \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 redis<\/p>\n<\/li>\n<li>\n<p>cpu\/process: \u0447\u0438\u0441\u043b\u043e\u0434\u0440\u043e\u0431\u0438\u043b\u044c\u043d\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 <code>ProcessPoolExecutor(max_workers=3)<\/code> + \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 redis<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u043b\u044f \u043d\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u043e\u0439 <a href=\"https:\/\/github.com\/locustio\/locust\"><u>Locust<\/u><\/a>.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"bash\"># locust\/master.conf<\/code><\/pre>\n<pre><code class=\"python\">headless = true  # \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043d\u0441\u043e\u043b\u044c expect-workers = 10  # \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0434\u043e\u0436\u0438\u0434\u0430\u0435\u043c\u0441\u044f \u0441\u0442\u0430\u0440\u0442\u0430 10 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 host = http:\/\/localhost:8000  # \u0430\u0434\u0440\u0435\u0441 FastAPI<\/code><\/pre>\n<pre><code># locust\/background.py<\/code><\/pre>\n<pre><code class=\"python\">from random import shuffle from locust import between, HttpUser, task     # \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f Background Task class TaskUser(HttpUser):    wait_time = between(0.001, 0.001)      @task    def execute(self):        endpoint = os.environ.get(\"endpoint\")        endpoints = (            [endpoint]            if endpoint            else [\"io\/simple\", \"io\/sync\", \"io\/thread\", \"cpu\/simple\", \"cpu\/process\"]        )        shuffle(endpoints)          for endpoint in endpoints:            self.client.post(f\"\/api\/v1\/incr\/{endpoint}\/background\", json={\"value\": 1}) <\/code><\/pre>\n<pre><code class=\"bash\"># locust\/benchmark.py<\/code><\/pre>\n<pre><code class=\"python\">from random import shuffle from locust import between, HttpUser, task     # \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a class TaskUser(HttpUser):    wait_time = between(0.001, 0.001)      @task    def execute(self):        endpoint = os.environ.get(\"endpoint\")        endpoints = (            [endpoint]            if endpoint            else [\"io\/simple\", \"io\/sync\", \"io\/thread\", \"cpu\/simple\", \"cpu\/process\"]        )          worker = os.environ.get(\"worker\")        workers = (            [worker]            if worker            else [\"arq\", \"saq\", \"faststream\", \"async-celery\"]        )          params = [(endpoint, worker) for endpoint in endpoints for worker in workers]        shuffle(params)          for endpoint, worker in params:            self.client.post(f\"\/api\/v1\/incr\/{endpoint}\/{worker}\", json={\"value\": 1})<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b:<\/p>\n<ul>\n<li>\n<p>-i: \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0439<\/p>\n<\/li>\n<li>\n<p>-u: \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430:<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"bash\">env endpoint=io\/simple locust --config locust\/master.conf -f locust\/background.py -i 10000 -u 100<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p><a class=\"anchor\" name=\"22\" id=\"22\"><\/a><\/p>\n<h2>\u0422\u0430\u0431\u043b\u0438\u0446\u044b \u0438 \u0433\u0440\u0430\u0444\u0438\u043a\u0438<\/h2>\n<p><a class=\"anchor\" name=\"23\" id=\"23\"><\/a><\/p>\n<h3>\u041a\u0430\u043a Background Task \u0432\u043b\u0438\u044f\u0435\u0442 \u043d\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c FastAPI<\/h3>\n<p># 200M memory, 0.2 cpus<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">\n<\/td>\n<td>\n<p align=\"left\">\u0418\u0442\u0435\u0440\u0430\u0446\u0438\u0438 (\u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0444\u043e\u043d\u043e\u0432\u044b\u0445 \u0437\u0430\u0434\u0430\u0447)<\/p>\n<\/td>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438<\/p>\n<\/td>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">RPS<\/p>\n<\/td>\n<td data-colwidth=\"87\" width=\"87\">\n<p align=\"left\">\u0412\u0440\u0435\u043c\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 (\u043c\u0435\u0434\u0438\u0430\u043d\u0430), \u043c\u0441<\/p>\n<\/td>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">\u0412\u0440\u0435\u043c\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 (95-\u0439 \u043f\u0435\u0440\u0446\u0435\u043d\u0442\u0438\u043b\u044c), \u043c\u0441<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0412\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0432\u0441\u0435\u0445 \u0437\u0430\u0434\u0430\u0447, \u0441\u0435\u043a<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043e\u0448\u0438\u0431\u043e\u043a (%)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">io\/simple<\/p>\n<\/td>\n<td>\n<p align=\"left\">50000<\/p>\n<\/td>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">100<\/p>\n<\/td>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">319<\/p>\n<\/td>\n<td data-colwidth=\"87\" width=\"87\">\n<p align=\"left\">200<\/p>\n<\/td>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">390<\/p>\n<\/td>\n<td>\n<p align=\"left\">160<\/p>\n<\/td>\n<td>\n<p align=\"left\">0.0<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">io\/sync<\/p>\n<\/td>\n<td>\n<p align=\"left\">50000<\/p>\n<\/td>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">100<\/p>\n<\/td>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">290 <\/p>\n<\/td>\n<td data-colwidth=\"87\" width=\"87\">\n<p align=\"left\">280 <\/p>\n<\/td>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">410 <\/p>\n<\/td>\n<td>\n<p align=\"left\">180<\/p>\n<\/td>\n<td>\n<p align=\"left\">0.0<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">io\/thread<\/p>\n<\/td>\n<td>\n<p align=\"left\">50000<\/p>\n<\/td>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">100<\/p>\n<\/td>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">209 <\/p>\n<\/td>\n<td data-colwidth=\"87\" width=\"87\">\n<p align=\"left\">400<\/p>\n<\/td>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">600 <\/p>\n<\/td>\n<td>\n<p align=\"left\">245<\/p>\n<\/td>\n<td>\n<p align=\"left\">0.0<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">cpu\/simple<\/p>\n<\/td>\n<td>\n<p align=\"left\">100 <\/p>\n<\/td>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">3<\/p>\n<\/td>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">0.4 <\/p>\n<\/td>\n<td data-colwidth=\"87\" width=\"87\">\n<p align=\"left\">7300 <\/p>\n<\/td>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">9600<\/p>\n<\/td>\n<td>\n<p align=\"left\">245<\/p>\n<\/td>\n<td>\n<p align=\"left\">0.0<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">cpu\/process<\/p>\n<\/td>\n<td>\n<p align=\"left\">1000<\/p>\n<\/td>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">3<\/p>\n<\/td>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">16<\/p>\n<\/td>\n<td data-colwidth=\"87\" width=\"87\">\n<p align=\"left\">190<\/p>\n<\/td>\n<td data-colwidth=\"88\" width=\"88\">\n<p align=\"left\">400<\/p>\n<\/td>\n<td>\n<p align=\"left\"> 2460<\/p>\n<\/td>\n<td>\n<p align=\"left\">0.0<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p># io\/simple (\u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 redis)<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/e10\/03c\/59b\/e1003c59beb8f59e4a4797634ee7bb0c.png\" width=\"1600\" height=\"386\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/e10\/03c\/59b\/e1003c59beb8f59e4a4797634ee7bb0c.png\"\/><\/figure>\n<p>\u0424\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u043f\u0440\u043e\u0441\u0442\u044b\u0435 \u0438 \u0431\u044b\u0441\u0442\u0440\u044b\u0435, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u0438\u0434\u0438\u043c \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e \u0445\u043e\u0440\u043e\u0448\u0438\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438 RPS.<\/p>\n<p># io\/sync (\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 redis)<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/4d2\/8b1\/a4d\/4d28b1a4d6e4c3935c19d7c138f43e7b.png\" width=\"1600\" height=\"395\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/4d2\/8b1\/a4d\/4d28b1a4d6e4c3935c19d7c138f43e7b.png\"\/><\/figure>\n<p>\u0421\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043a\u043e\u0434: \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 RPS \u043c\u0435\u043d\u044c\u0448\u0435, \u0430 \u0432\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447 \u0431\u043e\u043b\u044c\u0448\u0435. \u0427\u0435\u043c \u0434\u043e\u043b\u044c\u0448\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043a\u043e\u0434, \u0442\u0435\u043c \u0445\u0443\u0436\u0435 \u0431\u0443\u0434\u0443\u0442 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c\u0441\u044f \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438.<\/p>\n<p># io\/thread (\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 redis \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u0442\u0440\u0435\u0434\u0435)<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/d8d\/e43\/e5b\/d8de43e5b0b33252ac18a39ca81d4368.png\" width=\"1600\" height=\"374\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d8d\/e43\/e5b\/d8de43e5b0b33252ac18a39ca81d4368.png\"\/><\/figure>\n<p>\u041d\u0435 \u0441\u0442\u043e\u0438\u0442 \u0437\u043b\u043e\u0443\u043f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u0442\u044c \u044d\u0442\u0438\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u043c, \u0442.\u043a. \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a\u043e\u0434\u0430 \u0432 \u0434\u0440\u0443\u0433\u043e\u043c \u0442\u0440\u0435\u0434\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043d\u0430\u043a\u043b\u0430\u0434\u043d\u044b\u0445 \u0440\u0430\u0441\u0445\u043e\u0434\u043e\u0432, \u0447\u0442\u043e \u043c\u044b \u0438 \u0432\u0438\u0434\u0438\u043c \u043f\u043e \u0442\u0430\u0431\u043b\u0438\u0446\u0435 \u0438 \u043d\u0430 \u0433\u0440\u0430\u0444\u0438\u043a\u0430\u0445: \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438 \u0437\u0430\u043c\u0435\u0442\u043d\u043e \u0445\u0443\u0436\u0435, \u0447\u0435\u043c \u0432 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0435. \u041f\u0440\u0438 \u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447\u0430\u0445 \u043c\u043e\u0436\u0435\u0442 \u0441\u0438\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0435\u0434\u0430\u0442\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c.<\/p>\n<p># cpu\/simple (\u0447\u0438\u0441\u043b\u043e\u0434\u0440\u043e\u0431\u0438\u043b\u044c\u043d\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430 + \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 redis)<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a46\/b03\/5f8\/a46b035f8ebc334c85251f67d9eafc45.png\" width=\"1600\" height=\"583\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a46\/b03\/5f8\/a46b035f8ebc334c85251f67d9eafc45.png\"\/><\/figure>\n<p>FastAPI \u0431\u044b\u043b \u043d\u0430\u0441\u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d, \u0447\u0442\u043e \u043d\u0435 \u0432\u0441\u0435\u0433\u0434\u0430 \u0443\u0441\u043f\u0435\u0432\u0430\u043b \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u0440\u0438\u043a\u0438. \u0412\u044b\u0432\u043e\u0434 \u043e\u0447\u0435\u0432\u0438\u0434\u0435\u043d: CPU-bound \u0444\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u044b\u043d\u043e\u0441\u0438\u0442\u044c \u0432 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u0435\u0440\u0432\u0438\u0441.<\/p>\n<p># cpu\/process (\u0447\u0438\u0441\u043b\u043e\u0434\u0440\u043e\u0431\u0438\u043b\u044c\u043d\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 + \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 redis)<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/2d1\/d9a\/e83\/2d1d9ae83ecec619ecddb2d33cdb40c3.png\" width=\"1600\" height=\"582\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/2d1\/d9a\/e83\/2d1d9ae83ecec619ecddb2d33cdb40c3.png\"\/><\/figure>\n<p>\u0421 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f Locust \u0431\u044b\u043b\u043e \u043e\u043a\u043e\u043b\u043e 16 RPS, \u043d\u043e \u0434\u043b\u044f FastAPI \u0437\u0430\u043f\u0440\u043e\u0441 \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044f \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0441\u043b\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0444\u043e\u043d\u043e\u0432\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438. \u0418\u043c\u0435\u043d\u043d\u043e \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432 Grafana \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 RPS \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043e\u043a\u043e\u043b\u043e \u043d\u0443\u043b\u044f, \u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>Total requests<\/code> \u0440\u0430\u0441\u0442\u0451\u0442 \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 <code>Executed tasks<\/code>. \u0412\u044b\u0432\u043e\u0434: BackgroundTask \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0435 \u0442\u0430\u043a\u0438\u0445 \u0444\u043e\u043d\u043e\u0432\u044b\u0445 \u0437\u0430\u0434\u0430\u0447.<\/p>\n<p><a class=\"anchor\" name=\"24\" id=\"24\"><\/a><\/p>\n<h3>\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438 FastAPI \u043f\u0440\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 \u0444\u043e\u043d\u043e\u0432\u044b\u0445 \u0437\u0430\u0434\u0430\u0447 \u0432 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u0435\u0440\u0432\u0438\u0441<\/h3>\n<p>\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c \u044d\u043d\u0434\u043f\u043e\u0439\u043d\u0442 io\/simple, 50000 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0439, 100 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043d\u0435 \u0443\u0449\u0435\u043c\u043b\u044f\u0442\u044c FastStream, \u0434\u043b\u044f \u0447\u0438\u0441\u0442\u043e\u0442\u044b \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430 ARQ, SAQ \u0438 Celery \u0431\u0443\u0434\u0443\u0442 \u0438\u043c\u0435\u0442\u044c concurrency=1.<\/p>\n<p># 200M memory, 0.2 cpus<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td data-colwidth=\"175\" width=\"175\">\n<p align=\"left\">\n<\/td>\n<td data-colwidth=\"175\" width=\"175\">\n<p align=\"left\">RPS<\/p>\n<\/td>\n<td data-colwidth=\"176\" width=\"176\">\n<p align=\"left\">\u0412\u0440\u0435\u043c\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 (\u043c\u0435\u0434\u0438\u0430\u043d\u0430), \u043c\u0441<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0412\u0440\u0435\u043c\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 (95-\u0439 \u043f\u0435\u0440\u0446\u0435\u043d\u0442\u0438\u043b\u044c), \u043c\u0441<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"175\" width=\"175\">\n<p align=\"left\">ARQ<\/p>\n<\/td>\n<td data-colwidth=\"175\" width=\"175\">\n<p align=\"left\">227<\/p>\n<\/td>\n<td data-colwidth=\"176\" width=\"176\">\n<p align=\"left\">380<\/p>\n<\/td>\n<td>\n<p align=\"left\">590<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"175\" width=\"175\">\n<p align=\"left\">SAQ*<\/p>\n<\/td>\n<td data-colwidth=\"175\" width=\"175\">\n<p align=\"left\">162*<\/p>\n<\/td>\n<td data-colwidth=\"176\" width=\"176\">\n<p align=\"left\">290*<\/p>\n<\/td>\n<td>\n<p align=\"left\">500*<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"175\" width=\"175\">\n<p align=\"left\">FastStream<\/p>\n<\/td>\n<td data-colwidth=\"175\" width=\"175\">\n<p align=\"left\">262<\/p>\n<\/td>\n<td data-colwidth=\"176\" width=\"176\">\n<p align=\"left\">300<\/p>\n<\/td>\n<td>\n<p align=\"left\">490<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"175\" width=\"175\">\n<p align=\"left\">Celery<\/p>\n<\/td>\n<td data-colwidth=\"175\" width=\"175\">\n<p align=\"left\">151<\/p>\n<\/td>\n<td data-colwidth=\"176\" width=\"176\">\n<p align=\"left\">590<\/p>\n<\/td>\n<td>\n<p align=\"left\">880<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p><a class=\"anchor\" name=\"25\" id=\"25\"><\/a><\/p>\n<h4>ARQ<\/h4>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/553\/b3d\/6b9\/553b3d6b9797a148b6eefba9d596d0e7.png\" width=\"1600\" height=\"696\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/553\/b3d\/6b9\/553b3d6b9797a148b6eefba9d596d0e7.png\"\/><\/figure>\n<p>\u0425\u043e\u0440\u043e\u0448\u0438\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438, \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430. \u0420\u0430\u0437\u043d\u0438\u0446\u0430 \u043c\u0435\u0436\u0434\u0443 \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0435\u0439 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0430\u044f.<\/p>\n<p><a class=\"anchor\" name=\"26\" id=\"26\"><\/a><\/p>\n<h4>SAQ<\/h4>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/9dc\/fa6\/c69\/9dcfa6c6946b8546344a1feabb987a9c.png\" width=\"1600\" height=\"695\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/9dc\/fa6\/c69\/9dcfa6c6946b8546344a1feabb987a9c.png\"\/><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/696\/d30\/a81\/696d30a81c8995bedfceba7d5df8eed6.png\" width=\"1600\" height=\"463\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/696\/d30\/a81\/696d30a81c8995bedfceba7d5df8eed6.png\"\/><\/figure>\n<p>\u0422\u0443\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043d\u0435\u043e\u0436\u0438\u0434\u0430\u043d\u043d\u044b\u0439.\u00a0<\/p>\n<ul>\n<li>\n<p>\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0437\u0430\u0434\u0430\u0447\u0438 \u0431\u043e\u0434\u0440\u043e \u0441\u0442\u0430\u0432\u0438\u043b\u0438\u0441\u044c \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u044b \u0431\u044b\u043b\u0430 \u0447\u0443\u0442\u044c \u0432\u044b\u0448\u0435, \u0447\u0435\u043c \u0443 ARQ<\/p>\n<\/li>\n<li>\n<p>\u041d\u043e \u0432\u0441\u043a\u043e\u0440\u0435 \u0441\u0435\u0440\u0432\u0438\u0441 \u0441\u0442\u0430\u043b \u0434\u0435\u0433\u0440\u0430\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0430 \u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u043d\u0435 \u0441\u0442\u0430\u0432\u0438\u043b\u0438\u0441\u044c \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c<\/p>\n<\/li>\n<li>\n<p>\u0412 \u0438\u0442\u043e\u0433\u0435 web-\u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u043b \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c<\/p>\n<\/li>\n<\/ul>\n<p>\u0423\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0432 2 \u0440\u0430\u0437\u0430 \u0440\u0435\u0448\u0438\u043b\u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443, \u043d\u043e \u043d\u0435\u043d\u0430\u0434\u043e\u043b\u0433\u043e: \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0439 \u0432 2 \u0440\u0430\u0437\u0430, \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0432\u0435\u0440\u043d\u0443\u043b\u0430\u0441\u044c. \u041f\u0440\u043e\u0439\u0442\u0438 \u0442\u0435\u0441\u0442 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043b\u0438\u0448\u044c \u043f\u043e\u0441\u043b\u0435 \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u0438\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f \u043c\u0435\u0436\u0434\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438.<\/p>\n<p><a class=\"anchor\" name=\"27\" id=\"27\"><\/a><\/p>\n<h4>FastStream<\/h4>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f9f\/fe6\/f2e\/f9ffe6f2ebbb536d11242c80828c4a74.png\" width=\"1600\" height=\"694\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f9f\/fe6\/f2e\/f9ffe6f2ebbb536d11242c80828c4a74.png\"\/><\/figure>\n<p>\u041b\u0443\u0447\u0448\u0438\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438 \u0441\u0440\u0435\u0434\u0438 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a. \u0424\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u043b\u0438\u0441\u044c \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u043b\u0435 \u043f\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c.<\/p>\n<p><a class=\"anchor\" name=\"28\" id=\"28\"><\/a><\/p>\n<h4>Celery<\/h4>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/9d9\/8f7\/89f\/9d98f789f5b61cfc71a69318ace7f467.png\" width=\"1600\" height=\"690\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/9d9\/8f7\/89f\/9d98f789f5b61cfc71a69318ace7f467.png\"\/><\/figure>\n<p>\u041f\u043e\u043a\u0430\u0437\u0430\u043b \u0441\u0430\u043c\u044b\u0435 \u0441\u043a\u0440\u043e\u043c\u043d\u044b\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b. \u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043b\u0430\u0441\u044c \u0441\u0438\u043b\u044c\u043d\u043e \u043f\u043e\u0437\u0436\u0435 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0438.<\/p>\n<p><a class=\"anchor\" name=\"29\" id=\"29\"><\/a><\/p>\n<h4>\u0421\u0440\u0430\u0432\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447<\/h4>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/4c6\/691\/32a\/4c669132a9328902edab277bef380f77.png\" width=\"961\" height=\"834\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/4c6\/691\/32a\/4c669132a9328902edab277bef380f77.png\"\/><\/figure>\n<p><a class=\"anchor\" name=\"30\" id=\"30\"><\/a><\/p>\n<h4>\u0421\u0440\u0430\u0432\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u0441\u043b\u0435 \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u0438\u044f \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432<\/h4>\n<p># 500M memory, 0.5 cpus<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td data-colwidth=\"175\" width=\"175\">\n<p align=\"left\">\n<\/td>\n<td>\n<p align=\"left\">RPS<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0412\u0440\u0435\u043c\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 (\u043c\u0435\u0434\u0438\u0430\u043d\u0430), \u043c\u0441<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0412\u0440\u0435\u043c\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 (95-\u0439 \u043f\u0435\u0440\u0446\u0435\u043d\u0442\u0438\u043b\u044c), \u043c\u0441<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"175\" width=\"175\">\n<p align=\"left\">ARQ<\/p>\n<\/td>\n<td>\n<p align=\"left\">1114<\/p>\n<\/td>\n<td>\n<p align=\"left\">18<\/p>\n<\/td>\n<td>\n<p align=\"left\">34<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"175\" width=\"175\">\n<p align=\"left\">SAQ<\/p>\n<\/td>\n<td>\n<p align=\"left\">1079<\/p>\n<\/td>\n<td>\n<p align=\"left\">19<\/p>\n<\/td>\n<td>\n<p align=\"left\">37<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"175\" width=\"175\">\n<p align=\"left\">FastStream<\/p>\n<\/td>\n<td>\n<p align=\"left\">1178<\/p>\n<\/td>\n<td>\n<p align=\"left\">15<\/p>\n<\/td>\n<td>\n<p align=\"left\">31<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"175\" width=\"175\">\n<p align=\"left\">Celery<\/p>\n<\/td>\n<td>\n<p align=\"left\">871<\/p>\n<\/td>\n<td>\n<p align=\"left\">30<\/p>\n<\/td>\n<td>\n<p align=\"left\"> 59<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/851\/98b\/415\/85198b41570ead3c538e100745c66ff7.png\" width=\"960\" height=\"794\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/851\/98b\/415\/85198b41570ead3c538e100745c66ff7.png\"\/><\/figure>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u0438\u043c, \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0433\u043b\u0430\u0434\u0438\u043b\u043e \u043a\u0430\u0440\u0442\u0438\u043d\u0443 \u0434\u043b\u044f ARQ, SAQ \u0438 FastStream. \u0423 Celery \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u043e \u0441\u043a\u0440\u043e\u043c\u043d\u0435\u0435 \u2014 \u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0441\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.<\/p>\n<p><strong>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435,<\/strong> \u0447\u0442\u043e \u0432 Celery \u0432\u043d\u0443\u0442\u0440\u0438 \u0444\u043e\u043d\u043e\u0432\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u043d\u0435\u043b\u044c\u0437\u044f \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0432 ProcessPoolExecutor, \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u043e\u0448\u0438\u0431\u043a\u0443 AssertionError (&#8216;daemonic processes are not allowed to have children&#8217;).<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/6e6\/c5a\/d64\/6e6c5ad642c80be59012691e1fc84eb5.png\" width=\"956\" height=\"792\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/6e6\/c5a\/d64\/6e6c5ad642c80be59012691e1fc84eb5.png\"\/><\/figure>\n<p><a class=\"anchor\" name=\"31\" id=\"31\"><\/a><\/p>\n<h2>\u0418\u0442\u043e\u0433\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/h2>\n<ul>\n<li>\n<p>BackgroundTasks \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u043b\u0438\u0431\u043e \u0434\u043b\u044f \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u0445 I\/O-bound \u0437\u0430\u0434\u0430\u0447. \u0412 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u043b\u0443\u0447\u0448\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p>FastStream \u043e\u043a\u0430\u0437\u0430\u043b\u0441\u044f \u0441\u0430\u043c\u044b\u043c \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u043d\u044b\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c \u0438 \u0438\u043c\u0435\u0435\u0442 \u043e\u0447\u0435\u043d\u044c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430.<\/p>\n<\/li>\n<li>\n<p>Celery \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u043e \u043f\u0440\u043e\u0438\u0433\u0440\u044b\u0432\u0430\u0435\u0442 \u0432 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438, \u043d\u043e \u0437\u0430 \u0441\u0447\u0451\u0442 \u0448\u0438\u0440\u043e\u043a\u043e\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430 \u043c\u043e\u0436\u0435\u0442 \u0441\u0442\u0430\u0442\u044c \u043d\u0435\u043f\u043b\u043e\u0445\u0438\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u043c. \u041e\u0436\u0438\u0434\u0430\u0435\u043c \u0448\u0435\u0441\u0442\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e.<\/p>\n<\/li>\n<li>\n<p>ARQ \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u044b\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c, \u043d\u043e \u043d\u0438\u0447\u0435\u043c \u043e\u0441\u043e\u0431\u044b\u043c \u043d\u0435 \u0432\u044b\u0434\u0435\u043b\u0438\u043b\u0441\u044f. \u041f\u0430\u0440\u0443 \u0440\u0430\u0437 \u0432\u043e\u0440\u043a\u0435\u0440 \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u0432\u0430\u043b \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c, \u0445\u043e\u0442\u044f \u0437\u0430\u0434\u0430\u0447\u0438 \u0441\u0442\u0430\u0432\u0438\u043b\u0438\u0441\u044c \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c. \u041b\u0435\u0447\u0438\u043b\u043e\u0441\u044c \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u043e\u043c \u0432\u043e\u0440\u043a\u0435\u0440\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u044d\u0442\u043e \u043f\u043e\u043f\u0440\u0430\u0432\u0438\u043b\u0438 \u0432 \u043d\u043e\u0432\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438.<\/p>\n<\/li>\n<li>\n<p>SAQ \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0430\u043d\u0430\u043b\u043e\u0433\u043e\u043c ARQ \u0441\u043e \u0441\u0432\u043e\u0438\u043c\u0438 \u043f\u043b\u044e\u0441\u0430\u043c\u0438 \u0438 \u043c\u0438\u043d\u0443\u0441\u0430\u043c\u0438. \u0417\u0430\u0432\u0438\u0441\u0430\u0435\u0442 \u043f\u0440\u0438 \u0434\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0435.<\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"32\" id=\"32\"><\/a><\/p>\n<h2>\u0412\u044b\u0432\u043e\u0434\u044b<\/h2>\n<ul>\n<li>\n<p><strong>\u0415\u0441\u043b\u0438 \u0432 \u043a\u043e\u0434\u0435 \u043c\u043d\u043e\u0433\u043e CPU-bound-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439<\/strong>, \u0438\u0445 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441<\/p>\n<\/li>\n<li>\n<p><strong>\u0415\u0441\u043b\u0438 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0430\u044f, \u0430 \u0444\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u2014 \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c I\/O-bound \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438<\/strong>, \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b FastAPI Background Tasks. \u041d\u043e \u0434\u043b\u044f \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u043b\u0443\u0447\u0448\u0435 \u0438\u0445 \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u0414\u0440\u0443\u0433\u0438\u0435 \u043d\u0430\u0448\u0438 \u0441\u0442\u0430\u0442\u044c\u0438 \u043f\u043e \u0431\u044d\u043a\u0435\u043d\u0434\u0443 \u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u043c\u0443 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u0434\u043b\u044f \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0449\u0438\u0445:<\/strong><\/p>\n<ul>\n<li>\n<p>\u0426\u0438\u043a\u043b \u0441\u0442\u0430\u0442\u0435\u0439 \u00ab\u041f\u0435\u0440\u0432\u044b\u0435 \u0448\u0430\u0433\u0438 \u0432 aiohttp\u00bb:\u00a0<a href=\"https:\/\/habr.com\/ru\/companies\/kts\/articles\/560058\/\">\u043f\u0438\u0448\u0435\u043c \u043f\u0435\u0440\u0432\u043e\u0435 hello-world-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/a>,\u00a0<a href=\"https:\/\/habr.com\/ru\/companies\/kts\/articles\/579214\/\">\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445<\/a>,\u00a0<a href=\"https:\/\/habr.com\/ru\/companies\/kts\/articles\/580202\/\">\u0432\u044b\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442 \u0432 \u0418\u043d\u0442\u0435\u0440\u043d\u0435\u0442<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/companies\/kts\/articles\/727528\/\">\u0412\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f 5 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u0432 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438 \u043d\u0430 Python<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/companies\/kts\/articles\/713084\/\">\u0420\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u043c\u0441\u044f \u0432 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0441\u0442\u0438: \u0433\u0434\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u043e, \u0430 \u0433\u0434\u0435 \u2014 \u043d\u0435\u0442?<\/a><\/p>\n<\/li>\n<\/ul>\n<p><strong>\u0414\u0440\u0443\u0433\u0438\u0435 \u043d\u0430\u0448\u0438 \u0441\u0442\u0430\u0442\u044c\u0438 \u043f\u043e \u0431\u044d\u043a\u0435\u043d\u0434\u0443 \u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u043c\u0443 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u043e\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f:<\/strong><\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/companies\/kts\/articles\/583036\/\">\u041f\u0438\u0448\u0435\u043c \u0441\u0432\u043e\u0439 Google, \u0438\u043b\u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043a\u0440\u0430\u0443\u043b\u0435\u0440 \u0441 rate limits \u043d\u0430 Python<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/companies\/kts\/articles\/598575\/\">\u041f\u0438\u0448\u0435\u043c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u0422\u0435\u043b\u0435\u0433\u0440\u0430\u043c-\u0431\u043e\u0442\u0430<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/companies\/kts\/articles\/659341\/\">\u041f\u0438\u0448\u0435\u043c Websocket-\u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043b\u044f \u0433\u0435\u043e\u043b\u043e\u043a\u0430\u0446\u0438\u0438 \u043d\u0430 asyncio<\/a><\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/816757\/\"> https:\/\/habr.com\/ru\/articles\/816757\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><\/figure>\n<p>\u041f\u0440\u0438\u0432\u0435\u0442! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0418\u0432\u0430\u043d, \u044f \u0431\u044d\u043a\u0435\u043d\u0434-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432 <a href=\"https:\/\/kts.tech\">KTS.<\/a><\/p>\n<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u0432 FastAPI \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0444\u043e\u043d\u043e\u0432\u044b\u043c\u0438 \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0438\u0445 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433 \u0432 Prometheus.<\/p>\n<p>\u0412 \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0430\u0445 \u0434\u043b\u044f \u0444\u043e\u043d\u043e\u0432\u044b\u0445 \u0437\u0430\u0434\u0430\u0447 \u0432 FastAPI \u043e\u0431\u044b\u0447\u043d\u043e \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e\u0442 celery \u0438 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 <u>\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439<\/u> \u043a\u043e\u0434. \u041d\u043e \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0439 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u0442\u0430\u043a\u043e\u0435 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0435\u0442\u0441\u044f \u0440\u0435\u0434\u043a\u043e, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u043e\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u0432 \u0444\u043e\u043d\u043e\u0432\u044b\u0445 \u0437\u0430\u0434\u0430\u0447\u0430\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <u>\u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439<\/u> \u043a\u043e\u0434.<\/p>\n<p>\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u043e\u043f\u0438\u0448\u0443 5 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432: \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 \u0432 FastAPI Background Tasks \u0438 4 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u2014 ARQ, SAQ, FastStream, \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043a \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u043c\u0443 \u043a\u043e\u0434\u0443 Celery. <\/p>\n<p>\u0412 \u043a\u043e\u043d\u0446\u0435 \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u0442\u044c \u0444\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438.<\/p>\n<p><strong>\u041e\u0433\u043b\u0430\u0432\u043b\u0435\u043d\u0438\u0435<\/strong><\/p>\n<ul>\n<li>\n<p><a href=\"#1\">\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 \u0444\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#2\">\u041f\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#3\">\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#4\">FasAPI Background Tasks<\/a><\/p>\n<ul>\n<li>\n<p><a href=\"#5\">\u041f\u043b\u044e\u0441\u044b<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#6\">\u041c\u0438\u043d\u0443\u0441\u044b<\/a><\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><a href=\"#7\">ARQ<\/a><\/p>\n<ul>\n<li>\n<p><a href=\"#8\">\u041f\u043b\u044e\u0441\u044b<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#9\">\u041c\u0438\u043d\u0443\u0441\u044b<\/a><\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><a href=\"#10\">SAQ<\/a><\/p>\n<ul>\n<li>\n<p><a href=\"#11\">\u041f\u043b\u044e\u0441\u044b<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#12\">\u041c\u0438\u043d\u0443\u0441\u044b<\/a><\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><a href=\"#13\">FastStream<\/a><\/p>\n<ul>\n<li>\n<p><a href=\"#14\">\u041f\u043b\u044e\u0441\u044b<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#15\">\u041c\u0438\u043d\u0443\u0441\u044b<\/a><\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><a href=\"#16\">Celery<\/a><\/p>\n<ul>\n<li>\n<p><a href=\"#17\">\u041f\u043b\u044e\u0441\u044b<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#18\">\u041c\u0438\u043d\u0443\u0441\u044b<\/a><\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><a href=\"#19\">\u041a\u0430\u043a\u0438\u0435 \u0435\u0449\u0451 \u0435\u0441\u0442\u044c \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0444\u043e\u043d\u043e\u0432\u044b\u043c\u0438 \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#20\">\u041c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433 \u0444\u043e\u043d\u043e\u0432\u044b\u0445 \u0437\u0430\u0434\u0430\u0447<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#21\">\u041d\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#22\">\u0422\u0430\u0431\u043b\u0438\u0446\u044b \u0438 \u0433\u0440\u0430\u0444\u0438\u043a\u0438<\/a><\/p>\n<ul>\n<li>\n<p><a href=\"#23\">\u041a\u0430\u043a Background Task \u0432\u043b\u0438\u044f\u0435\u0442 \u043d\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c FastAPI<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#24\">\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438 FastAPI \u043f\u0440\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 \u0444\u043e\u043d\u043e\u0432\u044b\u0445 \u0437\u0430\u0434\u0430\u0447 \u0432 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u0435\u0440\u0432\u0438\u0441<\/a><\/p>\n<ul>\n<li>\n<p><a href=\"#25\">ARQ<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#26\">SAQ<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#27\">FastStream<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#28\">Celery<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#29\">\u0421\u0440\u0430\u0432\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#30\">\u0421\u0440\u0430\u0432\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u0441\u043b\u0435 \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u0438\u044f \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432<\/a><\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><a href=\"#31\">\u0418\u0442\u043e\u0433\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#32\">\u0412\u044b\u0432\u043e\u0434\u044b<\/a><\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"1\" id=\"1\"><\/a><\/p>\n<h2>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 \u0444\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438<\/h2>\n<p>\u0414\u043b\u044f \u0443\u0441\u043a\u043e\u0440\u0435\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0447\u0430\u0441\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0438 \u0447\u0430\u0441\u0442\u043e \u0432\u044b\u043d\u043e\u0441\u0438\u0442\u0441\u044f \u0432 \u0444\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438:\u00a0<\/p>\n<ul>\n<li>\n<p>\u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u043f\u043e <a href=\"https:\/\/crontab.guru\"><u>cron<\/u><\/a><\/p>\n<\/li>\n<li>\n<p>\u0437\u0430\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 (\u043e\u0442\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435) \u0437\u0430\u0434\u0430\u0447\u0438<\/p>\n<\/li>\n<li>\n<p>\u0442\u044f\u0436\u0451\u043b\u044b\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b:<\/p>\n<ul>\n<li>\n<p>\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043a \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u043c \u2014 CPU-bound<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u2014 \u043a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, I\/O-bound<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440\u044b:<\/p>\n<ul>\n<li>\n<p><strong>\u041e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u043f\u0438\u0441\u0435\u043c \u043d\u0430 \u043f\u043e\u0447\u0442\u0443.<\/strong> \u041c\u043e\u0436\u043d\u043e \u0441\u0440\u0430\u0437\u0443 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043e\u0442\u0432\u0435\u0442 \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0443 \u043f\u0438\u0441\u044c\u043c\u0430 \u0432 \u0444\u043e\u043d\u0435. \u042d\u0442\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043d\u044f\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u043a\u0443\u043d\u0434, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043b\u0443\u0447\u0448\u0435 \u043d\u0435 \u0437\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0436\u0434\u0430\u0442\u044c, \u043f\u043e\u043a\u0430 \u043f\u0438\u0441\u044c\u043c\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0441\u044f<\/p>\n<\/li>\n<li>\n<p><strong>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445.<\/strong> \u041f\u0440\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u043e\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043e\u0442\u0432\u0435\u0442 &#171;Accepted&#187; (HTTP 202) \u0438 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0444\u0430\u0439\u043b\u043e\u043c \u0432 \u0444\u043e\u043d\u043e\u0432\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 I\/O-bound \u0438 CPU-bound \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438<\/summary>\n<div class=\"spoiler__content\">\n<p><strong>I\/O-bound \u2014 <\/strong>\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f, \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0430 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c\u044e \u043f\u043e\u0434\u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0432\u0432\u043e\u0434\u0430-\u0432\u044b\u0432\u043e\u0434\u0430. \u041a \u0442\u0430\u043a\u0438\u043c \u0437\u0430\u0434\u0430\u0447\u0430\u043c \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043d\u0435\u0441\u0442\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043f\u043e \u0441\u0435\u0442\u0438, \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0441 \u0431\u0430\u0437\u043e\u0439, \u0447\u0442\u0435\u043d\u0438\u0435\/\u0437\u0430\u043f\u0438\u0441\u044c \u0444\u0430\u0439\u043b\u0430 \u043d\u0430 \u0434\u0438\u0441\u043a.\u00a0<\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, HTTP-\u0437\u0430\u043f\u0440\u043e\u0441:<\/p>\n<p>r = requests.get(&#8216;<a href=\"https:\/\/api.github.com\/user\">https:\/\/api.github.com\/user<\/a>&#8216;, auth=(&#8216;user&#8217;, &#8216;pass&#8217;))<\/p>\n<p><strong>CPU-bound<\/strong> \u2014 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f, \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0430 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c\u044e CPU. \u0412 web-\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445 \u044d\u0442\u043e \u043e\u0431\u044b\u0447\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u043c\u0435\u0434\u0438\u0430: \u043e\u0431\u0440\u0435\u0437\u043a\u0430, \u0440\u0435\u0441\u0430\u0439\u0437, \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u044f \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a.<\/p>\n<\/div>\n<\/details>\n<p><a class=\"anchor\" name=\"2\" id=\"2\"><\/a><\/p>\n<h2>\u041f\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0443 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435, \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0431\u0440\u043e\u043a\u0435\u0440 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u2014 \u043c\u044b \u0432\u043e\u0437\u044c\u043c\u0451\u043c Redis, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043e\u043d \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0447\u0435\u0442\u044b\u0440\u0451\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a.<\/p>\n<p>\u0415\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0444\u043e\u043d\u043e\u0432\u044b\u0445 \u0437\u0430\u0434\u0430\u0447, \u043d\u043e \u0434\u043b\u044f \u0443\u043d\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u043c <a href=\"https:\/\/www.starlette.io\/background\/\"><u>\u0441 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0435\u0439 \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u0430 BackgroundTask \u0432 JSONResponse<\/u><\/a>:<\/p>\n<pre><code class=\"python\">@router.post(\"\") async def simple() -> JSONResponse:    task = BackgroundTask(func, *args, **kwargs)    return JSONResponse({\"status\": \"ok\"}, background=task)<\/code><\/pre>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 func \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0446\u0435\u043b\u0435\u0432\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e (\u0434\u043b\u044f BackgroundTask), \u043b\u0438\u0431\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442 \u0437\u0430\u0434\u0430\u0447\u0443 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435.<\/p>\n<p><a class=\"anchor\" name=\"3\" id=\"3\"><\/a><\/p>\n<p>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0442\u0443\u0442: <a href=\"https:\/\/github.com\/ipakeev\/fastapi-async-tasks\">https:\/\/github.com\/ipakeev\/fastapi-async-tasks<\/a><\/p>\n<p>\u0412 \u043d\u0430\u0448\u0435\u0439 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u044f Store, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0435\u0434\u0438\u043d\u043e\u0439 \u0442\u043e\u0447\u043a\u043e\u0439 \u0432\u0445\u043e\u0434\u0430 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0430\u043a\u0441\u0435\u0441\u0441\u043e\u0440\u043e\u0432. \u041f\u0440\u043e\u0435\u043a\u0442 \u0438\u043c\u0435\u0435\u0442 \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u0430\u043a\u0443\u044e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from contextlib import asynccontextmanager from typing import Annotated   from fastapi import Depends     class BaseAccessor:    def __init__(self, store: \"Store\") -> None:        self.store = store        self.config = settings      async def connect(self) -> None:        pass      async def disconnect(self) -> None:        pass     class Store:    def __init__(self) -> None:        # \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0446\u0438\u043a\u043b\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0438\u043c\u043f\u043e\u0440\u0442        from app.core.accessor import CoreAccessor        from app.redis.accessor import RedisAccessor        from app.worker.accessor import WorkerAccessor          self.core = CoreAccessor(self)        self.redis = RedisAccessor(self)        self.worker = WorkerAccessor(self)      async def connect(self) -> None:        await self.redis.connect()        await self.worker.connect()        await self.core.connect()      async def disconnect(self) -> None:        await self.core.disconnect()        await self.worker.disconnect()        await self.redis.disconnect()     _store: Store | None = None     def get_store() -> Store:    assert _store, \"Store is not initialized\"    return _store     async def connect_to_store() -> Store:    global _store      if not _store:        _store = Store()        await _store.connect()      return _store     async def disconnect_from_store() -> None:    global _store      if _store:        await _store.disconnect()        _store = None     @asynccontextmanager async def store_lifespan() -> AsyncGenerator[Store, None]:    await connect_to_store()    try:        yield get_store()    finally:        await disconnect_from_store()     StoreDep = Annotated[Store, Depends(get_store)] <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0414\u043b\u044f \u0434\u0435\u043a\u043b\u0430\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <a href=\"https:\/\/github.com\/ets-labs\/python-dependency-injector\"><u>dependency-injector<\/u><\/a>.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0432\u0441\u0435 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0444\u043e\u043d\u043e\u0432\u044b\u043c \u0437\u0430\u0434\u0430\u0447\u0438 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430.<\/p>\n<p><a class=\"anchor\" name=\"4\" id=\"4\"><\/a><\/p>\n<h3>FastAPI Background Tasks<\/h3>\n<p><a href=\"https:\/\/fastapi.tiangolo.com\/tutorial\/background-tasks\/\"><em>\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 GitHub<\/em><\/a><\/p>\n<p>\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 \u0432 FastAPI \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0444\u043e\u043d\u043e\u0432\u044b\u0445 \u0437\u0430\u0434\u0430\u0447. \u041e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f <em>\u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u043b\u0435<\/em> \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u0435\u0440\u043d\u0443\u043b \u043e\u0442\u0432\u0435\u0442. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u043d\u0435\u0442 \u043d\u0438\u043a\u0430\u043a\u043e\u0439 \u043e\u0447\u0435\u0440\u0435\u0434\u0438, \u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043d\u0438\u0433\u0434\u0435 \u043d\u0435 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f.<\/p>\n<pre><code class=\"python\">from starlette.background import BackgroundTask from starlette.responses import JSONResponse     # \u0444\u043e\u043d\u043e\u0432\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 async def send_email(email: str, message: str = \"Hello\") -> None:    ...     @router.post(\"\") async def hello() -> JSONResponse:    task = BackgroundTask(send_email, \"support@mail.ru\", message=\"Hello\")    return JSONResponse({\"status\": \"ok\"}, background=task) <\/code><\/pre>\n<p><a class=\"anchor\" name=\"5\" id=\"5\"><\/a><\/p>\n<h4>\u041f\u043b\u044e\u0441\u044b<\/h4>\n<p>&#8212; \u0423\u0434\u043e\u0431\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c<\/p>\n<p>&#8212; \u041e\u0447\u0435\u043d\u044c \u0443\u0434\u043e\u0431\u043d\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043d\u0435 \u043d\u0443\u0436\u043d\u044b \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0446\u0438\u0438<\/p>\n<p>&#8212; \u041d\u0435 \u043d\u0443\u0436\u043d\u043e \u0440\u0430\u0437\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0442\u044c \u0431\u0440\u043e\u043a\u0435\u0440 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439<\/p>\n<p>&#8212; \u041d\u0435 \u043d\u0443\u0436\u043d\u043e \u0440\u0430\u0437\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 (worker)<\/p>\n<p><a class=\"anchor\" name=\"6\" id=\"6\"><\/a><\/p>\n<h4>\u041c\u0438\u043d\u0443\u0441\u044b<\/h4>\n<p>&#8212; \u041f\u0440\u0438 \u0431\u043e\u043b\u044c\u0448\u043e\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0435 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 I\/O-bound \u0437\u0430\u0434\u0430\u0447 \u0438 \u0434\u0430\u0436\u0435 \u043f\u0440\u0438 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0435 CPU-bound \u0437\u0430\u0434\u0430\u0447 \u0441\u0435\u0440\u0432\u0438\u0441 \u0434\u0435\u0433\u0440\u0430\u0434\u0438\u0440\u0443\u0435\u0442: \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0432\u0440\u0435\u043c\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u0441\u043d\u0438\u0436\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e RPS.<\/p>\n<p>&#8212; \u041f\u0440\u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438\/\u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043d\u0435\u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043d\u043d\u044b\u0435 \u0438 \u043d\u0435 \u043d\u0430\u0447\u0430\u0442\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0431\u0443\u0434\u0443\u0442 \u0443\u0442\u0435\u0440\u044f\u043d\u044b<\/p>\n<p><a class=\"anchor\" name=\"7\" id=\"7\"><\/a><\/p>\n<h3>ARQ<\/h3>\n<p><a href=\"https:\/\/github.com\/samuelcolvin\/arq\"><em>\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 GitHub<\/em><\/a><\/p>\n<p>\u041b\u0435\u0433\u043a\u043e\u0432\u0435\u0441\u043d\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430, \u0437\u0430\u0434\u0443\u043c\u0430\u043d\u043d\u0430\u044f \u043a\u0430\u043a \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 <a href=\"https:\/\/python-rq.org\/\"><u>RQ<\/u><\/a>.<\/p>\n<p>\u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438:<\/p>\n<ul>\n<li>\n<p>\u0417\u0430\u0434\u0430\u0447\u0430 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043e\u043b\u0436\u043d\u0430 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u043f\u0435\u0440\u0432\u044b\u043c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0447\u0442\u043e \u0443\u0433\u043e\u0434\u043d\u043e<\/p>\n<\/li>\n<li>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0443 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0435\u0451 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import arq import arq.connections     # \u0444\u043e\u043d\u043e\u0432\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430: \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u044d\u043b. \u043f\u043e\u0447\u0442\u044b \u0432 \u0444\u043e\u043d\u0435 async def send_email(ctx: dict, email: str, message: str = \"Hello\") -> None:    store: Store = ctx[\"store\"]    \u2026     # \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0444\u043e\u043d\u043e\u0432\u044b\u043c \u0437\u0430\u0434\u0430\u0447\u0430\u043c class WorkerAccessor(AbstractTaskAccessor):      def __init__(self, *args: Any, **kwargs: Any) -> None:        super().__init__(*args, **kwargs)     # \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043f\u0443\u043b \u0437\u0430\u0434\u0430\u0447 arq        self.job_pool: arq.ArqRedis | None = None      async def connect(self) -> None:   # \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 Redis        self.job_pool = await arq.create_pool(            arq.connections.RedisSettings( # \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f                host=self.config.REDIS_DSN.host,                port=self.config.REDIS_DSN.port,                username=self.config.REDIS_DSN.username,                password=self.config.REDIS_DSN.password,                database=self.config.REDIS_DSN.path.removeprefix(\"\/\"),            )        )      async def disconnect(self) -> None:        \"\"\"\u0414\u043b\u044f \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\"\"\"        if self.job_pool:            await self.job_pool.close()      async def run(self) -> None:        \"\"\"\u0414\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0432\u043e\u0440\u043a\u0435\u0440\u0430 \u0432 \u0434\u0440\u0443\u0433\u043e\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0435\"\"\"               async def on_startup(ctx: dict) -> None:  # \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0432 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435            ctx[\"store\"] = self.store          worker = arq.Worker(  # \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0444\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438            functions=[send_email],            on_startup=on_startup,            max_jobs=self.config.ARQ_CONCURRENCY,            health_check_interval=60,            handle_signals=False,            redis_pool=self.job_pool,        )        try:  # \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0432\u043e\u0440\u043a\u0435\u0440            await worker.main()        finally:  # \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0432\u043e\u0440\u043a\u0435\u0440            await worker.close()               async def send_email(self, email: str, message: str = \"Hello\") -> None:        # \u0441\u0442\u0430\u0432\u0438\u043c \u0437\u0430\u0434\u0430\u0447\u0443 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435        await self.job_pool.enqueue_job(send_email.__name__, email, message=message)  # \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0444\u043e\u043d\u043e\u0432\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u044d\u043b. \u043f\u043e\u0447\u0442\u044b, \u0432\u0430\u0440\u0438\u0430\u043d\u0442 1 @router.post(\"\") async def hello(store: StoreDep) -> JSONResponse:    task = BackgroundTask(store.worker.arq.send_email, \"support@mail.ru\", message=\"Hello\")    return JSONResponse({\"status\": \"ok\"}, background=task)     # \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0444\u043e\u043d\u043e\u0432\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u044d\u043b. \u043f\u043e\u0447\u0442\u044b, \u0432\u0430\u0440\u0438\u0430\u043d\u0442 2 @router.post(\"\") async def hello(store: StoreDep) -> dict:    await store.worker.arq.send_email(\"support@mail.ru\", message=\"Hello\")    return {\"status\": \"ok\"}<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0432\u043e\u0440\u043a\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u0442\u0430\u043a\u0438\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c:<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">async def main() -> None:    async with store_lifespan() as store:        await store.worker.arq.run()     if __name__ == \"__main__\":    asyncio.run(main()) <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p><a class=\"anchor\" name=\"8\" id=\"8\"><\/a><\/p>\n<h4>\u041f\u043b\u044e\u0441\u044b<\/h4>\n<ul>\n<li>\n<p>\u0423\u0434\u043e\u0431\u043d\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c (\u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 <a href=\"https:\/\/github.com\/cunla\/fakeredis-py\"><u>fakeredis<\/u><\/a>)<\/p>\n<\/li>\n<li>\n<p>\u041c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0442\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438\u00a0<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a \u0437\u0430\u0434\u0430\u0447 \u043f\u043e \u0440\u0430\u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044e (cron)<\/p>\n<\/li>\n<li>\n<p>\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430 (retrying) \u0438 \u043e\u0442\u043c\u0435\u043d\u044b \u0437\u0430\u0434\u0430\u0447\u0438<\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"9\" id=\"9\"><\/a><\/p>\n<h4>\u041c\u0438\u043d\u0443\u0441\u044b<\/h4>\n<ul>\n<li>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u0440\u043e\u043a\u0435\u0440\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e Redis<\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"10\" id=\"10\"><\/a><\/p>\n<h3>SAQ<\/h3>\n<p><a href=\"https:\/\/github.com\/tobymao\/saq\"><em>\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 GitHub<\/em><\/a><\/p>\n<p>\u0410\u043d\u0430\u043b\u043e\u0433 ARQ \u0441\u043e \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u043c \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u043e\u043c.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import saq   from aiohttp.web_runner import AppRunner, TCPSite from redis.asyncio import Redis from saq.web.aiohttp import create_app     # \u0444\u043e\u043d\u043e\u0432\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 async def send_email(ctx: dict, email: str, message: str = \"Hello\") -> None:    store: Store = ctx[\"store\"]    \u2026     class WorkerkAccessor(BaseAccessor):    def __init__(self, *args: Any, **kwargs: Any) -> None:        super().__init__(*args, **kwargs)          self.queue: saq.Queue | None = None      async def connect(self) -> None:        client = Redis.from_url(self.config.REDIS_DSN.__str__())        self.queue = saq.Queue(client)      async def disconnect(self) -><\/code><\/pre>\n<\/div>\n<\/details>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-376933","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/376933","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=376933"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/376933\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=376933"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=376933"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=376933"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}