{"id":383674,"date":"2024-06-29T05:03:01","date_gmt":"2024-06-29T05:03:01","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=383674"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-06-29T05:03:01","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=383674","title":{"rendered":"<span>\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438 \u0447\u0435\u0440\u0435\u0437 API-gateway<\/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>\u041e\u0434\u043d\u0430\u0436\u0434\u044b \u0442\u0438\u043c\u043b\u0438\u0434 \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0434\u043e \u043c\u043d\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0443 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0447\u0435\u0440\u0435\u0437 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0441\u00a0\u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438 \u0447\u0435\u0440\u0435\u0437 \u0435\u0434\u0438\u043d\u0443\u044e \u0442\u043e\u0447\u043a\u0443 \u0432\u0445\u043e\u0434\u0430 \u0441\u00a0\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c FastAPI \u0438\u00a0RabbitMQ. \u0421\u043f\u0435\u0448\u0443 \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441\u00a0\u0442\u043e\u0431\u043e\u0439, \u043c\u043e\u0439 \u0447\u0438\u0442\u0430\u0442\u0435\u043b\u044c, \u0442\u0435\u043c, \u0447\u0442\u043e \u0443\u00a0\u043c\u0435\u043d\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c. \u041f\u043e\u00a0\u043c\u0435\u0440\u0435 \u043f\u043e\u0432\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u0430\u043c \u043f\u043e\u044f\u0441\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u00a0\u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u043c\u0443 \u043a\u043e\u0434\u0443. \u0418, \u0434\u0430, \u0441\u0434\u0435\u043b\u0430\u044e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u043e\u0442\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u044f \u043f\u043e\u00a0\u0432\u043e\u043f\u0440\u043e\u0441\u0430\u043c \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0438 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f, \u0432\u00a0\u0442.\u0447. \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0445, \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<h3>\u0417\u0430\u0434\u0430\u0447\u0430<\/h3>\n<p>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0447\u0435\u0440\u0435\u0437 \u0435\u0434\u0438\u043d\u044b\u0439 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 (\u00abFront\u00bb side) \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u043a\u00a0\u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435, \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0439 \u0432\u00a0\u0437\u0430\u043a\u0440\u044b\u0442\u043e\u043c \u043a\u043e\u043d\u0442\u0443\u0440\u0435 (\u00abBack\u00bb side). \u0412\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u043e \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c \u043e\u0447\u0435\u0440\u0435\u0434\u0435\u0439 RabbitMQ. \u0412\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043e\u0434 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c Python, FastAPI \u0438\u00a0Gunicorn. \u0412\u00a0\u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u043e\u043c \u0432\u0438\u0434\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u0447\u0442\u043e\u2011\u0442\u043e \u0442\u0430\u043a\u043e\u0435:<\/p>\n<figure class=\"bordered full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/ce5\/40b\/856\/ce540b8569d47f2071a14cc0339c3c7d.jpg\" alt=\"\u041f\u0440\u0438\u043d\u0446\u0438\u043f\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b\" title=\"\u041f\u0440\u0438\u043d\u0446\u0438\u043f\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b\" width=\"1282\" height=\"779\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ce5\/40b\/856\/ce540b8569d47f2071a14cc0339c3c7d.jpg\" data-blurred=\"true\"\/><figcaption>\u041f\u0440\u0438\u043d\u0446\u0438\u043f\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b<\/figcaption><\/figure>\n<h3>\u041f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h3>\n<p>API-\u0448\u043b\u044e\u0437 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u043e\u0442\u00a0\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u00a0\u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437\u00a0\u0447\u0430\u0441\u0442\u043d\u043e\u0441\u0442\u0435\u0439 \u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u0438\u043d\u0442\u0435\u0440\u043f\u0440\u0435\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c. \u0414\u0430\u043b\u0435\u0435 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0432\u00a0\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0432\u00a0\u043e\u0447\u0435\u0440\u0435\u0434\u044c RabbitMQ \u0438\u00a0\u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442\u044c \u0432\u00a0\u0440\u0435\u0436\u0438\u043c \u00ab\u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f\u00bb. \u0421\u00a0\u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b RabbitMQ \u0442\u0430\u043a\u00a0\u0436\u0435 \u0432\u00a0\u0440\u0435\u0436\u0438\u043c\u0435 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c <a href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%A3%D0%B4%D0%B0%D0%BB%D1%91%D0%BD%D0%BD%D1%8B%D0%B9_%D0%B2%D1%8B%D0%B7%D0%BE%D0%B2_%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D0%B4%D1%83%D1%80\" rel=\"noopener noreferrer nofollow\">RPC-\u0441\u0435\u0440\u0432\u0435\u0440<\/a>. \u041f\u0440\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043e\u043d\u00a0\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442, \u0432\u00a0\u043a\u0430\u043a\u043e\u0435 \u0438\u0437\u00a0\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0435\u0433\u043e \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u0438\u0442\u044c, \u0437\u0430\u043f\u0443\u043b\u0438\u0442 \u0435\u0433\u043e \u0432\u00a0\u0441\u0442\u043e\u0440\u043e\u043d\u0443 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e Gunicorn, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043c\u0435\u0436\u0434\u0443 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430\u043c\u0438 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0438\u00a0\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442 \u0432\u0435\u0440\u043d\u0435\u0442 RPC-\u0441\u0435\u0440\u0432\u0435\u0440\u0443, \u0430\u00a0\u0442\u043e\u0442 \u0432\u00a0\u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043f\u0440\u043e\u043a\u0438\u043d\u0435\u0442 \u0435\u0433\u043e \u0434\u0430\u043b\u044c\u0448\u0435 \u0432\u00a0\u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c. \u0421\u00a0\u0442\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b \u00ab\u0436\u0434\u0443\u0449\u0438\u0439\u00bb \u0448\u043b\u044e\u0437 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u044d\u0442\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435, \u0432\u044b\u0439\u0434\u0435\u0442 \u0438\u0437\u00a0\u0440\u0435\u0436\u0438\u043c\u0430 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f \u0438\u00a0\u0441\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442 \u0432\u0435\u0440\u043d\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e.<\/p>\n<h3>\u0418\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435<\/h3>\n<p>\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u043c \u0432\u0441\u0435 \u043c\u043e\u0434\u0443\u043b\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c\u043e\u0439 \u0441\u0445\u0435\u043c\u044b \u0432\u00a0\u043e\u0434\u043d\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435, \u043f\u043e\u0434\u0440\u0430\u0437\u0443\u043c\u0435\u0432\u0430\u044f \u043f\u0440\u0438 \u044d\u0442\u043e\u043c, \u0447\u0442\u043e \u0443\u00a0\u043a\u0430\u0436\u0434\u043e\u0439 \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0435\u0439 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u0432\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435, \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u043e, \u0441\u043f\u043e\u0441\u043e\u0431\u044b \u0438\u00a0\u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0438\u00a0\u0442.\u0434. \u0421\u0435\u0440\u0432\u0438\u0441\u044b \u0431\u0443\u0434\u0443\u0442 <s>\u043f\u0440\u043e\u0441\u0442\u044b\u0435<\/s> \u043f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0438\u0435, \u043d\u043e\u00a0\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u044b\u0435 \u0434\u043b\u044f \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438\u00a0\u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f \u043e\u0431\u0449\u0435\u0439 \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u0438. \u041f\u043e\u043b\u043d\u044b\u0439 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u0432\u00a0\u043c\u043e\u0435\u043c <a href=\"https:\/\/github.com\/VovkaST\/api-gateway\" rel=\"noopener noreferrer nofollow\">\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438<\/a>.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u0438 \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u0432 \u043d\u0435\u0433\u043e:<\/p>\n<pre><code class=\"bash\">mkdir api-gateway &amp;&amp; cd api-gateway<\/code><\/pre>\n<p>\u0421\u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u0444\u0430\u0439\u043b \u0441\u00a0\u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u043c\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u043c\u0438 \u0438 \u043d\u0430\u0437\u043e\u0432\u0435\u043c \u0435\u0433\u043e \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u043e\u043d\u043d\u043e requirements.txt, \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u0434\u0442\u044f\u043d\u0443\u0442\u0441\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438: <\/p>\n<pre><code class=\"bash\">echo \" aiohttp==3.8.3 aio-pika==8.2.5 fastapi==0.88.0 gunicorn==20.1.0 path==16.5.0 PyYAML==6.0 pyyaml-tags==1.0.4 six==1.16.0 SQLAlchemy==1.4.45 uvicorn==0.20.0 \" > requirements.txt<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0432\u00a0\u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0435.venv3.10\u00a0\u0438 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0435\u043c \u0435\u0433\u043e. \u041a\u0441\u0442\u0430\u0442\u0438, \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e Python 3.10.6\u00a0\u043f\u043e\u0434\u00a0Ubuntu 20.04:<\/p>\n<pre><code class=\"bash\">python3 -m venv .venv3.10 &amp;&amp; source .venv3.10\/bin\/activate<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u0438 \u0434\u0430\u043b\u0435\u0435 \u0432\u0441\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u044b \u0431\u0443\u0434\u0435\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043f\u043e\u0434\u00a0\u044d\u0442\u043e\u0439 \u0441\u0440\u0435\u0434\u043e\u0439.<\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c RabbitMQ. \u042f \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0438\u0442\u0430\u044e \u0440\u0430\u0437\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0432\u00a0Docker\u2011\u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435. \u0425\u043e\u0440\u043e\u0448\u0430\u044f \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0430\u044f \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f \u043f\u043e\u00a0\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0435 <a href=\"https:\/\/hub.docker.com\/_\/rabbitmq\" rel=\"noopener noreferrer nofollow\">\u0437\u0434\u0435\u0441\u044c<\/a>.<\/p>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441&#8230; \u041a\u043e\u043d\u0446\u0430.:\u2011)<\/p>\n<h4>1. \u0421\u0435\u0440\u0432\u0438\u0441<\/h4>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0438 \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u0432\u00a0\u043d\u0435\u0433\u043e:<\/p>\n<pre><code class=\"bash\">mkdir srv_clients &amp;&amp; cd srv_clients<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0444\u0430\u0439\u043b\u043e\u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0442\u0430\u043a\u00a0\u0436\u0435 \u043e\u0434\u043d\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439:<\/p>\n<pre><code class=\"bash\">touch __init__.py main.py schemas.py database.py models.py querysets.py<\/code><\/pre>\n<p><code>__init__.py<\/code> \u2014 <a href=\"https:\/\/docs-python.ru\/tutorial\/sistema-importa-python\/fajl-paketa-init-py\/\" rel=\"noopener noreferrer nofollow\">\u0441\u043b\u0443\u0436\u0435\u0431\u043d\u044b\u0439<\/a> \u0444\u0430\u0439\u043b \u043f\u0430\u043a\u0435\u0442\u0430 python \u0431\u0443\u0434\u0435\u0442 \u043f\u0443\u0441\u0442<\/p>\n<p><code>database.py<\/code>\u00a0\u2014 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a\u00a0\u0411\u0414<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine from sqlalchemy.orm import declarative_base, sessionmaker  engine = create_async_engine(\"sqlite+aiosqlite:\/\/\/clients.sqlite\") sm = sessionmaker(     engine, autocommit=False, autoflush=False, class_=AsyncSession )  Base = declarative_base()<\/code><\/pre>\n<p>\u0414\u043b\u044f\u00a0\u0440\u0430\u0431\u043e\u0442\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 \u0441\u0435\u0441\u0441\u0438\u0438 \u0438 SQLite \u0432\u00a0\u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0421\u0423\u0411\u0414.<\/p>\n<\/div>\n<\/details>\n<p><code>models.py<\/code> \u2014 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u043c\u043e\u0434\u0435\u043b\u0438 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440 \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from sqlalchemy import Column, Integer, Text from sqlalchemy.orm.collections import InstrumentedList  from srv_clients.database import Base   class BaseModel(Base):     __abstract__ = True     id = Column(Integer, primary_key=True, comment=\"\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\")      def to_dict(self) -> dict:         data = dict()         for k, v in self.__dict__.items():             if k.startswith(\"_\"):                 continue             elif isinstance(v, Base):                 v = v.to_dict()             elif isinstance(v, InstrumentedList):                 v = [item.to_dict() for item in v]             data[k] = v         return data<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u0431\u0430\u0437\u043e\u0432\u0443\u044e \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0442\u043d\u0443\u044e \u043c\u043e\u0434\u0435\u043b\u044c. \u0412\u00a0\u043d\u0435\u0439 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u043e \u043f\u043e\u043b\u0435 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430, \u044f\u0432\u043b\u044f\u044e\u0449\u0435\u0435\u0441\u044f \u0442\u0430\u043a\u0436\u0435 \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u043c \u043a\u043b\u044e\u0447\u043e\u043c. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0438\u0439 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 <code>to_dict<\/code> \u0434\u043b\u044f\u00a0\u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 \u043a\u043b\u0430\u0441\u0441\u0430 \u0432\u00a0\u0441\u043b\u043e\u0432\u0430\u0440\u044c. <em>\u0414\u0430, \u043e\u043d \u043d\u0435\u00a0\u043e<\/em>\u0445\u0432<em>\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0438 \u0438 \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043a\u0443\u0447\u0443 \u043e\u0448\u0438\u0431\u043e\u043a \u0438 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0431\u043e\u043b\u0435\u0435 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e\u0439 \u043f\u0440\u043e\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u043d\u043e\u00a0\u0434\u043b\u044f\u00a0\u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0435\u0433\u043e \u0431\u043e\u043b\u0435\u0435 \u0447\u0435\u043c \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e.<\/em><\/p>\n<pre><code class=\"python\">class Client(BaseModel):     __tablename__ = \"client\"     __table_args = {\"comment\": \"\u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432\"}      surname = Column(Text(length=255), nullable=False, comment=\"\u0424\u0430\u043c\u0438\u043b\u0438\u044f\")     name = Column(Text(length=255), nullable=False, comment=\"\u0418\u043c\u044f\")     country = Column(Text(length=255), comment=\"\u0421\u0442\u0440\u0430\u043d\u0430\")<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0443\u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u043e\u0442 <code>BaseModel<\/code> \u043c\u043e\u0434\u0435\u043b\u044c \u041a\u043b\u0438\u0435\u043d\u0442\u0430, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u00a0\u043d\u0435\u0439 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0439 \u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0445 \u043f\u043e\u043b\u0435\u0439 \u0434\u043b\u0438\u043d\u043e\u0439 \u0434\u043e 255\u00a0\u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432 \u0442\u0430\u043a\u00a0\u0436\u0435 \u0441\u00a0\u043f\u043e\u044f\u0441\u043d\u0435\u043d\u0438\u044f\u043c\u0438. <\/p>\n<\/div>\n<\/details>\n<p><code>querysets.py<\/code> \u2014 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043d\u0430\u0431\u043e\u0440\u044b \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0434\u043b\u044f\u00a0\u0440\u0430\u0431\u043e\u0442\u044b \u0441\u00a0\u0434\u0430\u043d\u043d\u044b\u043c\u0438<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from typing import List  from sqlalchemy import delete, select, update from sqlalchemy.ext.asyncio import AsyncSession  from srv_clients.models import Client   class ClientQueryset:     model = Client      @classmethod     async def create(cls, session: AsyncSession, **kwargs) -> int:         created = cls.model(**kwargs)         session.add(created)         await session.flush([created])         return created.id<\/code><\/pre>\n<p>\u041c\u0435\u0442\u043e\u0434 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438. \u0422\u0430\u043a \u043a\u0430\u043a\u00a0\u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u0438\u043d\u043a\u0440\u0435\u043c\u0435\u043d\u0442\u043e\u043c \u0438 \u0432\u00a0\u043f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0438\u0445 \u0443\u0441\u043b\u043e\u0432\u0438\u044f\u0445 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438, \u043f\u0440\u0438\u043d\u0443\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u044b\u0437\u043e\u0432\u0435\u043c \u043c\u0435\u0442\u043e\u0434 \u0441\u0435\u0441\u0441\u0438\u0438 <code>flush<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0431\u0440\u043e\u0441\u0438\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432\u00a0\u0411\u0414 \u0438 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0443 <code>id<\/code> \u043f\u0440\u0438\u0441\u0432\u043e\u0438\u0442\u0441\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435.<\/p>\n<pre><code class=\"python\">    @classmethod     async def get_by_id(cls, session: AsyncSession, id_: int) -> Client:         return await session.scalar(             select(cls.model).where(cls.model.id == id_)         )      @classmethod     async def get_multiple(         cls,         session: AsyncSession,         limit: int = None,         offset: int = None,         **filters,     ) -> List[Client]:         where = list()         if surname := filters.get(\"surname\"):             where.append(cls.model.surname == surname)         if name := filters.get(\"name\"):             where.append(cls.model.name == name)         if country := filters.get(\"country\"):             where.append(cls.model.country == country)         return await session.scalars(             select(cls.model).where(*where).limit(limit).offset(offset)         )<\/code><\/pre>\n<p>\u0414\u043b\u044f\u00a0\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043f\u043e\u00a0\u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0443 \u0438 \u043e\u0442\u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u0441\u043f\u0438\u0441\u043a\u0430 \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043c\u0435\u0442\u043e\u0434\u044b \u0441\u0435\u0441\u0441\u0438\u0438 <code>scalar<\/code> \u0438 <code>scalars<\/code> \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e. \u0423\u0441\u043b\u043e\u0432\u0438\u044f \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u0438 \u043e\u0436\u0438\u0434\u0430\u0435\u043c \u0432\u00a0\u0432\u0438\u0434\u0435 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u0435\u0439 \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u0438 \u0441\u00a0\u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 <code>limit<\/code> \u0438 <code>offset<\/code>.<\/p>\n<pre><code class=\"python\">    @classmethod     async def update(cls, session: AsyncSession, id_: int, **kwargs) -> None:         await session.execute(             update(cls.model).where(cls.model.id == id_).values(**kwargs)         )      @classmethod     async def delete(cls, session: AsyncSession, id_: int) -> None:         await session.execute(delete(cls.model).where(cls.model.id == id_)) <\/code><\/pre>\n<p>\u0412\u0441\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u043e\u0431\u0435\u0440\u043d\u0443\u0442\u044b \u0432\u00a0\u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 <code><a class=\"mention\" href=\"\/users\/classmethod\">@classmethod<\/a><\/code> \u0434\u043b\u044f\u00a0\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0432\u044b\u0437\u043e\u0432\u0430 \u0431\u0435\u0437\u00a0\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 \u043a\u043b\u0430\u0441\u0441\u0430.<\/p>\n<\/div>\n<\/details>\n<p><code>schemas.py<\/code> \u2014 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0432\u0445\u043e\u0434\u043d\u044b\u0435\/\u0432\u044b\u0445\u043e\u0434\u043d\u044b\u0435 \u0441\u0445\u0435\u043c\u044b \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from pydantic import BaseModel, Field   class ClientSchema(BaseModel):     surname: str = Field(..., description=\"\u0424\u0430\u043c\u0438\u043b\u0438\u044f\")     name: str = Field(..., description=\"\u0418\u043c\u044f\")     country: str = Field(..., description=\"\u0421\u0442\u0440\u0430\u043d\u0430\")   class ClientViewSchema(ClientSchema):     id: int = Field(..., description=\"\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\") <\/code><\/pre>\n<p>\u0412\u0441\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 (\u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u00a0\u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043f\u0435\u0440\u0435\u0434\u0430\u0434\u0438\u043c \u0442\u0438\u043f <code>ellipsis<\/code>\u00a0\u2014 <code>...<\/code>). \u041a\u00a0\u043c\u043e\u0434\u0435\u043b\u0438 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0430\u0442\u0440\u0438\u0431\u0443\u0442 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430.<\/p>\n<\/div>\n<\/details>\n<p><code>main.py<\/code> \u2014 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u0435\u043c\u044b\u0439 \u043a\u043e\u0434 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from typing import List  from fastapi import FastAPI, HTTPException, Request from fastapi.params import Query from starlette import status  from srv_clients.database import Base, engine from srv_clients.database import sm as session_maker from srv_clients.querysets import ClientQueryset from srv_clients.schemas import ClientSchema, ClientViewSchema  app = FastAPI() app.state.engine = engine app.state.session_maker = session_maker<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043a\u00a0\u043d\u0435\u043c\u0443 \u0432\u00a0\u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a\u00a0\u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440 \u0441\u0435\u0441\u0441\u0438\u0439. \u0414\u0435\u043b\u0430\u0435\u043c \u044d\u0442\u043e \u0434\u043b\u044f\u00a0\u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u043c\u0435\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u043a\u00a0\u043d\u0438\u043c \u0432\u043d\u0443\u0442\u0440\u0438 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u043e\u0432 \u0447\u0435\u0440\u0435\u0437 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440<code>Request<\/code>.<\/p>\n<pre><code class=\"python\">@app.on_event(\"startup\") async def on_startup():     async with app.state.engine.begin() as conn:         await conn.run_sync(Base.metadata.create_all)   @app.on_event(\"shutdown\") async def on_shutdown():     # async with app.state.engine.begin() as conn:     #     await conn.run_sync(Base.metadata.drop_all)     await app.state.engine.dispose()<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0434\u0432\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f:<\/p>\n<ul>\n<li>\n<p><code>startup<\/code>\u2014 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u043c \u043c\u043e\u0434\u0435\u043b\u0438 SQLAlchemy \u0432\u00a0\u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0411\u0414;<\/p>\n<\/li>\n<li>\n<p><code>shutdown<\/code>\u2014 \u043e\u0447\u0438\u0449\u0430\u0435\u043c \u0411\u0414 \u043f\u0443\u0442\u0435\u043c \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0432\u0441\u0435\u0445 \u0442\u0430\u0431\u043b\u0438\u0446 (\u0437\u0430\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043e) \u0438 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0435\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441\u00a0\u0411\u0414.<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"python\">@app.get(\"\/clients\", response_model=List[ClientViewSchema]) async def get_filtered_clients(     request: Request,     surname: str = Query(None, description=\"\u0424\u0430\u043c\u0438\u043b\u0438\u044f\"),     name: str = Query(None, description=\"\u0418\u043c\u044f\"),     country: str = Query(None, description=\"\u0421\u0442\u0440\u0430\u043d\u0430\"),     limit: int = Query(         None, description=\"\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432\"     ),     offset: int = Query(         None,         description=\"\u0421\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0435\u0440\u0432\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438\",     ), ):     async with request.app.state.session_maker() as session:         rows = await ClientQueryset.get_multiple(             session,             surname=surname,             name=name,             country=country,             limit=limit,             offset=offset,         )         return [row.to_dict() for row in rows.all()]   @app.post(     \"\/clients\",     response_model=ClientViewSchema,     status_code=status.HTTP_201_CREATED, ) async def create_client(request: Request, data: ClientSchema):     sm = request.app.state.session_maker     async with sm.begin() as session:         id_ = await ClientQueryset.create(session, **data.dict())     async with sm() as session:         client = await ClientQueryset.get_by_id(session, id_)         return client.to_dict()   @app.get(\"\/clients\/{client_id}\", response_model=ClientViewSchema) async def get_client(request: Request, client_id: int):     async with request.app.state.session_maker() as session:         client = await ClientQueryset.get_by_id(session, client_id)         if not client:             raise HTTPException(                 status_code=status.HTTP_404_NOT_FOUND,                 detail=\"Client not found\",             )         return client.to_dict()   @app.patch(     \"\/clients\/{client_id}\",     response_model=ClientViewSchema,     status_code=status.HTTP_202_ACCEPTED, ) async def update_client(request: Request, client_id: int, data: ClientSchema):     sm = request.app.state.session_maker     async with sm.begin() as session:         client = await ClientQueryset.get_by_id(session, client_id)         if not client:             raise HTTPException(                 status_code=status.HTTP_404_NOT_FOUND, detail=\"Client not found\"             )         await ClientQueryset.update(session, client_id, **data.dict())      async with sm() as session:         client = await ClientQueryset.get_by_id(session, client_id)         return client.to_dict()   @app.delete(     \"\/clients\/{client_id}\",     status_code=status.HTTP_202_ACCEPTED, ) async def delete_client(request: Request, client_id: int):     sm = request.app.state.session_maker     async with sm.begin() as session:         client = await ClientQueryset.get_by_id(session, client_id)         if not client:             raise HTTPException(                 status_code=status.HTTP_404_NOT_FOUND, detail=\"User not found\"             )         await ClientQueryset.delete(session, client_id)<\/code><\/pre>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0437\u0434\u0435\u0441\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 CRUD \u0441\u00a0\u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044f\u043c\u0438. \u0414\u043b\u044f\u00a0\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439, \u0432\u043d\u043e\u0441\u044f\u0449\u0438\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432\u00a0\u0411\u0414, \u043e\u0442\u043a\u0440\u043e\u0435\u043c \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044e (\u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0442\u043e\u0447\u043a\u0443 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f\u00a0\u2014 savepoint) \u0441\u00a0\u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430 <code>begin<\/code> \u0438 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u0430 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430, \u043f\u0440\u0438\u00a0\u0432\u044b\u0445\u043e\u0434\u0435 \u0438\u0437\u00a0\u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043e\u043d\u0430 \u0431\u0443\u0434\u0435\u0442 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0437\u0430\u043a\u0440\u044b\u0442\u0430 (\u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u043d \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440 <code>commit<\/code>). \u0412\u00a0\u0441\u043b\u0443\u0447\u0430\u0435 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u044f \u043a\u0430\u043a\u0438\u0445\u2011\u043b\u0438\u0431\u043e \u043e\u0448\u0438\u0431\u043e\u043a \u0432\u043d\u0443\u0442\u0440\u0438 \u0431\u043b\u043e\u043a\u0430 <code>with<\/code> \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044f \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u0441\u044f \u043e\u0442\u043a\u0430\u0442\u043e\u043c (\u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u043d \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440 <code>rollback<\/code>).<\/p>\n<p>\u0414\u043b\u044f\u00a0\u0440\u0430\u0437\u043d\u043e\u043e\u0431\u0440\u0430\u0437\u0438\u044f \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u044e\u00a0\u2014<code>get_filtered_clients<\/code>.<\/p>\n<p>\u0414\u043b\u044f\u00a0\u0441\u043b\u0443\u0447\u0430\u0435\u0432, \u043a\u043e\u0433\u0434\u0430 \u0437\u0430\u043f\u0438\u0441\u044c \u0438\u0437\u00a0\u0411\u0414 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043e\u043b\u0436\u043d\u0430\u00a0\u0431\u044b\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0430 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0440\u0438\u00a0\u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0438\u043b\u0438\u00a0\u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0438), \u0432\u044b\u0431\u0440\u043e\u0441\u0438\u043c \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 <code>HTTPException<\/code> \u043a\u043e\u0433\u0434\u0430 \u043e\u043d\u0430 \u043d\u0435\u00a0\u043d\u0430\u0439\u0434\u0435\u043d\u0430.<\/p>\n<p>\u0414\u043b\u044f\u00a0\u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043e\u0442\u0432\u0435\u0442\u0430 \u0432\u00a0\u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u0441\u0445\u0435\u043c\u044b, \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0435\u043c\u043e\u0439 \u0432\u00a0\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435 <code>response_model<\/code>\u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430, \u0438\u043b\u0438\u00a0\u0438\u0445 \u0441\u043f\u0438\u0441\u043e\u043a, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0449\u0430\u044f \u0435\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043e\u043b\u0436\u043d\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0438\u043b\u0438\u00a0\u0441\u043f\u0438\u0441\u043e\u043a \u0441\u043b\u043e\u0432\u0430\u0440\u0435\u0439. \u0422\u0443\u0442 \u043d\u0430\u043c \u0438 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u0441\u044f \u043c\u0435\u0442\u043e\u0434 <code>to_dict<\/code> \u0431\u0430\u0437\u043e\u0432\u043e\u0439 \u043c\u043e\u0434\u0435\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/div>\n<\/details>\n<p>\u041c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c! \u041d\u0430\u043f\u0438\u0448\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443:<\/p>\n<pre><code class=\"bash\">python -m gunicorn --bind 0.0.0.0:8001 --workers 4 -k uvicorn.workers.UvicornWorker main:app<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c, \u0447\u0442\u043e\u00a0\u0441\u0435\u0440\u0432\u0438\u0441 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043f\u043e\u00a0\u0430\u0434\u0440\u0435\u0441\u0443 <code>0.0.0.0<\/code> \u0438 \u043d\u0430 <code>8001<\/code> \u043f\u043e\u0440\u0442\u0435, \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u043e 4\u00a0\u0432\u043e\u0440\u043a\u0435\u0440\u0430 \u043a\u043b\u0430\u0441\u0441\u0430 <code>UvicornWorker<\/code> (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u00a0\u2014 <a href=\"https:\/\/www.uvicorn.org\/deployment\/\" rel=\"noopener noreferrer nofollow\">\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u0432\u00a0\u043f\u0435\u0440\u0432\u043e\u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0435<\/a>). \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432\u00a0\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e <code>app<\/code> \u043c\u043e\u0434\u0443\u043b\u044f <code>main<\/code> (<code>main:app<\/code>).<\/p>\n<p>\u0418\u043c\u0435\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0442\u0430\u043a\u043e\u0439 \u0432\u044b\u0432\u043e\u0434:<\/p>\n<pre><code class=\"bash\">[2023-01-11 21:02:00 +0300] [29518] [INFO] Starting gunicorn 20.1.0 [2023-01-11 21:02:00 +0300] [29518] [INFO] Listening at: http:\/\/0.0.0.0:8001 (29518) [2023-01-11 21:02:00 +0300] [29518] [INFO] Using worker: uvicorn.workers.UvicornWorker [2023-01-11 21:02:00 +0300] [29520] [INFO] Booting worker with pid: 29520 [2023-01-11 21:02:00 +0300] [29521] [INFO] Booting worker with pid: 29521 [2023-01-11 21:02:00 +0300] [29522] [INFO] Booting worker with pid: 29522 [2023-01-11 21:02:00 +0300] [29523] [INFO] Booting worker with pid: 29523 [2023-01-11 21:02:01 +0300] [29521] [INFO] Started server process [29521] [2023-01-11 21:02:01 +0300] [29521] [INFO] Waiting for application startup. [2023-01-11 21:02:01 +0300] [29523] [INFO] Started server process [29523] [2023-01-11 21:02:01 +0300] [29523] [INFO] Waiting for application startup. [2023-01-11 21:02:01 +0300] [29520] [INFO] Started server process [29520] [2023-01-11 21:02:01 +0300] [29520] [INFO] Waiting for application startup. [2023-01-11 21:02:01 +0300] [29522] [INFO] Started server process [29522] [2023-01-11 21:02:01 +0300] [29522] [INFO] Waiting for application startup. [2023-01-11 21:02:01 +0300] [29521] [INFO] Application startup complete. [2023-01-11 21:02:01 +0300] [29522] [INFO] Application startup complete. [2023-01-11 21:02:01 +0300] [29523] [INFO] Application startup complete. [2023-01-11 21:02:01 +0300] [29520] [INFO] Application startup complete.<\/code><\/pre>\n<h4>2. RPC-\u0441\u0435\u0440\u0432\u0435\u0440<\/h4>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0438\u0437\u00a0\u043e\u0447\u0435\u0440\u0435\u0434\u0438, \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0438\u0445 \u0432\u00a0\u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0432\u00a0\u044d\u0442\u0443\u00a0\u0436\u0435 \u043e\u0447\u0435\u0440\u0435\u0434\u044c. \u041d\u0430\u00a0\u044d\u0442\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u043e\u0441\u0432\u0435\u0442\u0438\u043c \u0434\u0432\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0432\u043e\u043f\u0440\u043e\u0441\u0430: \u0440\u0430\u0431\u043e\u0442\u0443 \u0441\u00a0\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u043c\u0438 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f, \u0432\u00a0\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f\u00a0\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a\u00a0\u043e\u0447\u0435\u0440\u0435\u0434\u0438, \u0438 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0443 \u0441\u00a0\u043e\u0447\u0435\u0440\u0435\u0434\u044c\u044e RabbitMQ. <em>\u041a\u0441\u0442\u0430\u0442\u0438, <\/em><a href=\"https:\/\/habr.com\/ru\/company\/southbridge\/blog\/703060\/\" rel=\"noopener noreferrer nofollow\"><em>\u0437\u0434\u0435\u0441\u044c<\/em><\/a><em> \u0435\u0441\u0442\u044c \u0445\u043e\u0440\u043e\u0448\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f \u043e\u00a0\u0435\u0433\u043e \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043f\u043e\u043d\u044f\u0442\u0438\u044f.<\/em><\/p>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441\u00a0\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u043f\u0430\u043a\u0435\u0442\u0430. \u0412\u0435\u0440\u043d\u0435\u043c\u0441\u044f \u0432\u00a0\u043a\u043e\u0440\u0435\u043d\u044c \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u0430\u0442\u0430\u043b\u043e\u0433 <code>rpc<\/code> \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0438 \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u0432\u00a0\u043d\u0435\u0433\u043e:<\/p>\n<pre><code class=\"bash\">cd .. &amp;&amp; mkdir rpc &amp;&amp; cd rpc<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0444\u0430\u0439\u043b\u043e\u0432 \u0442\u0430\u043a\u00a0\u0436\u0435 \u043e\u0434\u043d\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439:<\/p>\n<pre><code class=\"bash\">touch __init__.py common.py resolver.py server.py<\/code><\/pre>\n<p>\u0412\u043b\u043e\u0436\u0438\u043c \u0441\u044e\u0434\u0430 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u0441\u00a0\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u0438 <code>.env<\/code> \u0444\u0430\u0439\u043b\u043e\u043c:<\/p>\n<pre><code class=\"bash\">mkdir config &amp;&amp; touch config\/.env config\/local.yaml<\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0442\u0430\u043a\u00a0\u0436\u0435 \u043f\u043e\u00a0\u043f\u043e\u0440\u044f\u0434\u043a\u0443.<\/p>\n<p><code>__init__.py<\/code>\u2014 \u0441\u043b\u0443\u0436\u0435\u0431\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u043f\u0430\u043a\u0435\u0442\u0430 python \u0431\u0443\u0434\u0435\u0442 \u043f\u0443\u0441\u0442.<\/p>\n<p><code>common.py<\/code> \u2014 \u043e\u0431\u0449\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u0435\u0440\u0432\u0435\u0440\u0430.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<p>\u0417\u0434\u0435\u0441\u044c \u043e\u0442\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0435 \u043e\u0442\u00a0\u043f\u0435\u0440\u0432\u0438\u0447\u043d\u043e\u0439 \u0446\u0435\u043b\u0438\u00a0\u2014 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0432\u00a0yaml\u2011\u0444\u0430\u0439\u043b\u0435, \u0430\u00a0\u0442\u0430\u043a\u0436\u0435 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u043a\u00a0\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u043c \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0438\u0437\u00a0\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430. \u0414\u043b\u044f\u00a0\u044d\u0442\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <a href=\"https:\/\/pypi.org\/project\/pyyaml-tags\/\" rel=\"noopener noreferrer nofollow\">PyYaml\u2011Tags<\/a>. \u041f\u043e\u043a\u0430 \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0431\u044a\u044f\u0432\u0438\u043c \u043a\u043b\u0430\u0441\u0441 \u0438 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 <code>_from_yaml<\/code>. \u0412\u044b\u0437\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 \u0447\u0443\u0442\u044c \u043d\u0438\u0436\u0435.<\/p>\n<pre><code class=\"python\">import os from pathlib import Path  import yaml from dotenv import load_dotenv from yaml_tags import BaseTag, tag_registry   @tag_registry.register(\"env_tag\") class EnvTag(BaseTag):     def _from_yaml(         self,         _loader,         _work_dir,         _prefix,         _suffix,         param=None,         *args,         **kwargs,     ):         result = os.environ.get(param, \"\")         return f\"{_prefix}{result}{_suffix}\"<\/code><\/pre>\n<p>\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0441\u0432\u043e\u0439 \u0442\u044d\u0433 <code>env_tag<\/code> \u0438 \u043c\u0435\u0442\u043e\u0434 \u0435\u0433\u043e \u0438 \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<pre><code class=\"python\">def load_config(config_path: Path) -> dict:     tag_registry.require(\"env_tag\")     env_path = f\"{config_path.absolute().parent}\/.env\"     load_dotenv(dotenv_path=env_path)     with open(config_path) as f:         return yaml.load(f, Loader=yaml.Loader)   def get_config_path(file_name: str) -> Path:     current_dir = Path(__file__).absolute().parent     return current_dir \/ \"config\" \/ file_name<\/code><\/pre>\n<p>\u0422\u0443\u0442 \u043c\u044b \u0441\u0447\u0438\u0442\u0430\u0435\u043c .env \u0444\u0430\u0439\u043b. \u0411\u0443\u0434\u0435\u043c \u043e\u0436\u0438\u0434\u0430\u0442\u044c \u0435\u0433\u043e \u0432\u00a0\u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0435 \u0441\u00a0\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u043c(\u2011\u043c\u0438) \u0444\u0430\u0439\u043b\u043e\u043c(\u2011\u0430\u043c\u0438).<\/p>\n<p><code>local.yaml:<\/code><\/p>\n<pre><code class=\"yaml\">rmq:   url: amqp:\/\/gateway:&lt;% env_tag(param=\"RMQ_PASS\") %>@127.0.0.1:5672\/gateway<\/code><\/pre>\n<p>\u0412\u00a0\u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0445\u0440\u0430\u043d\u0438\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0440\u043e\u043a\u0443 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a\u00a0RabbitMQ.<\/p>\n<p><code>.env:<\/code><\/p>\n<pre><code class=\"bash\">RMQ_PASS=gateway<\/code><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u043a\u00a0\u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u043c\u0443 \u0442\u044d\u0433\u0443 \u043f\u0440\u043e\u0441\u0442\u043e\u0435: <code>&lt;% [\u0438\u043c\u044f_\u0442\u044d\u0433\u0430]([\u0438\u043c\u044f_\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e_\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430]=[\u0438\u043c\u044f_\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439_\u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f]) %><\/code><\/p>\n<\/div>\n<\/details>\n<p><code>resolver.py<\/code>\u2014 \u0445\u0440\u0430\u043d\u0438\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043c\u0435\u0436\u0434\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from typing import Tuple  import aiohttp  SERVICE_MAP = {     \"clients\": \"0.0.0.0:8001\",     \"goods\": \"0.0.0.0:8002\", }   async def resolve(     method: str,     path: str,     params: dict = None,     data: dict = None,     headers: dict = None, ) -> Tuple[dict, int]:     \"\"\"     \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u043e \u043f\u0435\u0440\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 url-\u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043a \u043a\u0430\u043a\u043e\u043c\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0443      \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0435, \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0432 \u043d\u0435\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442      \u043e\u0442\u0432\u0435\u0442 \u0432 \u0432\u0438\u0434\u0435 \u0441\u043b\u043e\u0432\u0430\u0440\u044f \u0438 \u043a\u043e\u0434 \u0441\u0442\u0430\u0442\u0443\u0441\u0430.     \"\"\"     _, service_name, *_ = path.split(\"\/\")     service_host = SERVICE_MAP[service_name]     url = f\"http:\/\/{service_host}{path}\"     response, status_code = await make_request(         url, method, params, data, headers     )     return response, status_code   async def make_request(     url: str,     method: str,     params: dict = None,     data: dict = None,     headers: dict = None, ) -> Tuple[dict, int]:     \"\"\"     \u0412\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u0442\u0438\u043f \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c method,     \u043f\u043e url-\u0430\u0434\u0440\u0435\u0441\u0443. \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043e\u0442\u0432\u0435\u0442 \u0432 \u0432\u0438\u0434\u0435 \u0441\u043b\u043e\u0432\u0430\u0440\u044f \u0438 \u043a\u043e\u0434 \u0441\u0442\u0430\u0442\u0443\u0441\u0430.     \"\"\"          if not headers:         headers = {}     if not data:         data = {}      async with aiohttp.ClientSession() as session:         request = getattr(session, method)         async with await request(             url, params=params, json=data, headers=headers         ) as response:             response_json = await response.json()             return response_json, response.status <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p><code>server.py<\/code>\u2014 \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u0435\u043c\u044b\u0439 \u043a\u043e\u0434 RPC\u2011\u0441\u0435\u0440\u0432\u0435\u0440\u0430.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import asyncio  from aio_pika import connect_robust from aio_pika.patterns import RPC  from common import get_config_path, load_config from resolver import resolve   async def server() -> None:     config = load_config(get_config_path(\"local.yaml\"))     rmq = config[\"rmq\"]      connection = await connect_robust(rmq[\"url\"])      async with connection:         channel = await connection.channel()         rpc = await RPC.create(channel)         await rpc.register(resolve.__name__, resolve, auto_delete=True)              try:             await asyncio.Future()         finally:             await connection.close()   if __name__ == \"__main__\":     asyncio.run(server()) <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u0432\u0441\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e. \u0421\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b, \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u044b\u0432\u0430\u0435\u043c \u0435\u0433\u043e \u0432\u00a0\u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a\u00a0\u043e\u0447\u0435\u0440\u0435\u0434\u0438 RabbitMQ. \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u044d\u0442\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435, \u043a\u0430\u043d\u0430\u043b \u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a. \u041a\u0441\u0442\u0430\u0442\u0438, \u0432\u00a0\u043f\u0430\u043a\u0435\u0442\u0435 <code>aio_pika<\/code> \u0435\u0441\u0442\u044c \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0448\u0430\u0431\u043b\u043e\u043d\u044b, \u043a\u0440\u043e\u043c\u0435 <a href=\"https:\/\/aio-pika.readthedocs.io\/en\/latest\/apidoc.html#aio_pika.patterns.RPC\" rel=\"noopener noreferrer nofollow\">RPC<\/a>.<\/p>\n<\/div>\n<\/details>\n<p>\u0422\u0443\u0442 \u0442\u0430\u043a\u0436\u0435 \u0432\u0441\u0435 \u0433\u043e\u0442\u043e\u0432\u043e. \u041c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c:<\/p>\n<pre><code class=\"bash\">python server.py<\/code><\/pre>\n<p>\u0421\u0435\u0440\u0432\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0442\u044c, \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0440\u0430\u0437 \u0432\u00a0\u0440\u0430\u0437\u043d\u044b\u0445 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0430\u0445. \u0417\u0430\u043f\u0440\u043e\u0441\u044b \u0431\u0443\u0434\u0443\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0442\u044c\u0441\u044f \u043c\u0435\u0436\u0434\u0443 \u043a\u0430\u043d\u0430\u043b\u0430\u043c\u0438 \u043e\u0447\u0435\u0440\u0435\u0434\u0435\u0439.<\/p>\n<p>\u0417\u0430\u0433\u043b\u044f\u043d\u0443\u0432 \u0432\u00a0\u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 RabbtiMQ, \u0443\u0432\u0438\u0434\u0438\u043c \u0447\u0442\u043e\u00a0\u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u043e\u0434\u0438\u043d \u043e\u0431\u043c\u0435\u043d\u043d\u0438\u043a <code>rpc.dlx<\/code>:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/bcd\/5a9\/e7e\/bcd5a9e7e3de8b9cc8f28acdfbb61f7f.jpg\" alt=\"\u0412\u043a\u043b\u0430\u0434\u043a\u0430 Exchanges\" title=\"\u0412\u043a\u043b\u0430\u0434\u043a\u0430 Exchanges\" width=\"615\" height=\"431\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/bcd\/5a9\/e7e\/bcd5a9e7e3de8b9cc8f28acdfbb61f7f.jpg\" data-blurred=\"true\"\/><figcaption>\u0412\u043a\u043b\u0430\u0434\u043a\u0430 Exchanges<\/figcaption><\/figure>\n<p>\u0418 \u0434\u0432\u0435 \u043e\u0447\u0435\u0440\u0435\u0434\u0438:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/6a5\/369\/385\/6a53693850c72cdec65cc1eb5a597484.jpg\" alt=\"\u0412\u043a\u043b\u0430\u0434\u043a\u0430 Queues\" title=\"\u0412\u043a\u043b\u0430\u0434\u043a\u0430 Queues\" width=\"1082\" height=\"127\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/6a5\/369\/385\/6a53693850c72cdec65cc1eb5a597484.jpg\" data-blurred=\"true\"\/><figcaption>\u0412\u043a\u043b\u0430\u0434\u043a\u0430 Queues<\/figcaption><\/figure>\n<p>\u0421\u0445\u0435\u043c\u0430 \u0440\u0430\u0431\u043e\u0442\u044b \u0431\u0443\u0434\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/1b2\/9e7\/1f7\/1b29e71f7d14f7538209e4f16a41ba00.jpg\" alt=\"\u0421\u0445\u0435\u043c\u0430 \u0440\u0430\u0431\u043e\u0442\u044b RPC \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c RabbitMQ\" title=\"\u0421\u0445\u0435\u043c\u0430 \u0440\u0430\u0431\u043e\u0442\u044b RPC \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c RabbitMQ\" width=\"1033\" height=\"297\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1b2\/9e7\/1f7\/1b29e71f7d14f7538209e4f16a41ba00.jpg\" data-blurred=\"true\"\/><figcaption>\u0421\u0445\u0435\u043c\u0430 \u0440\u0430\u0431\u043e\u0442\u044b RPC \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c RabbitMQ<\/figcaption><\/figure>\n<ul>\n<li>\n<p>\u041a\u043e\u0433\u0434\u0430 \u043a\u043b\u0438\u0435\u043d\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f, \u043e\u043d \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u0430\u043d\u043e\u043d\u0438\u043c\u043d\u0443\u044e \u044d\u043a\u0441\u043a\u043b\u044e\u0437\u0438\u0432\u043d\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430 (\u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 <code>amq_0x4e0bc6f2bc4e3df862a47b2b772bec3d<\/code>);<\/p>\n<\/li>\n<li>\n<p>\u0414\u043b\u044f\u00a0\u0437\u0430\u043f\u0440\u043e\u0441\u0430 RPC \u043a\u043b\u0438\u0435\u043d\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0441\u00a0\u0434\u0432\u0443\u043c\u044f \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438: <code>answer_to<\/code>, \u0434\u043b\u044f\u00a0\u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0437\u0430\u0434\u0430\u043d\u0430 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430, \u0438 <code>corellation_id<\/code>, \u0434\u043b\u044f\u00a0\u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u043b\u044f\u00a0\u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430;<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u0440\u043e\u0441 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u00a0\u043e\u0447\u0435\u0440\u0435\u0434\u044c <code>resolve<\/code>;<\/p>\n<\/li>\n<li>\n<p>RPC\u2011\u0441\u0435\u0440\u0432\u0435\u0440 \u043e\u0436\u0438\u0434\u0430\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432\u00a0\u044d\u0442\u043e\u0439 \u043e\u0447\u0435\u0440\u0435\u0434\u0438. \u041a\u043e\u0433\u0434\u0430 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0437\u0430\u043f\u0440\u043e\u0441, \u043e\u043d \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0441\u00a0\u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u043c \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u041a\u043b\u0438\u0435\u043d\u0442\u0443, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0438\u0437\u00a0\u043f\u043e\u043b\u044f <code>reply_to<\/code>;<\/p>\n<\/li>\n<li>\n<p>\u041a\u043b\u0438\u0435\u043d\u0442 \u043e\u0436\u0438\u0434\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432\u00a0\u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430. \u041a\u043e\u0433\u0434\u0430 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435, \u043e\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>corellation_id<\/code> . \u0415\u0441\u043b\u0438 \u043e\u043d \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044e \u0438\u0437\u00a0\u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043e\u043d \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043e\u0442\u0432\u0435\u0442 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e.<\/p>\n<\/li>\n<\/ul>\n<h4>3. Gateway<\/h4>\n<p>\u041d\u0430\u0448 \u0448\u043b\u044e\u0437 \u0434\u043e\u043b\u0436\u0435\u043d \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0442\u044c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043a\u043e\u043d\u0435\u0447\u043d\u044b\u0445 \u0442\u043e\u0447\u0435\u043a \u0432\u0441\u0435\u0445 \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432, \u0430\u00a0\u0442\u0430\u043a\u0436\u0435 \u0441\u0445\u0435\u043c \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0438 \u0432\u044b\u0445\u043e\u0434\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u044b\u0434\u0435\u043b\u0438\u043c \u0434\u043b\u044f\u00a0\u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u0430\u0442\u0430\u043b\u043e\u0433.<\/p>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441\u00a0\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b. \u0412\u0435\u0440\u043d\u0435\u043c\u0441\u044f \u0432\u00a0\u043a\u043e\u0440\u0435\u043d\u044c \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u0430\u0442\u0430\u043b\u043e\u0433 <code>gateway<\/code> \u0438 \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u0432\u00a0\u043d\u0435\u0433\u043e:<\/p>\n<pre><code class=\"bash\">cd .. &amp;&amp; mkdir gateway &amp;&amp; cd gateway<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0444\u0430\u0439\u043b\u043e\u0432 \u0438 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u043e\u0432 \u0442\u0430\u043a\u00a0\u0436\u0435 \u043e\u0434\u043d\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439:<\/p>\n<pre><code class=\"bash\">touch __init__.py rabbit.py common.py main.py &amp;&amp; mkdir config &amp;&amp; touch config\/.env config\/local.yaml &amp;&amp; mkdir srv_clients &amp;&amp; touch srv_clients\/__init__.py srv_clients\/routes.py srv_clients\/schemas.py<\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0441\u043d\u043e\u0432\u0430 \u043f\u043e\u00a0\u043f\u043e\u0440\u044f\u0434\u043a\u0443.<\/p>\n<p><code>__init__.py<\/code>\u00a0\u2014 \u0441\u043b\u0443\u0436\u0435\u0431\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u043f\u0430\u043a\u0435\u0442\u0430 python \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u043e\u043d\u043d\u043e \u043f\u0443\u0441\u0442.<\/p>\n<p><code>rabbit.py<\/code>\u00a0\u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u00a0\u043e\u0447\u0435\u0440\u0435\u0434\u044f\u043c\u0438 RabbitMQ.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from typing import Tuple  from aio_pika import connect_robust from aio_pika.patterns import RPC   async def send_request_to_queue(     config: dict, message: dict ) -> Tuple[dict, int]:     rmq = config[\"rmq\"]     connection = await connect_robust(rmq[\"url\"])      async with connection:         channel = await connection.channel()         rpc = await RPC.create(channel)         result, status_code = await rpc.proxy.resolve(**message)         return result, status_code<\/code><\/pre>\n<p>\u0418\u0437\u00a0\u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0434\u043b\u044f\u00a0\u0440\u0430\u0431\u043e\u0442\u044b \u0441\u00a0\u043e\u0447\u0435\u0440\u0435\u0434\u044c\u044e \u0438 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435. \u041e\u0431\u043e\u0437\u043d\u0430\u0447\u0430\u0435\u043c, \u0447\u0442\u043e\u00a0\u0445\u043e\u0442\u0438\u043c \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>resolve<\/code>. \u041e\u043d\u0430, \u043a\u0430\u043a\u00a0\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0438 \u0447\u0438\u0441\u043b\u043e\u0432\u043e\u0439 \u043a\u043e\u0434 \u0441\u0442\u0430\u0442\u0443\u0441\u0430.<\/p>\n<\/div>\n<\/details>\n<p><code>common.py<\/code> \u2014 \u043e\u0431\u0449\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u0435\u0440\u0432\u0435\u0440\u0430.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<p>\u041f\u043e\u00a0\u0431\u043e\u043b\u044c\u0448\u0435\u0439 \u0447\u0430\u0441\u0442\u0438 \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0442\u044c \u043a\u043e\u0434 rpc\u2011\u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0441\u00a0\u043e\u0434\u043d\u0438\u043c \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\u043c\u00a0\u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439 <code>router<\/code>.<\/p>\n<pre><code class=\"python\">import os from functools import wraps from pathlib import Path from typing import Any, Optional  import yaml from dotenv import load_dotenv from fastapi import HTTPException, Request, Response from starlette import status from yaml_tags import BaseTag, tag_registry  from gateway.rabbit import send_request_to_queue   @tag_registry.register(\"env_tag\") class EnvTag(BaseTag):     def _from_yaml(         self,         _loader,         _work_dir,         _prefix,         _suffix,         param=None,         *args,         **kwargs,     ) -> str:         result = os.environ.get(param, \"\")         return f\"{_prefix}{result}{_suffix}\"   def load_config(config_path: Path) -> dict:     tag_registry.require(\"env_tag\")     env_path = f\"{config_path.absolute().parent}\/.env\"     load_dotenv(dotenv_path=env_path)     with open(config_path) as f:         return yaml.load(f, Loader=yaml.Loader)   def get_config_path(file_name: str) -> Path:     current_dir = Path(__file__).absolute().parent     return current_dir \/ \"config\" \/ file_name   def router(     method,     path: str,     data_key: Optional[str] = None,     status_code: Optional[int] = status.HTTP_200_OK,     response_model: Optional[Any] = None, ):     \"\"\"     \u041e\u0431\u0435\u0440\u0442\u043a\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0449\u0430\u044f \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u043a RPC-\u0441\u0435\u0440\u0432\u0435\u0440\u0443.      :param method: \u0412\u044b\u0437\u044b\u0432\u0430\u0435\u043c\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 (\u0444\u0443\u043d\u043a\u0446\u0438\u044f) \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0449\u0430\u044f \u0442\u043e\u0442 \u0438\u043b\u0438                     \u0438\u043d\u043e\u0439 \u043c\u0435\u0442\u043e\u0434 http-\u0437\u0430\u043f\u0440\u043e\u0441\u0430.     :param path: \u0410\u0434\u0440\u0435\u0441 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430.     :param data_key: \u0418\u043c\u044f \u043a\u043b\u044e\u0447\u0430, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u044e\u0442\u0441\u044f \u0434\u0430\u043d\u043d\u044b\u0435                       \u043d\u0430 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442.     :param status_code: \u041e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0439 \u043a\u043e\u0434 \u043e\u0442\u0432\u0435\u0442\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430.     :param response_model: \u041c\u043e\u0434\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043e\u0442\u0432\u0435\u0442\u0430.     \"\"\"     app_method = method(         path, status_code=status_code, response_model=response_model     )      def wrapper(endpoint):         @app_method         @wraps(endpoint)         async def decorator(request: Request, response: Response, **kwargs):             request_method = request.scope[\"method\"].lower()             data = kwargs.get(data_key)             data = data.dict() if data else {}             response_data, response_status_code = await send_request_to_queue(                 config=request.app.config,                 message={                     \"method\": request_method,                     \"path\": request.scope[\"path\"],                     \"params\": dict(request.query_params),                     \"data\": data,                     \"headers\": {},                 },             )             if response_status_code >= status.HTTP_400_BAD_REQUEST:                 raise HTTPException(                     status_code=response_status_code, detail=response_data                 )             response.status_code = response_status_code             return response_data      return wrapper <\/code><\/pre>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f <code>router<\/code> \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u00a0\u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u043e\u043d\u043d\u044b\u043c \u043c\u0435\u0442\u043e\u0434\u043e\u043c \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u0432\u00a0\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440. \u0418\u0437\u00a0\u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044f \u043e\u0431\u044a\u0435\u043a\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0434\u043b\u044f\u00a0\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0432\u00a0\u043e\u0434\u0438\u043d \u0438\u0437\u00a0\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432. \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u00a0\u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0438 \u043e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f \u043e\u0442\u0432\u0435\u0442. \u0415\u0441\u043b\u0438 \u043a\u043e\u0434 \u043e\u0442\u0432\u0435\u0442\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442 \u043e\u0431\u00a0\u043e\u0448\u0438\u0431\u043a\u0435 (>= 400), \u0432\u044b\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0441\u00a0\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u043c \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u043c \u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\u043c. \u0412\u00a0\u0441\u043b\u0443\u0447\u0430\u0435 \u0443\u0441\u043f\u0435\u0445\u0430, \u043f\u043e\u0434\u043c\u0435\u043d\u044f\u0435\u043c \u0441\u0442\u0430\u0442\u0443\u0441 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 <code>response<\/code> \u043a\u043e\u0434\u043e\u043c, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u043c \u043e\u0442\u00a0\u0441\u0435\u0440\u0432\u0438\u0441\u0430.<\/p>\n<p>\u041d\u0430\u00a0\u0434\u0430\u043d\u043d\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u044c, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u043e\u0439 \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u043a\u0430\u043a\u0438\u043c\u2011\u0442\u043e \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0438 \u0432\u0441\u044f\u0447\u0435\u0441\u043a\u0438\u043c\u0438 \u043f\u043b\u044e\u0448\u043a\u0430\u043c\u0438. \u0414\u043b\u044f\u00a0\u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u043c\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u044b\u043c\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c\u2011\u043e\u0442\u0432\u0435\u0442\u043e\u043c.<\/p>\n<\/div>\n<\/details>\n<p><code>main.py<\/code>\u00a0\u2014 \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u0435\u043c\u044b\u0439 \u043a\u043e\u0434 \u0448\u043b\u044e\u0437\u0430.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from fastapi import FastAPI  from common import get_config_path, load_config from gateway.srv_clients.routes import clients_router  app = FastAPI() app.config = load_config(get_config_path(\"local.yaml\")) app.include_router(clients_router)<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043a\u00a0\u043d\u0435\u043c\u0443 \u0432\u00a0\u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430 <code>config<\/code> \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0441\u00a0\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438. \u0414\u0435\u043b\u0430\u0435\u043c \u044d\u0442\u043e \u043e\u043f\u044f\u0442\u044c\u00a0\u0436\u0435 \u0434\u043b\u044f\u00a0\u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u043c\u0435\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u043a\u00a0\u043d\u0438\u043c \u0432\u043d\u0443\u0442\u0440\u0438 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u043e\u0432 \u0447\u0435\u0440\u0435\u0437 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440<code>Request<\/code>, \u0430\u00a0\u0438\u043c\u0435\u043d\u043d\u043e \u0432\u00a0\u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u0435 <code>route<\/code>.<\/p>\n<\/div>\n<\/details>\n<p>\u041a\u0430\u043a\u00a0\u044f \u0433\u043e\u0432\u043e\u0440\u0438\u043b \u0432\u044b\u0448\u0435, \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u044f \u0438\u0445 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443.<\/p>\n<p><code>routes.py<\/code>\u00a0\u2014 \u043a\u043e\u043d\u0435\u0447\u043d\u044b\u0435 \u0442\u043e\u0447\u043a\u0438 \u0432\u0445\u043e\u0434\u0430 \u0432\u00a0\u0441\u0435\u0440\u0432\u0438\u0441 (endpoints).<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from typing import List  from fastapi import APIRouter, Response from fastapi.params import Query from starlette import status from starlette.requests import Request  from gateway.common import router from gateway.srv_clients.schemas import ClientSchema, ClientViewSchema  clients_router = APIRouter(prefix=\"\/clients\")   @router(     method=clients_router.get,     path=\"\",     response_model=List[ClientViewSchema], ) async def get_filtered_clients(     request: Request,     response: Response,     surname: str = Query(None, description=\"\u0424\u0430\u043c\u0438\u043b\u0438\u044f\"),     name: str = Query(None, description=\"\u0418\u043c\u044f\"),     country: str = Query(None, description=\"\u0421\u0442\u0440\u0430\u043d\u0430\"),     limit: int = Query(         None, description=\"\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432\"     ),     offset: int = Query(         None,         description=\"\u0421\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0435\u0440\u0432\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438\",     ), ):     pass   @router(     method=clients_router.post,     path=\"\",     response_model=ClientViewSchema,     status_code=status.HTTP_201_CREATED,     data_key=\"data\", ) async def create_client(     request: Request, response: Response, data: ClientSchema ):     pass   @router(     method=clients_router.get,     path=\"\/{client_id}\",     response_model=ClientViewSchema, ) async def get_client(request: Request, response: Response, client_id: int):     pass   @router(     method=clients_router.patch,     path=\"\/{client_id}\",     response_model=ClientViewSchema,     status_code=status.HTTP_202_ACCEPTED,     data_key=\"data\", ) async def update_client(     request: Request, response: Response, client_id: int, data: ClientSchema ):     pass   @router(     method=clients_router.delete,     path=\"\/{client_id}\",     status_code=status.HTTP_202_ACCEPTED, ) async def delete_client(request: Request, response: Response, client_id: int):     pass<\/code><\/pre>\n<p>\u0412\u0441\u0435 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u044b \u043d\u0435\u00a0\u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442 \u043a\u043e\u0434. \u0417\u0430\u043f\u0440\u043e\u0441\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u0432\u043d\u0443\u0442\u0440\u0438 \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u0430, \u0432\u00a0\u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e.<\/p>\n<\/div>\n<\/details>\n<p><code>schemas.py<\/code>\u00a0\u2014 \u0421\u0445\u0435\u043c\u044b \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0438 \u0432\u044b\u0445\u043e\u0434\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from pydantic import BaseModel, Field   class ClientSchema(BaseModel):     surname: str = Field(..., description=\"\u0424\u0430\u043c\u0438\u043b\u0438\u044f\")     name: str = Field(..., description=\"\u0418\u043c\u044f\")     country: str = Field(..., description=\"\u0421\u0442\u0440\u0430\u043d\u0430\")   class ClientViewSchema(ClientSchema):     id: int = Field(..., description=\"\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\")<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043f\u043e\u043b\u043d\u0430\u044f \u043a\u043e\u043f\u0438\u044f \u0441\u0445\u0435\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0430.<\/p>\n<\/div>\n<\/details>\n<p>\u0413\u043e\u0442\u043e\u0432\u043e! \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u0448\u043b\u044e\u0437 \u043d\u0430 8000-\u043c \u043f\u043e\u0440\u0442\u0435:<\/p>\n<pre><code class=\"bash\">python -m gunicorn --bind 0.0.0.0:8000 --workers 4 -k uvicorn.workers.UvicornWorker main:app<\/code><\/pre>\n<h4>\u041f\u0440\u043e\u0431\u044b<\/h4>\n<p>\u0412\u0441\u044f \u0441\u0432\u044f\u0437\u043a\u0430 \u0433\u043e\u0442\u043e\u0432\u0430. \u041c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0432\u0441\u0435 \u0432\u043c\u0435\u0441\u0442\u0435 \u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u043d\u0430\u00a0\u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0438\u0435\u043d\u0442\u0430:<\/p>\n<pre><code>POST http:\/\/localhost:8000\/clients Content-Type: application\/json  {   \"surname\": \"John\",   \"name\": \"Malkovitch\",   \"country\": \"USA\" }<\/code><\/pre>\n<p>\u0418 \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u043e\u0442\u0432\u0435\u0442:<\/p>\n<pre><code>HTTP\/1.1 201 Created date: Mon, 06 Feb 2023 10:43:10 GMT server: uvicorn content-length: 61 content-type: application\/json  {   \"surname\": \"John\",   \"name\": \"Malkovitch\",   \"country\": \"USA\",   \"id\": 1 }<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0440\u043e\u0441\u0438\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432:<\/p>\n<pre><code>GET http:\/\/localhost:8000\/clients Content-Type: application\/json<\/code><\/pre>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0431\u0443\u0434\u0435\u0442 \u0442\u0430\u043a\u0438\u043c:<\/p>\n<pre><code>HTTP\/1.1 200 OK date: Mon, 06 Feb 2023 10:45:12 GMT server: uvicorn content-length: 63 content-type: application\/json  [   {     \"surname\": \"John\",     \"name\": \"Malkovitch\",     \"country\": \"USA\",     \"id\": 1   } ]<\/code><\/pre>\n<p>\u041e\u0436\u0438\u0434\u0430\u0435\u043c\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a json\u2011\u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432.<\/p>\n<p>\u0422\u0430\u043a\u00a0\u0436\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0438\u043c \u0437\u0430\u043f\u0438\u0441\u044c \u043f\u043e\u00a0\u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0443:<\/p>\n<pre><code>GET http:\/\/localhost:8000\/clients\/1 Content-Type: application\/json<\/code><\/pre>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0431\u0443\u0434\u0435\u0442 \u0442\u0430\u043a\u0438\u043c:<\/p>\n<pre><code>HTTP\/1.1 200 OK date: Mon, 06 Feb 2023 10:48:26 GMT server: uvicorn content-length: 61 content-type: application\/json  {   \"surname\": \"John\",   \"name\": \"Malkovitch\",   \"country\": \"USA\",   \"id\": 1 }<\/code><\/pre>\n<p>\u0418\u0437\u043c\u0435\u043d\u0438\u043c \u043d\u0430\u0448\u0435\u0433\u043e \u0414\u0436\u043e\u043d\u0430 \u041c\u0430\u043b\u043a\u043e\u0432\u0438\u0447\u0430 \u0438\u0437\u00a0\u0421\u0428\u0410 \u043d\u0430\u00a0\u0414\u0436\u043e\u043d\u0430 \u0421\u043c\u0438\u0442\u0430 \u0438\u0437\u00a0\u0411\u0440\u0438\u0442\u0430\u043d\u0438\u0438:<\/p>\n<pre><code>PATCH http:\/\/localhost:8000\/clients\/1 Content-Type: application\/json  {   \"surname\": \"John\",   \"name\": \"Smith\",   \"country\": \"Great Britain\" }<\/code><\/pre>\n<p>\u0412\u00a0\u043e\u0442\u0432\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c:<\/p>\n<pre><code>HTTP\/1.1 202 Accepted date: Mon, 06 Feb 2023 10:52:02 GMT server: uvicorn content-length: 66 content-type: application\/json  {   \"surname\": \"John\",   \"name\": \"Smith\",   \"country\": \"Great Britain\",   \"id\": 1 }<\/code><\/pre>\n<p>\u0410\u00a0\u0442\u0435\u043f\u0435\u0440\u044c \u0443\u0434\u0430\u043b\u0438\u043c \u0435\u0433\u043e:<\/p>\n<pre><code>DELETE http:\/\/localhost:8000\/clients\/1 Content-Type: application\/json<\/code><\/pre>\n<p>\u0412\u00a0\u043e\u0442\u0432\u0435\u0442\u0435 \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442\u00a0\u043b\u0438\u0448\u044c \u0441\u0442\u0430\u0442\u0443\u0441 \u0438 \u043e\u043d 202-\u0439:<\/p>\n<pre><code>HTTP\/1.1 202 Accepted date: Mon, 06 Feb 2023 10:52:37 GMT server: uvicorn content-length: 4 content-type: application\/json  null<\/code><\/pre>\n<hr\/>\n<p>\u0412\u043e\u0442 \u0438 \u0432\u0441\u0451. \u041f\u0440\u043e\u0434\u0435\u043b\u0430\u043d\u043e \u043c\u043d\u043e\u0433\u043e \u0440\u0430\u0431\u043e\u0442\u044b \u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043e \u043c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430. RPC \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0441\u0442\u0430\u0440\u0430\u044f, \u043d\u043e\u00a0\u0445\u043e\u0440\u043e\u0448\u043e \u0441\u0435\u0431\u044f \u0437\u0430\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u043e\u0432\u0430\u0432\u0448\u0430\u044f \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044f. \u0414\u0430\u043d\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441\u00a0\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438 \u043d\u0435\u00a0\u0443\u043b\u044c\u0442\u0438\u043c\u0430\u0442\u0438\u0432\u0435\u043d \u0438 \u043c\u043e\u0436\u0435\u0442\u00a0\u0431\u044b\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u0441\u00a0\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0438\u043d\u044b\u0445 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439. \u0421\u043f\u0430\u0441\u0438\u0431\u043e, \u0447\u0442\u043e\u00a0\u0434\u043e\u0447\u0438\u0442\u0430\u043b\u0438 \u0434\u043e\u00a0\u043a\u043e\u043d\u0446\u0430. \u0414\u043e\u00a0\u0441\u043a\u043e\u0440\u044b\u0445 \u0432\u0441\u0442\u0440\u0435\u0447!<\/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\/705752\/\"> https:\/\/habr.com\/ru\/articles\/705752\/<\/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>\u041e\u0434\u043d\u0430\u0436\u0434\u044b \u0442\u0438\u043c\u043b\u0438\u0434 \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0434\u043e \u043c\u043d\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0443 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0447\u0435\u0440\u0435\u0437 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0441\u00a0\u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438 \u0447\u0435\u0440\u0435\u0437 \u0435\u0434\u0438\u043d\u0443\u044e \u0442\u043e\u0447\u043a\u0443 \u0432\u0445\u043e\u0434\u0430 \u0441\u00a0\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c FastAPI \u0438\u00a0RabbitMQ. \u0421\u043f\u0435\u0448\u0443 \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441\u00a0\u0442\u043e\u0431\u043e\u0439, \u043c\u043e\u0439 \u0447\u0438\u0442\u0430\u0442\u0435\u043b\u044c, \u0442\u0435\u043c, \u0447\u0442\u043e \u0443\u00a0\u043c\u0435\u043d\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c. \u041f\u043e\u00a0\u043c\u0435\u0440\u0435 \u043f\u043e\u0432\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u0430\u043c \u043f\u043e\u044f\u0441\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u00a0\u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u043c\u0443 \u043a\u043e\u0434\u0443. \u0418, \u0434\u0430, \u0441\u0434\u0435\u043b\u0430\u044e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u043e\u0442\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u044f \u043f\u043e\u00a0\u0432\u043e\u043f\u0440\u043e\u0441\u0430\u043c \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0438 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f, \u0432\u00a0\u0442.\u0447. \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0445, \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<h3>\u0417\u0430\u0434\u0430\u0447\u0430<\/h3>\n<p>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0447\u0435\u0440\u0435\u0437 \u0435\u0434\u0438\u043d\u044b\u0439 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 (\u00abFront\u00bb side) \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u043a\u00a0\u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435, \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0439 \u0432\u00a0\u0437\u0430\u043a\u0440\u044b\u0442\u043e\u043c \u043a\u043e\u043d\u0442\u0443\u0440\u0435 (\u00abBack\u00bb side). \u0412\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u043e \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c \u043e\u0447\u0435\u0440\u0435\u0434\u0435\u0439 RabbitMQ. \u0412\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043e\u0434 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c Python, FastAPI \u0438\u00a0Gunicorn. \u0412\u00a0\u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u043e\u043c \u0432\u0438\u0434\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u0447\u0442\u043e\u2011\u0442\u043e \u0442\u0430\u043a\u043e\u0435:<\/p>\n<figure class=\"bordered full-width\"><figcaption>\u041f\u0440\u0438\u043d\u0446\u0438\u043f\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b<\/figcaption><\/figure>\n<h3>\u041f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h3>\n<p>API-\u0448\u043b\u044e\u0437 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u043e\u0442\u00a0\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u00a0\u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437\u00a0\u0447\u0430\u0441\u0442\u043d\u043e\u0441\u0442\u0435\u0439 \u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u0438\u043d\u0442\u0435\u0440\u043f\u0440\u0435\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c. \u0414\u0430\u043b\u0435\u0435 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0432\u00a0\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0432\u00a0\u043e\u0447\u0435\u0440\u0435\u0434\u044c RabbitMQ \u0438\u00a0\u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442\u044c \u0432\u00a0\u0440\u0435\u0436\u0438\u043c \u00ab\u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f\u00bb. \u0421\u00a0\u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b RabbitMQ \u0442\u0430\u043a\u00a0\u0436\u0435 \u0432\u00a0\u0440\u0435\u0436\u0438\u043c\u0435 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c <a href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%A3%D0%B4%D0%B0%D0%BB%D1%91%D0%BD%D0%BD%D1%8B%D0%B9_%D0%B2%D1%8B%D0%B7%D0%BE%D0%B2_%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D0%B4%D1%83%D1%80\" rel=\"noopener noreferrer nofollow\">RPC-\u0441\u0435\u0440\u0432\u0435\u0440<\/a>. \u041f\u0440\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043e\u043d\u00a0\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442, \u0432\u00a0\u043a\u0430\u043a\u043e\u0435 \u0438\u0437\u00a0\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0435\u0433\u043e \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u0438\u0442\u044c, \u0437\u0430\u043f\u0443\u043b\u0438\u0442 \u0435\u0433\u043e \u0432\u00a0\u0441\u0442\u043e\u0440\u043e\u043d\u0443 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e Gunicorn, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043c\u0435\u0436\u0434\u0443 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430\u043c\u0438 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0438\u00a0\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442 \u0432\u0435\u0440\u043d\u0435\u0442 RPC-\u0441\u0435\u0440\u0432\u0435\u0440\u0443, \u0430\u00a0\u0442\u043e\u0442 \u0432\u00a0\u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043f\u0440\u043e\u043a\u0438\u043d\u0435\u0442 \u0435\u0433\u043e \u0434\u0430\u043b\u044c\u0448\u0435 \u0432\u00a0\u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c. \u0421\u00a0\u0442\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b \u00ab\u0436\u0434\u0443\u0449\u0438\u0439\u00bb \u0448\u043b\u044e\u0437 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u044d\u0442\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435, \u0432\u044b\u0439\u0434\u0435\u0442 \u0438\u0437\u00a0\u0440\u0435\u0436\u0438\u043c\u0430 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f \u0438\u00a0\u0441\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442 \u0432\u0435\u0440\u043d\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e.<\/p>\n<h3>\u0418\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435<\/h3>\n<p>\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u043c \u0432\u0441\u0435 \u043c\u043e\u0434\u0443\u043b\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c\u043e\u0439 \u0441\u0445\u0435\u043c\u044b \u0432\u00a0\u043e\u0434\u043d\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435, \u043f\u043e\u0434\u0440\u0430\u0437\u0443\u043c\u0435\u0432\u0430\u044f \u043f\u0440\u0438 \u044d\u0442\u043e\u043c, \u0447\u0442\u043e \u0443\u00a0\u043a\u0430\u0436\u0434\u043e\u0439 \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0435\u0439 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u0432\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435, \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u043e, \u0441\u043f\u043e\u0441\u043e\u0431\u044b \u0438\u00a0\u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0438\u00a0\u0442.\u0434. \u0421\u0435\u0440\u0432\u0438\u0441\u044b \u0431\u0443\u0434\u0443\u0442 <s>\u043f\u0440\u043e\u0441\u0442\u044b\u0435<\/s> \u043f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0438\u0435, \u043d\u043e\u00a0\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u044b\u0435 \u0434\u043b\u044f \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438\u00a0\u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f \u043e\u0431\u0449\u0435\u0439 \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u0438. \u041f\u043e\u043b\u043d\u044b\u0439 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u0432\u00a0\u043c\u043e\u0435\u043c <a href=\"https:\/\/github.com\/VovkaST\/api-gateway\" rel=\"noopener noreferrer nofollow\">\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438<\/a>.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u0438 \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u0432 \u043d\u0435\u0433\u043e:<\/p>\n<pre><code class=\"bash\">mkdir api-gateway &amp;&amp; cd api-gateway<\/code><\/pre>\n<p>\u0421\u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u0444\u0430\u0439\u043b \u0441\u00a0\u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u043c\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u043c\u0438 \u0438 \u043d\u0430\u0437\u043e\u0432\u0435\u043c \u0435\u0433\u043e \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u043e\u043d\u043d\u043e requirements.txt, \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u0434\u0442\u044f\u043d\u0443\u0442\u0441\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438: <\/p>\n<pre><code class=\"bash\">echo \" aiohttp==3.8.3 aio-pika==8.2.5 fastapi==0.88.0 gunicorn==20.1.0 path==16.5.0 PyYAML==6.0 pyyaml-tags==1.0.4 six==1.16.0 SQLAlchemy==1.4.45 uvicorn==0.20.0 \" > requirements.txt<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0432\u00a0\u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0435.venv3.10\u00a0\u0438 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0435\u043c \u0435\u0433\u043e. \u041a\u0441\u0442\u0430\u0442\u0438, \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e Python 3.10.6\u00a0\u043f\u043e\u0434\u00a0Ubuntu 20.04:<\/p>\n<pre><code class=\"bash\">python3 -m venv .venv3.10 &amp;&amp; source .venv3.10\/bin\/activate<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u0438 \u0434\u0430\u043b\u0435\u0435 \u0432\u0441\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u044b \u0431\u0443\u0434\u0435\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043f\u043e\u0434\u00a0\u044d\u0442\u043e\u0439 \u0441\u0440\u0435\u0434\u043e\u0439.<\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c RabbitMQ. \u042f \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0438\u0442\u0430\u044e \u0440\u0430\u0437\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0432\u00a0Docker\u2011\u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435. \u0425\u043e\u0440\u043e\u0448\u0430\u044f \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0430\u044f \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f \u043f\u043e\u00a0\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0435 <a href=\"https:\/\/hub.docker.com\/_\/rabbitmq\" rel=\"noopener noreferrer nofollow\">\u0437\u0434\u0435\u0441\u044c<\/a>.<\/p>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441&#8230; \u041a\u043e\u043d\u0446\u0430.:\u2011)<\/p>\n<h4>1. \u0421\u0435\u0440\u0432\u0438\u0441<\/h4>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0438 \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u0432\u00a0\u043d\u0435\u0433\u043e:<\/p>\n<pre><code class=\"bash\">mkdir srv_clients &amp;&amp; cd srv_clients<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0444\u0430\u0439\u043b\u043e\u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0442\u0430\u043a\u00a0\u0436\u0435 \u043e\u0434\u043d\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439:<\/p>\n<pre><code class=\"bash\">touch __init__.py main.py schemas.py database.py models.py querysets.py<\/code><\/pre>\n<p><code>__init__.py<\/code> \u2014 <a href=\"https:\/\/docs-python.ru\/tutorial\/sistema-importa-python\/fajl-paketa-init-py\/\" rel=\"noopener noreferrer nofollow\">\u0441\u043b\u0443\u0436\u0435\u0431\u043d\u044b\u0439<\/a> \u0444\u0430\u0439\u043b \u043f\u0430\u043a\u0435\u0442\u0430 python \u0431\u0443\u0434\u0435\u0442 \u043f\u0443\u0441\u0442<\/p>\n<p><code>database.py<\/code>\u00a0\u2014 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a\u00a0\u0411\u0414<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine from sqlalchemy.orm import declarative_base, sessionmaker  engine = create_async_engine(\"sqlite+aiosqlite:\/\/\/clients.sqlite\") sm = sessionmaker(     engine, autocommit=False, autoflush=False, class_=AsyncSession )  Base = declarative_base()<\/code><\/pre>\n<p>\u0414\u043b\u044f\u00a0\u0440\u0430\u0431\u043e\u0442\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 \u0441\u0435\u0441\u0441\u0438\u0438 \u0438 SQLite \u0432\u00a0\u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0421\u0423\u0411\u0414.<\/p>\n<\/div>\n<\/details>\n<p><code>models.py<\/code> \u2014 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u043c\u043e\u0434\u0435\u043b\u0438 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440 \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from sqlalchemy import Column, Integer, Text from sqlalchemy.orm.collections import InstrumentedList  from srv_clients.database import Base   class BaseModel(Base):     __abstract__ = True     id = Column(Integer, primary_key=True, comment=\"\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\")      def to_dict(self) -> dict:         data = dict()         for k, v in self.__dict__.items():             if k.startswith(\"_\"):                 continue             elif isinstance(v, Base):                 v = v.to_dict()             elif isinstance(v, InstrumentedList):                 v = [item.to_dict() for item in v]             data[k] = v         return data<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u0431\u0430\u0437\u043e\u0432\u0443\u044e \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0442\u043d\u0443\u044e \u043c\u043e\u0434\u0435\u043b\u044c. \u0412\u00a0\u043d\u0435\u0439 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u043e \u043f\u043e\u043b\u0435 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430, \u044f\u0432\u043b\u044f\u044e\u0449\u0435\u0435\u0441\u044f \u0442\u0430\u043a\u0436\u0435 \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u043c \u043a\u043b\u044e\u0447\u043e\u043c. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0438\u0439 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 <code>to_dict<\/code> \u0434\u043b\u044f\u00a0\u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 \u043a\u043b\u0430\u0441\u0441\u0430 \u0432\u00a0\u0441\u043b\u043e\u0432\u0430\u0440\u044c. <em>\u0414\u0430, \u043e\u043d \u043d\u0435\u00a0\u043e<\/em>\u0445\u0432<em>\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0438 \u0438 \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043a\u0443\u0447\u0443 \u043e\u0448\u0438\u0431\u043e\u043a \u0438 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0431\u043e\u043b\u0435\u0435 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e\u0439 \u043f\u0440\u043e\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u043d\u043e\u00a0\u0434\u043b\u044f\u00a0\u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0435\u0433\u043e \u0431\u043e\u043b\u0435\u0435 \u0447\u0435\u043c \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e.<\/em><\/p>\n<pre><code class=\"python\">class Client(BaseModel):     __tablename__ = \"client\"     __table_args = {\"comment\": \"\u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432\"}      surname = Column(Text(length=255), nullable=False, comment=\"\u0424\u0430\u043c\u0438\u043b\u0438\u044f\")     name = Column(Text(length=255), nullable=False, comment=\"\u0418\u043c\u044f\")     country = Column(Text(length=255), comment=\"\u0421\u0442\u0440\u0430\u043d\u0430\")<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0443\u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u043e\u0442 <code>BaseModel<\/code> \u043c\u043e\u0434\u0435\u043b\u044c \u041a\u043b\u0438\u0435\u043d\u0442\u0430, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u00a0\u043d\u0435\u0439 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0439 \u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0445 \u043f\u043e\u043b\u0435\u0439 \u0434\u043b\u0438\u043d\u043e\u0439 \u0434\u043e 255\u00a0\u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432 \u0442\u0430\u043a\u00a0\u0436\u0435 \u0441\u00a0\u043f\u043e\u044f\u0441\u043d\u0435\u043d\u0438\u044f\u043c\u0438. <\/p>\n<\/div>\n<\/details>\n<p><code>querysets.py<\/code> \u2014 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043d\u0430\u0431\u043e\u0440\u044b \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0434\u043b\u044f\u00a0\u0440\u0430\u0431\u043e\u0442\u044b \u0441\u00a0\u0434\u0430\u043d\u043d\u044b\u043c\u0438<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from typing import List  from sqlalchemy import delete, select, update from sqlalchemy.ext.asyncio import AsyncSession  from srv_clients.models import Client   class ClientQueryset:     model = Client      @classmethod     async def create(cls, session: AsyncSession, **kwargs) -> int:         created = cls.model(**kwargs)         session.add(created)         await session.flush([created])         return created.id<\/code><\/pre>\n<p>\u041c\u0435\u0442\u043e\u0434 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438. \u0422\u0430\u043a \u043a\u0430\u043a\u00a0\u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u0438\u043d\u043a\u0440\u0435\u043c\u0435\u043d\u0442\u043e\u043c \u0438 \u0432\u00a0\u043f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0438\u0445 \u0443\u0441\u043b\u043e\u0432\u0438\u044f\u0445 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438, \u043f\u0440\u0438\u043d\u0443\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u044b\u0437\u043e\u0432\u0435\u043c \u043c\u0435\u0442\u043e\u0434 \u0441\u0435\u0441\u0441\u0438\u0438 <code>flush<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0431\u0440\u043e\u0441\u0438\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432\u00a0\u0411\u0414 \u0438 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0443 <code>id<\/code> \u043f\u0440\u0438\u0441\u0432\u043e\u0438\u0442\u0441\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435.<\/p>\n<pre><code class=\"python\">    @classmethod     async def get_by_id(cls, session: AsyncSession, id_: int) -> Client:         return await session.scalar(             select(cls.model).where(cls.model.id == id_)         )      @classmethod     async def get_multiple(         cls,         session: AsyncSession,         limit: int = None,         offset: int = None,         **filters,     ) -> List[Client]:         where = list()         if surname := filters.get(\"surname\"):             where.append(cls.model.surname == surname)         if name := filters.get(\"name\"):             where.append(cls.model.name == name)         if country := filters.get(\"country\"):             where.append(cls.model.country == country)         return await session.scalars(             select(cls.model).where(*where).limit(limit).offset(offset)         )<\/code><\/pre>\n<p>\u0414\u043b\u044f\u00a0\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043f\u043e\u00a0\u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0443 \u0438 \u043e\u0442\u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u0441\u043f\u0438\u0441\u043a\u0430 \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043c\u0435\u0442\u043e\u0434\u044b \u0441\u0435\u0441\u0441\u0438\u0438 <code>scalar<\/code> \u0438 <code>scalars<\/code> \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e. \u0423\u0441\u043b\u043e\u0432\u0438\u044f \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u0438 \u043e\u0436\u0438\u0434\u0430\u0435\u043c \u0432\u00a0\u0432\u0438\u0434\u0435 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u0435\u0439 \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u0438 \u0441\u00a0\u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 <code>limit<\/code> \u0438 <code>offset<\/code>.<\/p>\n<pre><code class=\"python\">    @classmethod     async def update(cls, session: AsyncSession, id_: int, **kwargs) -> None:         await session.execute(             update(cls.model).where(cls.model.id == id_).values(**kwargs)         )      @classmethod     async def delete(cls, session: AsyncSession, id_: int) -> None:         await session.execute(delete(cls.model).where(cls.model.id == id_)) <\/code><\/pre>\n<p>\u0412\u0441\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u043e\u0431\u0435\u0440\u043d\u0443\u0442\u044b \u0432\u00a0\u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 <code><a class=\"mention\" href=\"\/users\/classmethod\">@classmethod<\/a><\/code> \u0434\u043b\u044f\u00a0\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0432\u044b\u0437\u043e\u0432\u0430 \u0431\u0435\u0437\u00a0\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 \u043a\u043b\u0430\u0441\u0441\u0430.<\/p>\n<\/div>\n<\/details>\n<p><code>schemas.py<\/code> \u2014 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0432\u0445\u043e\u0434\u043d\u044b\u0435\/\u0432\u044b\u0445\u043e\u0434\u043d\u044b\u0435 \u0441\u0445\u0435\u043c\u044b \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from pydantic import BaseModel, Field   class ClientSchema(BaseModel):     surname: str = Field(..., description=\"\u0424\u0430\u043c\u0438\u043b\u0438\u044f\")     name: str = Field(..., description=\"\u0418\u043c\u044f\")     country: str = Field(..., description=\"\u0421\u0442\u0440\u0430\u043d\u0430\")   class ClientViewSchema(ClientSchema):     id: int = Field(..., description=\"\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\") <\/code><\/pre>\n<p>\u0412\u0441\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 (\u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u00a0\u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043f\u0435\u0440\u0435\u0434\u0430\u0434\u0438\u043c \u0442\u0438\u043f <code>ellipsis<\/code>\u00a0\u2014 <code>...<\/code>). \u041a\u00a0\u043c\u043e\u0434\u0435\u043b\u0438 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0430\u0442\u0440\u0438\u0431\u0443\u0442 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430.<\/p>\n<\/div>\n<\/details>\n<p><code>main.py<\/code> \u2014 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u0435\u043c\u044b\u0439 \u043a\u043e\u0434 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from typing import List  from fastapi import FastAPI, HTTPException, Request from fastapi.params import Query from starlette import status  from srv_clients.database import Base, engine from srv_clients.database import sm as session_maker from srv_clients.querysets import ClientQueryset from srv_clients.schemas import ClientSchema, ClientViewSchema  app = FastAPI() app.state.engine = engine app.state.session_maker = session_maker<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043a\u00a0\u043d\u0435\u043c\u0443 \u0432\u00a0\u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a\u00a0\u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440 \u0441\u0435\u0441\u0441\u0438\u0439. \u0414\u0435\u043b\u0430\u0435\u043c \u044d\u0442\u043e \u0434\u043b\u044f\u00a0\u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u043c\u0435\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u043a\u00a0\u043d\u0438\u043c \u0432\u043d\u0443\u0442\u0440\u0438 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u043e\u0432 \u0447\u0435\u0440\u0435\u0437 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440<code>Request<\/code>.<\/p>\n<pre><code class=\"python\">@app.on_event(\"startup\") async def on_startup():     async with app.state.engine.begin() as conn:         await conn.run_sync(Base.metadata.create_all)   @app.on_event(\"shutdown\") async def on_shutdown():     # async with app.state.engine.begin() as conn:     #     await conn.run_sync(Base.metadata.drop_all)     await app.state.engine.dispose()<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0434\u0432\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f:<\/p>\n<ul>\n<li>\n<p><code>startup<\/code>\u2014 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u043c \u043c\u043e\u0434\u0435\u043b\u0438 SQLAlchemy \u0432\u00a0\u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0411\u0414;<\/p>\n<\/li>\n<li>\n<p><code>shutdown<\/code>\u2014 \u043e\u0447\u0438\u0449\u0430\u0435\u043c \u0411\u0414 \u043f\u0443\u0442\u0435\u043c \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0432\u0441\u0435\u0445 \u0442\u0430\u0431\u043b\u0438\u0446 (\u0437\u0430\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043e) \u0438 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0435\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441\u00a0\u0411\u0414.<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"python\">@app.get(\"\/clients\", response_model=List[ClientViewSchema]) async def get_filtered_clients(     request: Request,     surname: str = Query(None, description=\"\u0424\u0430\u043c\u0438\u043b\u0438\u044f\"),     name: str = Query(None, description=\"\u0418\u043c\u044f\"),     country: str = Query(None, description=\"\u0421\u0442\u0440\u0430\u043d\u0430\"),     limit: int = Query(         None, description=\"\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432\"     ),     offset: int = Query(         None,         description=\"\u0421\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0435\u0440\u0432\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438\",     ), ):     async with request.app.state.session_maker() as session:         rows = await ClientQueryset.get_multiple(             session,             surname=surname,             name=name,             country=country,             limit=limit,             offset=offset,         )         return [row.to_dict() for row in rows.all()]   @app.post(     \"\/clients\",<\/code><\/pre>\n<\/div>\n<\/details>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-383674","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/383674","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=383674"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/383674\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=383674"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=383674"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=383674"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}