{"id":401115,"date":"2024-06-29T15:39:23","date_gmt":"2024-06-29T15:39:23","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=401115"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=401115","title":{"rendered":"<span>\u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0441 FastAPI \u0438 Celery<\/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<p>\u0415\u0441\u043b\u0438 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0435\u0441\u0442\u044c \u0434\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b, \u0442\u043e \u0432\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u043e\u0442\u043e\u043a, \u0432\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0438\u0445 \u0432 \u0444\u043e\u043d\u043e\u0432\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435.<\/p>\n<p>\u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u043b\u0438 \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u044e (\u0440\u0430\u0437\u043c\u0435\u0440 \u043a\u043e\u0442\u043e\u0440\u043e\u0439, \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e, \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c) \u0438 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u043b\u0438 \u0441\u0432\u043e\u0439 \u0430\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b \u043f\u0440\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438. \u0415\u0441\u043b\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u043b\u043e \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0435 \u043f\u0438\u0441\u044c\u043c\u043e \u0441 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\u043c \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u0442\u043e \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u043c\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0431\u044b \u0436\u0434\u0430\u0442\u044c, \u043f\u043e\u043a\u0430 \u043e\u043d\u0438 \u043e\u0431\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443. \u0412\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u044d\u0442\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0437\u0430\u0434\u0430\u0447 \u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c\u0443 \u0440\u0430\u0431\u043e\u0447\u0435\u043c\u0443 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0443 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0441 \u043d\u0438\u043c\u0438. \u0412 \u044d\u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0434\u0440\u0443\u0433\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430, \u043f\u043e\u043a\u0430 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430. \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u0435\u0442 \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0434\u0440\u0443\u0433\u0438\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u0434\u043e\u0441\u0442\u0438\u0447\u044c \u044d\u0442\u043e\u0433\u043e, \u043c\u044b \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u043c \u0432\u0430\u043c \u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Celery \u0438 Redis \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0449\u0438\u0445\u0441\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 FastAPI. \u041c\u044b \u0442\u0430\u043a\u0436\u0435 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Docker \u0438 Docker Compose, \u0447\u0442\u043e\u0431\u044b \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u0432\u0441\u0435 \u0432\u043e\u0435\u0434\u0438\u043d\u043e. \u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u043a\u0430\u043a \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438 Celery \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u0445 (unit) \u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<h3>\u0426\u0435\u043b\u044c<\/h3>\n<p>\u041a \u043a\u043e\u043d\u0446\u0443 \u044d\u0442\u043e\u0433\u043e \u0443\u0440\u043e\u043a\u0430 \u0432\u044b \u0441\u043c\u043e\u0436\u0435\u0442\u0435:<\/p>\n<ul>\n<li>\n<p>\u0418\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u0443\u0439\u0442\u0435 Celery \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 Fast API \u0438 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u0442\u0435 \u0437\u0430\u0434\u0430\u0447\u0438.<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u0435 \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 FastAPI, Celery \u0438 Redis \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Docker.<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b \u0432 \u0444\u043e\u043d\u043e\u0432\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0440\u0430\u0431\u043e\u0447\u0435\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430.<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u0435 \u043b\u043e\u0433\u0438 Celery \u0432 \u0444\u0430\u0439\u043b.<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 <a href=\"https:\/\/flower.readthedocs.io\/en\/latest\/\" rel=\"noopener noreferrer nofollow\">Flower <\/a>\u0434\u043b\u044f \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430 \u0438 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447 Celery.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u0437\u0430\u0434\u0430\u0447\u0443 Celery \u043a\u0430\u043a \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u0445, \u0442\u0430\u043a \u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<\/li>\n<\/ul>\n<h3>\u0424\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438  <\/h3>\n<p>\u0414\u043b\u044f \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f, \u0434\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0449\u0438\u0435\u0441\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0432\u043d\u0435 \u043e\u0431\u044b\u0447\u043d\u043e\u0433\u043e \u043f\u043e\u0442\u043e\u043a\u0430 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432\/\u043e\u0442\u0432\u0435\u0442\u043e\u0432, \u0442.\u0435 \u0432 \u0444\u043e\u043d\u043e\u0432\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435.<\/p>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440\u044b:<\/p>\n<ol>\n<li>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a \u043c\u043e\u0434\u0435\u043b\u0435\u0439 \u043c\u0430\u0448\u0438\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f<\/p>\n<\/li>\n<li>\n<p>\u041e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u044b\u0445 \u043f\u0438\u0441\u0435\u043c \u0441 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\u043c<\/p>\n<\/li>\n<li>\n<p>\u0412\u0435\u0431-\u0441\u043a\u0440\u0430\u043f\u0435\u0440\u044b<\/p>\n<\/li>\n<li>\n<p>\u0410\u043d\u0430\u043b\u0438\u0437 \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043e\u0442\u0447\u0435\u0442\u043e\u0432<\/p>\n<\/li>\n<\/ol>\n<p>\u041f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u0439\u0442\u0435\u0441\u044c \u043e\u0442\u043b\u0438\u0447\u0430\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0432 \u0442\u0435\u0447\u0435\u043d\u0438\u0435 \u0436\u0438\u0437\u043d\u0435\u043d\u043d\u043e\u0433\u043e \u0446\u0438\u043a\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \/ \u043e\u0442\u0432\u0435\u0442\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 CRUD-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438, \u043e\u0442 \u0442\u0435\u0445, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0432 \u0444\u043e\u043d\u043e\u0432\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435.<\/p>\n<p>\u0421\u0442\u043e\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u0430\u0441\u0441 <a href=\"https:\/\/fastapi.tiangolo.com\/tutorial\/background-tasks\/\" rel=\"noopener noreferrer nofollow\">BackgroundTasks<\/a>\u00a0\u0438\u0437 FastAPI, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0438\u0437 <a href=\"https:\/\/github.com\/encode\/starlette\/blob\/0.26.1\/starlette\/background.py\" rel=\"noopener noreferrer nofollow\">Starlette<\/a>, \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447 \u0432 \u0444\u043e\u043d\u043e\u0432\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435.<\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<pre><code class=\"python\">from fastapi import BackgroundTasks   def send_email(email, message):     pass   @app.get(\"\/\") async def ping(background_tasks: BackgroundTasks):     background_tasks.add_task(send_email, \"email@address.com\", \"Hi!\")     return {\"message\": \"pong!\"}<\/code><\/pre>\n<p>\u0418\u0442\u0430\u043a, \u043a\u043e\u0433\u0434\u0430 \u0432\u0430\u043c \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Celery \u0432\u043c\u0435\u0441\u0442\u043e <code>BackgroundTasks<\/code>?<\/p>\n<ol>\n<li>\n<p><strong>\u0417\u0430\u0434\u0430\u0447\u0438 \u0441 \u0438\u043d\u0442\u0435\u043d\u0441\u0438\u0432\u043d\u044b\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u0430:<\/strong> Celery \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0437\u0430\u0434\u0430\u0447, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442 \u0442\u044f\u0436\u0435\u043b\u044b\u0435 \u0444\u043e\u043d\u043e\u0432\u044b\u0435 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f. \u0412 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a, <code>BackgroundTasks<\/code> \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0432 \u0442\u043e\u043c \u0436\u0435 \u0446\u0438\u043a\u043b\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<\/li>\n<li>\n<p><strong>\u041e\u0447\u0435\u0440\u0435\u0434\u044c \u0437\u0430\u0434\u0430\u0447:<\/strong> \u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438 \u0438 \u0432\u043e\u0440\u043a\u0435\u0440\u0430\u043c\u0438, \u0432\u0430\u043c \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Celery. \u0427\u0430\u0441\u0442\u043e \u0432\u0430\u043c \u0437\u0430\u0445\u043e\u0447\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u0442\u0430\u0442\u0443\u0441 \u0437\u0430\u0434\u0430\u043d\u0438\u044f, \u0430 \u0437\u0430\u0442\u0435\u043c \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043a\u0430\u043a\u043e\u0435-\u043b\u0438\u0431\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 &#8212; \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0435 \u043f\u0438\u0441\u044c\u043c\u043e \u0441 \u043e\u0448\u0438\u0431\u043a\u043e\u0439, \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0434\u0440\u0443\u0433\u0443\u044e \u0444\u043e\u043d\u043e\u0432\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443 \u0438\u043b\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u044c \u043f\u043e\u043f\u044b\u0442\u043a\u0443 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0438. Celery  \u0441\u043f\u0440\u0430\u0432\u0438\u0442\u0441\u044f \u0441\u043e \u0432\u0441\u0435\u043c \u044d\u0442\u0438\u043c \u0437\u0430 \u0432\u0430\u0441.<\/p>\n<\/li>\n<\/ol>\n<h3>\u041f\u0440\u0438\u043d\u0446\u0438\u043f \u0440\u0430\u0431\u043e\u0442\u044b<\/h3>\n<p>\u041d\u0430\u0448\u0430 \u0446\u0435\u043b\u044c &#8212; \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0431\u044b\u0441\u0442\u0440\u043e\u0435 API-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u043d\u043e \u0441 Celery \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 \u0432\u043d\u0435 \u043e\u0431\u044b\u0447\u043d\u043e\u0433\u043e \u0446\u0438\u043a\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \/ \u043e\u0442\u0432\u0435\u0442\u0430.<\/p>\n<ol>\n<li>\n<p>\u041a\u043e\u043d\u0435\u0447\u043d\u044b\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u043d\u043e\u0432\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e POST-\u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430.<\/p>\n<\/li>\n<li>\n<p>\u0412 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0435 POST-\u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u0438 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0437\u0430\u0434\u0430\u0447\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0443\u044e \u0441\u0442\u043e\u0440\u043e\u043d\u0443.<\/p>\n<\/li>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f AJAX, \u043a\u043b\u0438\u0435\u043d\u0442 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u0442 \u043e\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0441\u0442\u0430\u0442\u0443\u0441 \u0437\u0430\u0434\u0430\u0447\u0438, \u0432 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a \u0441\u0430\u043c\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0432 \u0444\u043e\u043d\u043e\u0432\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435.<\/p>\n<\/li>\n<\/ol>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c81\/6e8\/43c\/c816e843c25177aef678060c63643a5f.png\" width=\"531\" height=\"452\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c81\/6e8\/43c\/c816e843c25177aef678060c63643a5f.png\"\/><\/figure>\n<h3>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<p>\u0421\u043a\u043b\u043e\u043d\u0438\u0440\u0443\u0439\u0442\u0435 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0438\u0437 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f <a href=\"https:\/\/github.com\/testdrivenio\/fastapi-celery\" rel=\"noopener noreferrer nofollow\">fastapi-celery<\/a> , \u0430 \u0437\u0430\u0442\u0435\u043c \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 \u0442\u0435\u0433 <a href=\"https:\/\/github.com\/testdrivenio\/fastapi-celery\/tree\/v1\" rel=\"noopener noreferrer nofollow\">v1<\/a> \u0432 <code>master<\/code> \u0432\u0435\u0442\u043a\u0435:<\/p>\n<pre><code class=\"bash\">git clone https:\/\/github.com\/testdrivenio\/fastapi-celery --branch v1 --single-branch cd fastapi-celery git checkout v1 -b master<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0442\u0440\u0435\u043c\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430\u043c\u0438 (FastAPI, Redis, Celery worker), \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Docker \u0434\u043b\u044f \u0443\u043f\u0440\u043e\u0449\u0435\u043d\u0438\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u0440\u0430\u0431\u043e\u0447\u0435\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u0438\u0445 \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0435 \u043e\u043d\u0438 \u043c\u043e\u0433\u043b\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c\u0441\u044f \u0438\u0437 \u043e\u0434\u043d\u043e\u0433\u043e \u043e\u043a\u043d\u0430 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043e\u0434\u043d\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u044b.<\/p>\n<p>\u0418\u0437 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0433\u043e \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043e\u0431\u0440\u0430\u0437\u044b \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b Docker:<\/p>\n<pre><code class=\"bash\">docker-compose up -d --build<\/code><\/pre>\n<p>\u041a\u0430\u043a \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0431\u043e\u0440\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043a <a href=\"http:\/\/localhost:8004\" rel=\"noopener noreferrer nofollow\">http:\/\/localhost:8004<\/a>:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/531\/4e7\/cf8\/5314e7cf88174c499382227ac3571f9b.png\" width=\"2408\" height=\"972\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/531\/4e7\/cf8\/5314e7cf88174c499382227ac3571f9b.png\"\/><\/figure>\n<p>\u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0442\u0435\u0441\u0442\u044b \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u043e\u0448\u043b\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e:  <\/p>\n<pre><code class=\"bash\">$ docker-compose exec web python -m pytest  =============================== test session starts ================================ platform linux -- Python 3.11.2, pytest-7.2.2, pluggy-1.0.0 rootdir: \/usr\/src\/app plugins: anyio-3.6.2 collected 1 item  tests\/test_tasks.py .                                                        [100%]  ================================ 1 passed in 0.20s =================================<\/code><\/pre>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0434\u0432\u0438\u0433\u0430\u0442\u044c\u0441\u044f \u0434\u0430\u043b\u044c\u0448\u0435, \u0432\u0437\u0433\u043b\u044f\u043d\u0438\u0442\u0435 \u043d\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430:  <\/p>\n<pre><code class=\"sql\">\u251c\u2500\u2500 .gitignore \u251c\u2500\u2500 LICENSE \u251c\u2500\u2500 README.md \u251c\u2500\u2500 docker-compose.yml \u2514\u2500\u2500 project     \u251c\u2500\u2500 Dockerfile     \u251c\u2500\u2500 main.py     \u251c\u2500\u2500 requirements.txt     \u251c\u2500\u2500 static     \u2502   \u251c\u2500\u2500 main.css     \u2502   \u2514\u2500\u2500 main.js     \u251c\u2500\u2500 templates     \u2502   \u251c\u2500\u2500 _base.html     \u2502   \u251c\u2500\u2500 footer.html     \u2502   \u2514\u2500\u2500 home.html     \u2514\u2500\u2500 tests         \u251c\u2500\u2500 __init__.py         \u251c\u2500\u2500 conftest.py         \u2514\u2500\u2500 test_tasks.py<\/code><\/pre>\n<h3>\u0417\u0430\u043f\u0443\u0441\u043a \u0437\u0430\u0434\u0430\u0447\u0438<\/h3>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0441\u043e\u0431\u044b\u0442\u0438\u044f <code>onclick<\/code> \u0432 <em>project\/templates\/home.htm<\/em>l \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u0442\u0430\u043a, \u0447\u0442\u043e \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u0435\u0442 \u043d\u0430\u0436\u0430\u0442\u0438\u0435 \u043a\u043d\u043e\u043f\u043a\u0438:  <\/p>\n<pre><code class=\"xml\">&lt;div class=\"btn-group\" role=\"group\" aria-label=\"Basic example\">   &lt;button type=\"button\" class=\"btn btn-primary\" onclick=\"handleClick(1)\">Short&lt;\/a>   &lt;button type=\"button\" class=\"btn btn-primary\" onclick=\"handleClick(2)\">Medium&lt;\/a>   &lt;button type=\"button\" class=\"btn btn-primary\" onclick=\"handleClick(3)\">Long&lt;\/a> &lt;\/div><\/code><\/pre>\n<p><code>onclick<\/code> \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 <code>handleClick<\/code>, \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0439 \u0432 <em>project\/static\/main.js<\/em> , \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 AJAX POST-\u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0441 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u0442\u0438\u043f\u043e\u043c \u0437\u0430\u0434\u0430\u0447\u0438: <code>1<\/code>, <code>2<\/code> \u0438\u043b\u0438 <code>3<\/code>.<\/p>\n<pre><code class=\"javascript\">function handleClick(type) {   fetch('\/tasks', {     method: 'POST',     headers: {       'Content-Type': 'application\/json'     },     body: JSON.stringify({ type: type }),   })   .then(response => response.json())   .then(res => getStatus(res.data.task_id)); }<\/code><\/pre>\n<p>\u041d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u0443\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0432 <em>project\/main.py<\/em>:  <\/p>\n<pre><code class=\"python\">@app.post(\"\/tasks\", status_code=201) def run_task(payload = Body(...)):     task_type = payload[\"type\"]     return JSONResponse(task_type)<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0441\u0430\u043c\u043e\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0435 &#8212; \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Celery!<\/p>\n<h3>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 Celery<\/h3>\n<p>\u041d\u0430\u0447\u043d\u0438\u0442\u0435 \u0441 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f Celery \u0438 Redis \u0432 <em>requirements.txt<\/em>:  <\/p>\n<pre><code class=\"python\">aiofiles==23.1.0 celery==5.2.7 fastapi==0.95.0 Jinja2==3.1.2 pytest==7.2.2 redis==4.5.4 requests==2.28.2 uvicorn==0.21.1 httpx==0.23.3<\/code><\/pre>\n<p>\u0412 Celery \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f\u00a0<a href=\"https:\/\/docs.celeryq.dev\/en\/stable\/getting-started\/backends-and-brokers\/index.html\" rel=\"noopener noreferrer nofollow\">\u0431\u0440\u043e\u043a\u0435\u0440<\/a>\u00a0\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 &#8212;\u00a0<a href=\"https:\/\/www.rabbitmq.com\/\" rel=\"noopener noreferrer nofollow\">RabbitMQ<\/a>,\u00a0<a href=\"https:\/\/redis.io\/\" rel=\"noopener noreferrer nofollow\">Redis<\/a>\u00a0\u0438\u043b\u0438\u00a0<a href=\"https:\/\/aws.amazon.com\/sqs\/\" rel=\"noopener noreferrer nofollow\">AWS Simple Queue Service (SQS)<\/a>\u00a0&#8212; \u0434\u043b\u044f \u043e\u0431\u043b\u0435\u0433\u0447\u0435\u043d\u0438\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043c\u0435\u0436\u0434\u0443 \u0432\u043e\u0440\u043a\u0435\u0440\u043e\u043c Celery \u0438 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c.\u00a0\u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043a \u0431\u0440\u043e\u043a\u0435\u0440\u0443, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0437\u0430\u0442\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0432\u043e\u0440\u043a\u0435\u0440\u0430\u043c\u0438.\u00a0\u041f\u043e\u0441\u043b\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u0430 \u0431\u044d\u043a\u0435\u043d\u0434 (Redis backend).<\/p>\n<p>Redis \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043a\u0430\u043a \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u0440\u043e\u043a\u0435\u0440\u0430, \u0442\u0430\u043a \u0438 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u044d\u043a\u0435\u043d\u0434\u0430 (Redis backend).\u00a0\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 Redis \u0438\u00a0<a href=\"https:\/\/docs.celeryq.dev\/en\/stable\/userguide\/workers.html\" rel=\"noopener noreferrer nofollow\">worker<\/a>\u00a0Celery \u0432\u00a0\u0444\u0430\u0439\u043b <em>docker-compose.yml<\/em>\u00a0\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"yaml\">version: '3.8'  services:    web:     build: .\/project     ports:       - 8004:8000     command: uvicorn main:app --host 0.0.0.0 --reload     volumes:       - .\/project:\/usr\/src\/app     environment:       - CELERY_BROKER_URL=redis:\/\/redis:6379\/0       - CELERY_RESULT_BACKEND=redis:\/\/redis:6379\/0     depends_on:       - redis    worker:     build: .\/project     command: celery -A worker.celery worker --loglevel=info     volumes:       - .\/project:\/usr\/src\/app     environment:       - CELERY_BROKER_URL=redis:\/\/redis:6379\/0       - CELERY_RESULT_BACKEND=redis:\/\/redis:6379\/0     depends_on:       - web       - redis    redis:     image: redis:7<\/code><\/pre>\n<p>\u041e\u043f\u0438\u0448\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <code>celery -A worker.celery worker --loglevel=info<\/code>:<\/p>\n<ol>\n<li>\n<p><code>celery worker<\/code>\u00a0\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u00a0<a href=\"https:\/\/docs.celeryq.dev\/en\/stable\/userguide\/workers.html#starting-the-worker\" rel=\"noopener noreferrer nofollow\">\u0432\u043e\u0440\u043a\u0435\u0440\u0430 Celery<\/a><\/p>\n<\/li>\n<li>\n<p><code>-A worker.celery<\/code>\u00a0\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u00a0<a href=\"https:\/\/docs.celeryq.dev\/en\/stable\/userguide\/application.html\" rel=\"noopener noreferrer nofollow\">\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/a>\u00a0Celery (\u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043c\u044b \u0432\u0441\u043a\u043e\u0440\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c)<\/p>\n<\/li>\n<li>\n<p><code>--loglevel=info<\/code>\u00a0\u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u00a0<a href=\"https:\/\/docs.celeryq.dev\/en\/v5.2.7\/reference\/cli.html?highlight=loglevel#cmdoption-celery-events-l\" rel=\"noopener noreferrer nofollow\">\u0443\u0440\u043e\u0432\u0435\u043d\u044c \u0436\u0443\u0440\u043d\u0430\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/a>\u00a0\u043d\u0430 info<\/p>\n<\/li>\n<\/ol>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043d\u043e\u0432\u044b\u0439 \u0444\u0430\u0439\u043b \u0441 \u0438\u043c\u0435\u043d\u0435\u043c\u00a0<em>worker.py\u00a0<\/em>\u0432 &#171;project&#187;:  <\/p>\n<pre><code class=\"python\">import os import time  from celery import Celery   celery = Celery(__name__) celery.conf.broker_url = os.environ.get(\"CELERY_BROKER_URL\", \"redis:\/\/localhost:6379\") celery.conf.result_backend = os.environ.get(\"CELERY_RESULT_BACKEND\", \"redis:\/\/localhost:6379\")   @celery.task(name=\"create_task\") def create_task(task_type):     time.sleep(int(task_type) * 10)     return True<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u043d\u043e\u0432\u044b\u0439 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 Celery \u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u00a0<a href=\"https:\/\/docs.celeryq.dev\/en\/stable\/reference\/celery.html#celery.Celery.task\" rel=\"noopener noreferrer nofollow\">\u0437\u0430\u0434\u0430\u0447\u0438<\/a>, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438 \u043d\u043e\u0432\u0443\u044e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0437\u0430\u0434\u0430\u0447\u0438 Celery\u00a0<code>create_task<\/code>.  <\/p>\n<blockquote>\n<p>\u0418\u043c\u0435\u0439\u0442\u0435 \u0432 \u0432\u0438\u0434\u0443, \u0447\u0442\u043e \u0441\u0430\u043c\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0432\u043e\u0440\u043a\u0435\u0440\u043e\u043c Celery.  <\/p>\n<\/blockquote>\n<h3>\u0417\u0430\u043f\u0443\u0441\u043a \u0437\u0430\u0434\u0430\u0447\u0438<\/h3>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u044b \u0432\u00a0<em>main.py\u00a0<\/em>\u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0437\u0430\u0434\u0430\u0447\u0438 \u0438 \u043e\u0442\u0432\u0435\u0442\u0430 \u0441 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u043c \u0437\u0430\u0434\u0430\u0447\u0438: <\/p>\n<pre><code class=\"python\">@app.post(\"\/tasks\", status_code=201) def run_task(payload = Body(...)):     task_type = payload[\"type\"]     task = create_task.delay(int(task_type))     return JSONResponse({\"task_id\": task.id})<\/code><\/pre>\n<p>\u041d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0443:<\/p>\n<pre><code class=\"python\">from worker import create_task<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043d\u043e\u0432\u044b\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b:<\/p>\n<pre><code class=\"bash\">docker-compose up -d --build<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435:<\/p>\n<pre><code class=\"bash\">curl http:\/\/localhost:8004\/tasks -H \"Content-Type: application\/json\" --data '{\"type\": 0}'<\/code><\/pre>\n<p>\u0412\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0432\u0440\u043e\u0434\u0435:<\/p>\n<pre><code class=\"json\">{   \"task_id\": \"14049663-6257-4a1f-81e5-563c714e90af\" }<\/code><\/pre>\n<h3>\u0421\u0442\u0430\u0442\u0443\u0441 \u0437\u0430\u0434\u0430\u0447\u0438<\/h3>\n<p>\u0412\u0435\u0440\u043d\u0435\u043c\u0441\u044f \u043a\u00a0<code>handleClick<\/code>\u00a0\u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430:<\/p>\n<pre><code class=\"javascript\">function handleClick(type) {   fetch('\/tasks', {     method: 'POST',     headers: {       'Content-Type': 'application\/json'     },     body: JSON.stringify({ type: type }),   })   .then(response => response.json())   .then(res => getStatus(res.data.task_id)); }<\/code><\/pre>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043e\u0442\u0432\u0435\u0442 \u043e\u0442 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e AJAX-\u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c <code>getStatus()<\/code>\u00a0\u0441 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u043c \u0437\u0430\u0434\u0430\u0447\u0438 \u043a\u0430\u0436\u0434\u0443\u044e \u0441\u0435\u043a\u0443\u043d\u0434\u0443:<\/p>\n<pre><code class=\"javascript\">function getStatus(taskID) {   fetch(`\/tasks\/${taskID}`, {     method: 'GET',     headers: {       'Content-Type': 'application\/json'     },   })   .then(response => response.json())   .then(res => {     const html = `       &lt;tr>         &lt;td>${taskID}&lt;\/td>         &lt;td>${res.data.task_status}&lt;\/td>         &lt;td>${res.data.task_result}&lt;\/td>       &lt;\/tr>`;     document.getElementById('tasks').prepend(html);     const newRow = document.getElementById('table').insertRow();     newRow.innerHTML = html;     const taskStatus = res.data.task_status;     if (taskStatus === 'finished' || taskStatus === 'failed') return false;     setTimeout(function() {       getStatus(res.data.task_id);     }, 1000);   })   .catch(err => console.log(err)); }<\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u043e\u0442\u0432\u0435\u0442 \u0443\u0441\u043f\u0435\u0448\u0435\u043d, \u0432 DOM \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u043e\u0432\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430.<\/p>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435\u00a0\u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 <code>get_status<\/code>, \u0447\u0442\u043e\u0431\u044b \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0441\u0442\u0430\u0442\u0443\u0441:<\/p>\n<pre><code class=\"python\">@app.get(\"\/tasks\/{task_id}\") def get_status(task_id):     task_result = AsyncResult(task_id)     result = {         \"task_id\": task_id,         \"task_status\": task_result.status,         \"task_result\": task_result.result     }     return JSONResponse(result)<\/code><\/pre>\n<p>\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0439\u0442\u0435\u00a0<a href=\"https:\/\/docs.celeryq.dev\/en\/stable\/reference\/celery.result.html\" rel=\"noopener noreferrer nofollow\">AsyncResult<\/a>:<\/p>\n<pre><code class=\"python\">from celery.result import AsyncResult<\/code><\/pre>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b:<\/p>\n<pre><code class=\"bash\">docker-compose up -d --build<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043d\u043e\u0432\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443:<\/p>\n<pre><code class=\"bash\">curl http:\/\/localhost:8004\/tasks -H \"Content-Type: application\/json\" --data '{\"type\": 1}'<\/code><\/pre>\n<p>\u0417\u0430\u0442\u0435\u043c \u0432\u043e\u0437\u044c\u043c\u0438\u0442\u0435\u00a0<code>task_id<\/code>\u00a0\u0438\u0437 \u043e\u0442\u0432\u0435\u0442\u0430 \u0438 \u0432\u044b\u0437\u043e\u0432\u0438\u0442\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435:<\/p>\n<pre><code class=\"bash\">$ curl http:\/\/localhost:8004\/tasks\/f3ae36f1-58b8-4c2b-bf5b-739c80e9d7ff  {   \"task_id\": \"455234e0-f0ea-4a39-bbe9-e3947e248503\",   \"task_result\": true,   \"task_status\": \"SUCCESS\" }<\/code><\/pre>\n<p>\u0422\u0430\u043a\u0436\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u044d\u0442\u043e \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/ec6\/c91\/819\/ec6c918197ce4a8081ae8e1e7e6146dc.png\" width=\"2730\" height=\"1978\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ec6\/c91\/819\/ec6c918197ce4a8081ae8e1e7e6146dc.png\"\/><\/figure>\n<h3>\u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 Celery<\/h3>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u00a0<code>worker<\/code>\u00a0\u0432\u00a0<em>docker-compose.yml<\/em>, \u0447\u0442\u043e\u0431\u044b \u043b\u043e\u0433\u0438 Celery \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u043b\u0438\u0441\u044c \u0432 \u0444\u0430\u0439\u043b:<\/p>\n<pre><code class=\"yaml\">worker:   build: .\/project   command: celery -A worker.celery worker --loglevel=info --logfile=logs\/celery.log   volumes:     - .\/project:\/usr\/src\/app   environment:     - CELERY_BROKER_URL=redis:\/\/redis:6379\/0     - CELERY_RESULT_BACKEND=redis:\/\/redis:6379\/0   depends_on:     - web     - redis<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u043d\u043e\u0432\u044b\u0439 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u0432 &#171;project&#187; \u043f\u043e\u0434 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c &#171;logs&#187;.\u00a0\u0417\u0430\u0442\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u043d\u043e\u0432\u044b\u0439 \u0444\u0430\u0439\u043b \u0441 \u0438\u043c\u0435\u043d\u0435\u043c\u00a0<em>celery.log<\/em>\u00a0\u0432 \u044d\u0442\u043e\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u043a\u0430\u0442\u0430\u043b\u043e\u0433.<\/p>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b:<\/p>\n<pre><code>docker-compose up -d --build<\/code><\/pre>\n<p>\u0412\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u0447\u0442\u043e \u0444\u0430\u0439\u043b \u043b\u043e\u0433\u043e\u0432 \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043c\u044b \u043f\u0440\u043e\u0431\u0440\u043e\u0441\u0438\u043b\u0438 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e \u043f\u0440\u043e\u0435\u043a\u0442\u0430: <\/p>\n<pre><code>[2023-04-05 16:10:33,257: INFO\/MainProcess] Connected to redis:\/\/redis:6379\/0 [2023-04-05 16:10:33,262: INFO\/MainProcess] mingle: searching for neighbors [2023-04-05 16:10:34,271: INFO\/MainProcess] mingle: all alone [2023-04-05 16:10:34,283: INFO\/MainProcess] celery@6ea5007507db ready. [2023-04-05 16:11:49,400: INFO\/MainProcess]   Task create_task[7f0022ec-bcc8-4eff-b825-bde60d15f824] received [2023-04-05 16:11:59,418: INFO\/ForkPoolWorker-7]   Task create_task[7f0022ec-bcc8-4eff-b825-bde60d15f824]   succeeded in 10.015363933052868s: True <\/code><\/pre>\n<h3>\u041f\u0430\u043d\u0435\u043b\u044c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f Flower<\/h3>\n<p><a href=\"https:\/\/flower.readthedocs.io\/en\/latest\/\" rel=\"noopener noreferrer nofollow\">Flower<\/a>\u00a0&#8212; \u044d\u0442\u043e \u043b\u0435\u0433\u043a\u0438\u0439 \u0432\u0435\u0431-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430 Celery \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438.\u00a0\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u0442\u0435\u043a\u0443\u0449\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438, \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0442\u044c \u0438\u043b\u0438 \u0443\u043c\u0435\u043d\u044c\u0448\u0430\u0442\u044c \u043f\u0443\u043b \u0432\u043e\u0440\u043a\u0435\u0440\u043e\u0432, \u043f\u0440\u043e\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0442\u044c \u0433\u0440\u0430\u0444\u0438\u043a\u0438 \u0438 \u0440\u044f\u0434 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0444\u0430\u0439\u043b\u00a0<em>requirements.txt<\/em>:<\/p>\n<pre><code class=\"python\">aiofiles==23.1.0 celery==5.2.7 fastapi==0.95.0 flower==1.2.0 Jinja2==3.1.2 pytest==7.2.2 redis==4.5.4 requests==2.28.2 uvicorn==0.21.1 httpx==0.23.3<\/code><\/pre>\n<p>\u0417\u0430\u0442\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u043d\u043e\u0432\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u0432\u00a0<em>docker-compose.yml<\/em>:<\/p>\n<pre><code class=\"yaml\">dashboard:   build: .\/project   command: celery --broker=redis:\/\/redis:6379\/0 flower --port=5555   ports:     - 5556:5555   environment:     - CELERY_BROKER_URL=redis:\/\/redis:6379\/0     - CELERY_RESULT_BACKEND=redis:\/\/redis:6379\/0   depends_on:     - web     - redis     - worker<\/code><\/pre>\n<p>\u041f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b:<\/p>\n<pre><code class=\"bash\">docker-compose up -d --build<\/code><\/pre>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432\u00a0<a href=\"http:\/\/localhost:5556\/\" rel=\"noopener noreferrer nofollow\">http:\/\/localhost:5556\u00a0<\/a>\u0434\u043b\u044f \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0439 \u043f\u0430\u043d\u0435\u043b\u0438.\u00a0\u0412\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u043e\u0434\u043d\u043e\u0433\u043e \u0432\u043e\u0440\u043a\u0435\u0440\u0430, \u0433\u043e\u0442\u043e\u0432\u043e\u0433\u043e \u043a \u0440\u0430\u0431\u043e\u0442\u0435:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/679\/f85\/ad3\/679f85ad38010be4398173e77058142e.png\" width=\"2030\" height=\"602\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/679\/f85\/ad3\/679f85ad38010be4398173e77058142e.png\"\/><\/figure>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u0435\u0449\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0434\u0430\u0447, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u043d\u0435\u043b\u044c \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430:  <\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/078\/d29\/b85\/078d29b85c9aa810b55ecfe560ca3e30.png\" width=\"3572\" height=\"1764\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/078\/d29\/b85\/078d29b85c9aa810b55ecfe560ca3e30.png\"\/><\/figure>\n<p>\u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0449\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u043e\u0440\u043a\u0435\u0440\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c, \u043a\u0430\u043a \u044d\u0442\u043e \u043f\u043e\u0432\u043b\u0438\u044f\u0435\u0442 \u043d\u0430 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044e:  <\/p>\n<pre><code class=\"bash\">docker-compose up -d --build --scale worker=3<\/code><\/pre>\n<h3>\u0422\u0435\u0441\u0442\u044b<\/h3>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u0447\u043d\u0435\u043c \u0441 \u0441\u0430\u043c\u043e\u0433\u043e \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430:<\/p>\n<pre><code class=\"python\">def test_task():     assert create_task.run(1)     assert create_task.run(2)     assert create_task.run(3)<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0439 \u0432\u044b\u0448\u0435 \u043f\u0440\u0438\u043c\u0435\u0440 \u0432\u00a0<em>project\/tests\/test_tasks.py\u00a0<\/em>, \u0430 \u0437\u0430\u0442\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0438\u043c\u043f\u043e\u0440\u0442:<\/p>\n<pre><code class=\"python\">from worker import create_task<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u044d\u0442\u043e\u0442 \u0442\u0435\u0441\u0442 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e:<\/p>\n<pre><code class=\"bash\">docker-compose exec web python -m pytest -k \"test_task and not test_home\"<\/code><\/pre>\n<p>\u0412\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0437\u0430\u043d\u044f\u0442\u044c \u043e\u043a\u043e\u043b\u043e \u043e\u0434\u043d\u043e\u0439 \u043c\u0438\u043d\u0443\u0442\u044b:<\/p>\n<pre><code class=\"python\">=============================== test session starts ================================ platform linux -- Python 3.11.2, pytest-7.2.2, pluggy-1.0.0 rootdir: \/usr\/src\/app, configfile: pytest.ini plugins: anyio-3.6.2 collected 2 items \/ 1 deselected \/ 1 selected tests\/test_tasks.py .                                                        [100%] ==================== 1 passed, 1 deselected in 60.07s (0:01:00) ====================<\/code><\/pre>\n<p>\u0421\u0442\u043e\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u0432 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0445 \u0432\u044b\u0448\u0435 \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f\u0445 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438\u00a0<code>.run<\/code>\u00a0\u043c\u0435\u0442\u043e\u0434 (\u0430 \u043d\u0435\u00a0<code>.delay<\/code>). \u042d\u0442\u043e \u043d\u0443\u0436\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e, \u0431\u0435\u0437 \u0443\u0447\u0430\u0441\u0442\u0438\u044f \u0432\u043e\u0440\u043a\u0435\u0440\u0430 Celery.<\/p>\n<p>\u0425\u043e\u0442\u0438\u0442\u0435 \u0437\u0430\u043c\u043e\u043a\u0430\u0442\u044c <code>.run<\/code>\u00a0\u043c\u0435\u0442\u043e\u0434, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u043a\u043e\u0440\u0438\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441?<\/p>\n<pre><code class=\"python\">@patch(\"worker.create_task.run\") def test_mock_task(mock_run):     assert create_task.run(1)     create_task.run.assert_called_once_with(1)      assert create_task.run(2)     assert create_task.run.call_count == 2      assert create_task.run(3)     assert create_task.run.call_count == 3<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0438\u043c\u043f\u043e\u0440\u0442:<\/p>\n<pre><code class=\"python\">from unittest.mock import patch<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c:<\/p>\n<pre><code class=\"bash\">$ docker-compose exec web python -m pytest -k \"test_mock_task\"  =============================== test session starts ================================ platform linux -- Python 3.11.2, pytest-7.2.2, pluggy-1.0.0 rootdir: \/usr\/src\/app, configfile: pytest.ini plugins: anyio-3.6.2 collected 2 items \/ 1 deselected \/ 1 selected tests\/test_tasks.py .                                                        [100%] ========================= 1 passed, 1 deselected in 0.10s ==========================<\/code><\/pre>\n<p>\u041d\u0430\u043c\u043d\u043e\u0433\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435!<\/p>\n<p>\u041a\u0430\u043a \u043d\u0430\u0441\u0447\u0435\u0442 \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430?<\/p>\n<pre><code class=\"python\">def test_task_status(test_app):     response = test_app.post(         \"\/tasks\",         data=json.dumps({\"type\": 1})     )     content = response.json()     task_id = content[\"task_id\"]     assert task_id      response = test_app.get(f\"tasks\/{task_id}\")     content = response.json()     assert content == {\"task_id\": task_id, \"task_status\": \"PENDING\", \"task_result\": None}     assert response.status_code == 200      while content[\"task_status\"] == \"PENDING\":         response = test_app.get(f\"tasks\/{task_id}\")         content = response.json()     assert content == {\"task_id\": task_id, \"task_status\": \"SUCCESS\", \"task_result\": True}<\/code><\/pre>\n<p>\u0418\u043c\u0435\u0439\u0442\u0435 \u0432 \u0432\u0438\u0434\u0443, \u0447\u0442\u043e \u0432 \u044d\u0442\u043e\u043c \u0442\u0435\u0441\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0442\u043e\u0442 \u0436\u0435 \u0431\u0440\u043e\u043a\u0435\u0440 \u0438 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0447\u0442\u043e \u0438 \u043f\u0440\u0438 \u0432\u0435\u0440\u0441\u0438\u0438 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0432\u044b \u0437\u0430\u0445\u043e\u0442\u0438\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 Celery \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u0442\u0435 \u0438\u043c\u043f\u043e\u0440\u0442:<\/p>\n<pre><code class=\"python\">import json<\/code><\/pre>\n<p>\u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0442\u0435\u0441\u0442 \u043f\u0440\u043e\u0439\u0434\u0435\u043d \u0443\u0441\u043f\u0435\u0448\u043d\u043e.<\/p>\n<h3>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h3>\n<p>\u042d\u0442\u043e \u0431\u044b\u043b\u043e \u0431\u0430\u0437\u043e\u0432\u043e\u0435 \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u043f\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 Celery \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0434\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 FastAPI.\u00a0\u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043b\u044e\u0431\u044b\u0435 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u043b\u0438 \u0437\u0430\u043c\u0435\u0434\u043b\u044f\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u043a\u043e\u0434\u0430.<\/p>\n<p>Celery \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u044e\u0449\u0438\u0445\u0441\u044f \u0437\u0430\u0434\u0430\u0447. \u0422\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u044b\u0435, \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0435\u043c\u043a\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438, \u0447\u0442\u043e\u0431\u044b \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0430\u0448\u0438\u043d. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0441\u043e\u043a\u0440\u0430\u0442\u0438\u0442\u044c (1) \u0432\u0440\u0435\u043c\u044f \u0438 (2) \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u0430 \u043c\u0430\u0448\u0438\u043d\u0443, \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0449\u0443\u044e \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b.<\/p>\n<p>\u041a\u043e\u0434 \u0438\u0437\u00a0<a href=\"https:\/\/github.com\/testdrivenio\/fastapi-celery\" rel=\"noopener noreferrer nofollow\">\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f<\/a>.<\/p>\n<\/p>\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\/733452\/\"> https:\/\/habr.com\/ru\/articles\/733452\/<\/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<p>\u0415\u0441\u043b\u0438 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0435\u0441\u0442\u044c \u0434\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b, \u0442\u043e \u0432\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u043e\u0442\u043e\u043a, \u0432\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0438\u0445 \u0432 \u0444\u043e\u043d\u043e\u0432\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435.<\/p>\n<p>\u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u043b\u0438 \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u044e (\u0440\u0430\u0437\u043c\u0435\u0440 \u043a\u043e\u0442\u043e\u0440\u043e\u0439, \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e, \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c) \u0438 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u043b\u0438 \u0441\u0432\u043e\u0439 \u0430\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b \u043f\u0440\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438. \u0415\u0441\u043b\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u043b\u043e \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0435 \u043f\u0438\u0441\u044c\u043c\u043e \u0441 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\u043c \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u0442\u043e \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u043c\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0431\u044b \u0436\u0434\u0430\u0442\u044c, \u043f\u043e\u043a\u0430 \u043e\u043d\u0438 \u043e\u0431\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443. \u0412\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u044d\u0442\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0437\u0430\u0434\u0430\u0447 \u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c\u0443 \u0440\u0430\u0431\u043e\u0447\u0435\u043c\u0443 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0443 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0441 \u043d\u0438\u043c\u0438. \u0412 \u044d\u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0434\u0440\u0443\u0433\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430, \u043f\u043e\u043a\u0430 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430. \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u0435\u0442 \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0434\u0440\u0443\u0433\u0438\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u0434\u043e\u0441\u0442\u0438\u0447\u044c \u044d\u0442\u043e\u0433\u043e, \u043c\u044b \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u043c \u0432\u0430\u043c \u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Celery \u0438 Redis \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0449\u0438\u0445\u0441\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 FastAPI. \u041c\u044b \u0442\u0430\u043a\u0436\u0435 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Docker \u0438 Docker Compose, \u0447\u0442\u043e\u0431\u044b \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u0432\u0441\u0435 \u0432\u043e\u0435\u0434\u0438\u043d\u043e. \u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u043a\u0430\u043a \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438 Celery \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u0445 (unit) \u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<h3>\u0426\u0435\u043b\u044c<\/h3>\n<p>\u041a \u043a\u043e\u043d\u0446\u0443 \u044d\u0442\u043e\u0433\u043e \u0443\u0440\u043e\u043a\u0430 \u0432\u044b \u0441\u043c\u043e\u0436\u0435\u0442\u0435:<\/p>\n<ul>\n<li>\n<p>\u0418\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u0443\u0439\u0442\u0435 Celery \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 Fast API \u0438 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u0442\u0435 \u0437\u0430\u0434\u0430\u0447\u0438.<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u0435 \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 FastAPI, Celery \u0438 Redis \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Docker.<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b \u0432 \u0444\u043e\u043d\u043e\u0432\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0440\u0430\u0431\u043e\u0447\u0435\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430.<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u0435 \u043b\u043e\u0433\u0438 Celery \u0432 \u0444\u0430\u0439\u043b.<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 <a href=\"https:\/\/flower.readthedocs.io\/en\/latest\/\" rel=\"noopener noreferrer nofollow\">Flower <\/a>\u0434\u043b\u044f \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430 \u0438 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447 Celery.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u0437\u0430\u0434\u0430\u0447\u0443 Celery \u043a\u0430\u043a \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u0445, \u0442\u0430\u043a \u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<\/li>\n<\/ul>\n<h3>\u0424\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438  <\/h3>\n<p>\u0414\u043b\u044f \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f, \u0434\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0449\u0438\u0435\u0441\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0432\u043d\u0435 \u043e\u0431\u044b\u0447\u043d\u043e\u0433\u043e \u043f\u043e\u0442\u043e\u043a\u0430 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432\/\u043e\u0442\u0432\u0435\u0442\u043e\u0432, \u0442.\u0435 \u0432 \u0444\u043e\u043d\u043e\u0432\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435.<\/p>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440\u044b:<\/p>\n<ol>\n<li>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a \u043c\u043e\u0434\u0435\u043b\u0435\u0439 \u043c\u0430\u0448\u0438\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f<\/p>\n<\/li>\n<li>\n<p>\u041e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u044b\u0445 \u043f\u0438\u0441\u0435\u043c \u0441 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\u043c<\/p>\n<\/li>\n<li>\n<p>\u0412\u0435\u0431-\u0441\u043a\u0440\u0430\u043f\u0435\u0440\u044b<\/p>\n<\/li>\n<li>\n<p>\u0410\u043d\u0430\u043b\u0438\u0437 \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043e\u0442\u0447\u0435\u0442\u043e\u0432<\/p>\n<\/li>\n<\/ol>\n<p>\u041f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u0439\u0442\u0435\u0441\u044c \u043e\u0442\u043b\u0438\u0447\u0430\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0432 \u0442\u0435\u0447\u0435\u043d\u0438\u0435 \u0436\u0438\u0437\u043d\u0435\u043d\u043d\u043e\u0433\u043e \u0446\u0438\u043a\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \/ \u043e\u0442\u0432\u0435\u0442\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 CRUD-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438, \u043e\u0442 \u0442\u0435\u0445, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0432 \u0444\u043e\u043d\u043e\u0432\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435.<\/p>\n<p>\u0421\u0442\u043e\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u0430\u0441\u0441 <a href=\"https:\/\/fastapi.tiangolo.com\/tutorial\/background-tasks\/\" rel=\"noopener noreferrer nofollow\">BackgroundTasks<\/a>\u00a0\u0438\u0437 FastAPI, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0438\u0437 <a href=\"https:\/\/github.com\/encode\/starlette\/blob\/0.26.1\/starlette\/background.py\" rel=\"noopener noreferrer nofollow\">Starlette<\/a>, \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447 \u0432 \u0444\u043e\u043d\u043e\u0432\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435.<\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<pre><code class=\"python\">from fastapi import BackgroundTasks   def send_email(email, message):     pass   @app.get(\"\/\") async def ping(background_tasks: BackgroundTasks):     background_tasks.add_task(send_email, \"email@address.com\", \"Hi!\")     return {\"message\": \"pong!\"}<\/code><\/pre>\n<p>\u0418\u0442\u0430\u043a, \u043a\u043e\u0433\u0434\u0430 \u0432\u0430\u043c \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Celery \u0432\u043c\u0435\u0441\u0442\u043e <code>BackgroundTasks<\/code>?<\/p>\n<ol>\n<li>\n<p><strong>\u0417\u0430\u0434\u0430\u0447\u0438 \u0441 \u0438\u043d\u0442\u0435\u043d\u0441\u0438\u0432\u043d\u044b\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u0430:<\/strong> Celery \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0437\u0430\u0434\u0430\u0447, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442 \u0442\u044f\u0436\u0435\u043b\u044b\u0435 \u0444\u043e\u043d\u043e\u0432\u044b\u0435 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f. \u0412 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a, <code>BackgroundTasks<\/code> \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0432 \u0442\u043e\u043c \u0436\u0435 \u0446\u0438\u043a\u043b\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<\/li>\n<li>\n<p><strong>\u041e\u0447\u0435\u0440\u0435\u0434\u044c \u0437\u0430\u0434\u0430\u0447:<\/strong> \u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438 \u0438 \u0432\u043e\u0440\u043a\u0435\u0440\u0430\u043c\u0438, \u0432\u0430\u043c \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Celery. \u0427\u0430\u0441\u0442\u043e \u0432\u0430\u043c \u0437\u0430\u0445\u043e\u0447\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u0442\u0430\u0442\u0443\u0441 \u0437\u0430\u0434\u0430\u043d\u0438\u044f, \u0430 \u0437\u0430\u0442\u0435\u043c \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043a\u0430\u043a\u043e\u0435-\u043b\u0438\u0431\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 &#8212; \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0435 \u043f\u0438\u0441\u044c\u043c\u043e \u0441 \u043e\u0448\u0438\u0431\u043a\u043e\u0439, \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0434\u0440\u0443\u0433\u0443\u044e \u0444\u043e\u043d\u043e\u0432\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443 \u0438\u043b\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u044c \u043f\u043e\u043f\u044b\u0442\u043a\u0443 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0438. Celery  \u0441\u043f\u0440\u0430\u0432\u0438\u0442\u0441\u044f \u0441\u043e \u0432\u0441\u0435\u043c \u044d\u0442\u0438\u043c \u0437\u0430 \u0432\u0430\u0441.<\/p>\n<\/li>\n<\/ol>\n<h3>\u041f\u0440\u0438\u043d\u0446\u0438\u043f \u0440\u0430\u0431\u043e\u0442\u044b<\/h3>\n<p>\u041d\u0430\u0448\u0430 \u0446\u0435\u043b\u044c &#8212; \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0431\u044b\u0441\u0442\u0440\u043e\u0435 API-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u043d\u043e \u0441 Celery \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 \u0432\u043d\u0435 \u043e\u0431\u044b\u0447\u043d\u043e\u0433\u043e \u0446\u0438\u043a\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \/ \u043e\u0442\u0432\u0435\u0442\u0430.<\/p>\n<ol>\n<li>\n<p>\u041a\u043e\u043d\u0435\u0447\u043d\u044b\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u043d\u043e\u0432\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e POST-\u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430.<\/p>\n<\/li>\n<li>\n<p>\u0412 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0435 POST-\u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u0438 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0437\u0430\u0434\u0430\u0447\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0443\u044e \u0441\u0442\u043e\u0440\u043e\u043d\u0443.<\/p>\n<\/li>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f AJAX, \u043a\u043b\u0438\u0435\u043d\u0442 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u0442 \u043e\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0441\u0442\u0430\u0442\u0443\u0441 \u0437\u0430\u0434\u0430\u0447\u0438, \u0432 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a \u0441\u0430\u043c\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0432 \u0444\u043e\u043d\u043e\u0432\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435.<\/p>\n<\/li>\n<\/ol>\n<figure class=\"full-width\"><\/figure>\n<h3>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<p>\u0421\u043a\u043b\u043e\u043d\u0438\u0440\u0443\u0439\u0442\u0435 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0438\u0437 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f <a href=\"https:\/\/github.com\/testdrivenio\/fastapi-celery\" rel=\"noopener noreferrer nofollow\">fastapi-celery<\/a> , \u0430 \u0437\u0430\u0442\u0435\u043c \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 \u0442\u0435\u0433 <a href=\"https:\/\/github.com\/testdrivenio\/fastapi-celery\/tree\/v1\" rel=\"noopener noreferrer nofollow\">v1<\/a> \u0432 <code>master<\/code> \u0432\u0435\u0442\u043a\u0435:<\/p>\n<pre><code class=\"bash\">git clone https:\/\/github.com\/testdrivenio\/fastapi-celery --branch v1 --single-branch cd fastapi-celery git checkout v1 -b master<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0442\u0440\u0435\u043c\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430\u043c\u0438 (FastAPI, Redis, Celery worker), \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Docker \u0434\u043b\u044f \u0443\u043f\u0440\u043e\u0449\u0435\u043d\u0438\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u0440\u0430\u0431\u043e\u0447\u0435\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u0438\u0445 \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0435 \u043e\u043d\u0438 \u043c\u043e\u0433\u043b\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c\u0441\u044f \u0438\u0437 \u043e\u0434\u043d\u043e\u0433\u043e \u043e\u043a\u043d\u0430 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043e\u0434\u043d\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u044b.<\/p>\n<p>\u0418\u0437 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0433\u043e \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043e\u0431\u0440\u0430\u0437\u044b \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b Docker:<\/p>\n<pre><code class=\"bash\">docker-compose up -d --build<\/code><\/pre>\n<p>\u041a\u0430\u043a \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0431\u043e\u0440\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043a <a href=\"http:\/\/localhost:8004\" rel=\"noopener noreferrer nofollow\">http:\/\/localhost:8004<\/a>:<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0442\u0435\u0441\u0442\u044b \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u043e\u0448\u043b\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e:  <\/p>\n<pre><code class=\"bash\">$ docker-compose exec web python -m pytest  =============================== test session starts ================================ platform linux -- Python 3.11.2, pytest-7.2.2, pluggy-1.0.0 rootdir: \/usr\/src\/app plugins: anyio-3.6.2 collected 1 item  tests\/test_tasks.py .                                                        [100%]  ================================ 1 passed in 0.20s =================================<\/code><\/pre>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0434\u0432\u0438\u0433\u0430\u0442\u044c\u0441\u044f \u0434\u0430\u043b\u044c\u0448\u0435, \u0432\u0437\u0433\u043b\u044f\u043d\u0438\u0442\u0435 \u043d\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430:  <\/p>\n<pre><code class=\"sql\">\u251c\u2500\u2500 .gitignore \u251c\u2500\u2500 LICENSE \u251c\u2500\u2500 README.md \u251c\u2500\u2500 docker-compose.yml \u2514\u2500\u2500 project     \u251c\u2500\u2500 Dockerfile     \u251c\u2500\u2500 main.py     \u251c\u2500\u2500 requirements.txt     \u251c\u2500\u2500 static     \u2502   \u251c\u2500\u2500 main.css     \u2502   \u2514\u2500\u2500 main.js     \u251c\u2500\u2500 templates     \u2502   \u251c\u2500\u2500 _base.html     \u2502   \u251c\u2500\u2500 footer.html     \u2502   \u2514\u2500\u2500 home.html     \u2514\u2500\u2500 tests         \u251c\u2500\u2500 __init__.py         \u251c\u2500\u2500 conftest.py         \u2514\u2500\u2500 test_tasks.py<\/code><\/pre>\n<h3>\u0417\u0430\u043f\u0443\u0441\u043a \u0437\u0430\u0434\u0430\u0447\u0438<\/h3>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0441\u043e\u0431\u044b\u0442\u0438\u044f <code>onclick<\/code> \u0432 <em>project\/templates\/home.htm<\/em>l \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u0442\u0430\u043a, \u0447\u0442\u043e \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u0435\u0442 \u043d\u0430\u0436\u0430\u0442\u0438\u0435 \u043a\u043d\u043e\u043f\u043a\u0438:  <\/p>\n<pre><code class=\"xml\">&lt;div class=\"btn-group\" role=\"group\" aria-label=\"Basic example\">   &lt;button type=\"button\" class=\"btn btn-primary\" onclick=\"handleClick(1)\">Short&lt;\/a>   &lt;button type=\"button\" class=\"btn btn-primary\" onclick=\"handleClick(2)\">Medium&lt;\/a>   &lt;button type=\"button\" class=\"btn btn-primary\" onclick=\"handleClick(3)\">Long&lt;\/a> &lt;\/div><\/code><\/pre>\n<p><code>onclick<\/code> \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 <code>handleClick<\/code>, \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0439 \u0432 <em>project\/static\/main.js<\/em> , \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 AJAX POST-\u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0441 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u0442\u0438\u043f\u043e\u043c \u0437\u0430\u0434\u0430\u0447\u0438: <code>1<\/code>, <code>2<\/code> \u0438\u043b\u0438 <code>3<\/code>.<\/p>\n<pre><code class=\"javascript\">function handleClick(type) {   fetch('\/tasks', {     method: 'POST',     headers: {       'Content-Type': 'application\/json'     },     body: JSON.stringify({ type: type }),   })   .then(response => response.json())   .then(res => getStatus(res.data.task_id)); }<\/code><\/pre>\n<p>\u041d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u0443\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0432 <em>project\/main.py<\/em>:  <\/p>\n<pre><code class=\"python\">@app.post(\"\/tasks\", status_code=201) def run_task(payload = Body(...)):     task_type = payload[\"type\"]     return JSONResponse(task_type)<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0441\u0430\u043c\u043e\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0435 &#8212; \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Celery!<\/p>\n<h3>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 Celery<\/h3>\n<p>\u041d\u0430\u0447\u043d\u0438\u0442\u0435 \u0441 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f Celery \u0438 Redis \u0432 <em>requirements.txt<\/em>:  <\/p>\n<pre><code class=\"python\">aiofiles==23.1.0 celery==5.2.7 fastapi==0.95.0 Jinja2==3.1.2 pytest==7.2.2 redis==4.5.4 requests==2.28.2 uvicorn==0.21.1 httpx==0.23.3<\/code><\/pre>\n<p>\u0412 Celery \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f\u00a0<a href=\"https:\/\/docs.celeryq.dev\/en\/stable\/getting-started\/backends-and-brokers\/index.html\" rel=\"noopener noreferrer nofollow\">\u0431\u0440\u043e\u043a\u0435\u0440<\/a>\u00a0\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 &#8212;\u00a0<a href=\"https:\/\/www.rabbitmq.com\/\" rel=\"noopener noreferrer nofollow\">RabbitMQ<\/a>,\u00a0<a href=\"https:\/\/redis.io\/\" rel=\"noopener noreferrer nofollow\">Redis<\/a>\u00a0\u0438\u043b\u0438\u00a0<a href=\"https:\/\/aws.amazon.com\/sqs\/\" rel=\"noopener noreferrer nofollow\">AWS Simple Queue Service (SQS)<\/a>\u00a0&#8212; \u0434\u043b\u044f \u043e\u0431\u043b\u0435\u0433\u0447\u0435\u043d\u0438\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043c\u0435\u0436\u0434\u0443 \u0432\u043e\u0440\u043a\u0435\u0440\u043e\u043c Celery \u0438 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c.\u00a0\u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043a \u0431\u0440\u043e\u043a\u0435\u0440\u0443, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0437\u0430\u0442\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0432\u043e\u0440\u043a\u0435\u0440\u0430\u043c\u0438.\u00a0\u041f\u043e\u0441\u043b\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u0430 \u0431\u044d\u043a\u0435\u043d\u0434 (Redis backend).<\/p>\n<p>Redis \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043a\u0430\u043a \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u0440\u043e\u043a\u0435\u0440\u0430, \u0442\u0430\u043a \u0438 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u044d\u043a\u0435\u043d\u0434\u0430 (Redis backend).\u00a0\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 Redis \u0438\u00a0<a href=\"https:\/\/docs.celeryq.dev\/en\/stable\/userguide\/workers.html\" rel=\"noopener noreferrer nofollow\">worker<\/a>\u00a0Celery \u0432\u00a0\u0444\u0430\u0439\u043b <em>docker-compose.yml<\/em>\u00a0\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"yaml\">version: '3.8'  services:    web:     build: .\/project     ports:       - 8004:8000     command: uvicorn main:app --host 0.0.0.0 --reload     volumes:       - .\/project:\/usr\/src\/app     environment:       - CELERY_BROKER_URL=redis:\/\/redis:6379\/0       - CELERY_RESULT_BACKEND=redis:\/\/redis:6379\/0     depends_on:       - redis    worker:     build: .\/project     command: celery -A worker.celery worker --loglevel=info     volumes:       - .\/project:\/usr\/src\/app     environment:       - CELERY_BROKER_URL=redis:\/\/redis:6379\/0       - CELERY_RESULT_BACKEND=redis:\/\/redis:6379\/0     depends_on:       - web       - redis    redis:     image: redis:7<\/code><\/pre>\n<p>\u041e\u043f\u0438\u0448\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <code>celery -A worker.celery worker --loglevel=info<\/code>:<\/p>\n<ol>\n<li>\n<p><code>celery worker<\/code>\u00a0\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u00a0<a href=\"https:\/\/docs.celeryq.dev\/en\/stable\/userguide\/workers.html#starting-the-worker\" rel=\"noopener noreferrer nofollow\">\u0432\u043e\u0440\u043a\u0435\u0440\u0430 Celery<\/a><\/p>\n<\/li>\n<li>\n<p><code>-A worker.celery<\/code>\u00a0\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u00a0<a href=\"https:\/\/docs.celeryq.dev\/en\/stable\/userguide\/application.html\" rel=\"noopener noreferrer nofollow\">\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/a>\u00a0Celery (\u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043c\u044b \u0432\u0441\u043a\u043e\u0440\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c)<\/p>\n<\/li>\n<li>\n<p><code>--loglevel=info<\/code>\u00a0\u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u00a0<a href=\"https:\/\/docs.celeryq.dev\/en\/v5.2.7\/reference\/cli.html?highlight=loglevel#cmdoption-celery-events-l\" rel=\"noopener noreferrer nofollow\">\u0443\u0440\u043e\u0432\u0435\u043d\u044c \u0436\u0443\u0440\u043d\u0430\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/a>\u00a0\u043d\u0430 info<\/p>\n<\/li>\n<\/ol>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043d\u043e\u0432\u044b\u0439 \u0444\u0430\u0439\u043b \u0441 \u0438\u043c\u0435\u043d\u0435\u043c\u00a0<em>worker.py\u00a0<\/em>\u0432 &#171;project&#187;:  <\/p>\n<pre><code class=\"python\">import os import time  from celery import Celery   celery = Celery(__name__) celery.conf.broker_url = os.environ.get(\"CELERY_BROKER_URL\", \"redis:\/\/localhost:6379\") celery.conf.result_backend = os.environ.get(\"CELERY_RESULT_BACKEND\", \"redis:\/\/localhost:6379\")   @celery.task(name=\"create_task\") def create_task(task_type):     time.sleep(int(task_type) * 10)     return True<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u043d\u043e\u0432\u044b\u0439 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 Celery \u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u00a0<a href=\"https:\/\/docs.celeryq.dev\/en\/stable\/reference\/celery.html#celery.Celery.task\" rel=\"noopener noreferrer nofollow\">\u0437\u0430\u0434\u0430\u0447\u0438<\/a>, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438 \u043d\u043e\u0432\u0443\u044e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0437\u0430\u0434\u0430\u0447\u0438 Celery\u00a0<code>create_task<\/code>.  <\/p>\n<blockquote>\n<p>\u0418\u043c\u0435\u0439\u0442\u0435 \u0432 \u0432\u0438\u0434\u0443, \u0447\u0442\u043e \u0441\u0430\u043c\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0432\u043e\u0440\u043a\u0435\u0440\u043e\u043c Celery.  <\/p>\n<\/blockquote>\n<h3>\u0417\u0430\u043f\u0443\u0441\u043a \u0437\u0430\u0434\u0430\u0447\u0438<\/h3>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u044b \u0432\u00a0<em>main.py\u00a0<\/em>\u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0437\u0430\u0434\u0430\u0447\u0438 \u0438 \u043e\u0442\u0432\u0435\u0442\u0430 \u0441 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u043c \u0437\u0430\u0434\u0430\u0447\u0438: <\/p>\n<pre><code class=\"python\">@app.post(\"\/tasks\", status_code=201) def run_task(payload = Body(...)):     task_type = payload[\"type\"]     task = create_task.delay(int(task_type))     return JSONResponse({\"task_id\": task.id})<\/code><\/pre>\n<p>\u041d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0443:<\/p>\n<pre><code class=\"python\">from worker import create_task<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043d\u043e\u0432\u044b\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b:<\/p>\n<pre><code class=\"bash\">docker-compose up -d --build<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435:<\/p>\n<pre><code class=\"bash\">curl http:\/\/localhost:8004\/tasks -H \"Content-Type: application\/json\" --data '{\"type\": 0}'<\/code><\/pre>\n<p>\u0412\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0432\u0440\u043e\u0434\u0435:<\/p>\n<pre><code class=\"json\">{   \"task_id\": \"14049663-6257-4a1f-81e5-563c714e90af\" }<\/code><\/pre>\n<h3>\u0421\u0442\u0430\u0442\u0443\u0441 \u0437\u0430\u0434\u0430\u0447\u0438<\/h3>\n<p>\u0412\u0435\u0440\u043d\u0435\u043c\u0441\u044f \u043a\u00a0<code>handleClick<\/code>\u00a0\u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430:<\/p>\n<pre><code class=\"javascript\">function handleClick(type) {   fetch('\/tasks', {     method: 'POST',     headers: {       'Content-Type': 'application\/json'     },     body: JSON.stringify({ type: type }),   })   .then(response => response.json())   .then(res => getStatus(res.data.task_id)); }<\/code><\/pre>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043e\u0442\u0432\u0435\u0442 \u043e\u0442 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e AJAX-\u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c <code>getStatus()<\/code>\u00a0\u0441 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u043c \u0437\u0430\u0434\u0430\u0447\u0438 \u043a\u0430\u0436\u0434\u0443\u044e \u0441\u0435\u043a\u0443\u043d\u0434\u0443:<\/p>\n<pre><code class=\"javascript\">function getStatus(taskID) {   fetch(`\/tasks\/${taskID}`, {     method: 'GET',     headers: {       'Content-Type': 'application\/json'     },   })   .then(response => response.json())   .then(res => {     const html = `       &lt;tr>         &lt;td>${taskID}&lt;<\/code><\/pre>\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-401115","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/401115","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=401115"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/401115\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=401115"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=401115"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=401115"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}