{"id":391197,"date":"2024-06-29T09:41:34","date_gmt":"2024-06-29T09:41:34","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=391197"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=391197","title":{"rendered":"<span>\u041f\u0438\u0448\u0435\u043c \u0441\u0432\u043e\u0439 Google, \u0438\u043b\u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043a\u0440\u0430\u0443\u043b\u0435\u0440 \u0441 rate limits \u043d\u0430 Python<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/8db\/1f0\/ca0\/8db1f0ca0e9a337d30aee3800d3b92fc.png\" width=\"3120\" height=\"1760\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/8db\/1f0\/ca0\/8db1f0ca0e9a337d30aee3800d3b92fc.png\"\/><\/figure>\n<p>\u041f\u0440\u0438\u0432\u0435\u0442!<\/p>\n<p>\u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440, \u044f \u0440\u0443\u043a\u043e\u0432\u043e\u0436\u0443 backend-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u0432 <a href=\"https:\/\/habr.com\/ru\/company\/kts\/profile\/\"><u>\u041a\u0422S<\/u><\/a>. \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043a\u0440\u0430\u0443\u043b\u0435\u0440.<\/p>\n<p>\u0422\u0430\u043a\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u0447\u0430\u0441\u0442\u043e \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435, \u043a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044e\/\u043e\u0431\u043a\u0430\u0447\u043a\u0443 \u043c\u0435\u0436\u0434\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438.\u00a0<\/p>\n<p>\u0421\u0442\u0430\u0442\u044c\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0430 \u043f\u043e \u043c\u043e\u0442\u0438\u0432\u0430\u043c \u0432\u0435\u0431\u0438\u043d\u0430\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u043b\u0438 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043a\u0443\u0440\u0441\u0430 <a href=\"https:\/\/vk.cc\/cmUy1v\"><u>\u00ab\u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430 Python \u0434\u043b\u044f \u0434\u0436\u0443\u043d\u0438\u043e\u0440-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432\u00bb.<\/u><\/a>  \u0415\u0441\u043b\u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e, \u0437\u0430\u0433\u043b\u044f\u043d\u0438\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c.<\/p>\n<p>\u0427\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0432 \u0441\u0442\u0430\u0442\u044c\u0435:<\/p>\n<ul>\n<li>\n<p><a href=\"#1\">\u0426\u0435\u043b\u044c<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#2\">\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#3\">\u041f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#4\">\u0417\u0430\u0434\u0430\u0447\u0430 \u0434\u043b\u044f \u043a\u0440\u0430\u0443\u043b\u0435\u0440\u0430<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#5\">\u041f\u0440\u043e\u0431\u043d\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#6\">\u041f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u0439 \u0438\u0442\u043e\u0433<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#7\">\u0424\u0443\u043d\u043a\u0446\u0438\u0438 put \u0438 join<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#8\">Semaphore<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#9\">\u041e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0444\u043e\u043d\u043e\u0432\u043e\u0433\u043e \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u0430<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#10\">\u0420\u0430\u0431\u043e\u0442\u0430 \u043a\u0440\u0430\u0443\u043b\u0435\u0440\u0430 \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043e\u0431\u043a\u0430\u0447\u043a\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u0431\u043b\u043e\u0433\u0430 \u043d\u0430 \u0425\u0430\u0431\u0440\u0435<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#11\">\u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043b\u043e\u0432 \u043e \u043a\u0443\u0440\u0441\u0435 \u043f\u043e \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u043c\u0443 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u043d\u0430 Python <\/a><\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"1\" id=\"1\"><\/a><\/p>\n<h2>\u0426\u0435\u043b\u044c<\/h2>\n<p>\u0423 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u043a\u0440\u0430\u0443\u043b\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u043a\u0430\u0447\u0438\u0432\u0430\u0435\u0442 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b. \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0439 \u0431\u043e\u0442 Google, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0445\u043e\u0434\u0438\u0442 \u043f\u043e \u0441\u0430\u0439\u0442\u0430\u043c, \u0441\u043a\u0430\u0447\u0438\u0432\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435, \u043a\u043b\u0430\u0434\u0435\u0442 \u0432 \u0431\u0430\u0437\u0443 \u0438 \u0438\u043d\u0434\u0435\u043a\u0441\u0438\u0440\u0443\u0435\u0442, \u0438\u043b\u0438 \u043a\u0430\u043a\u043e\u0439-\u043d\u0438\u0431\u0443\u0434\u044c \u0430\u0433\u0440\u0435\u0433\u0430\u0442\u043e\u0440: \u0430\u043f\u0442\u0435\u043a, \u043c\u0430\u0440\u043a\u0435\u0442\u043f\u043b\u0435\u0439\u0441\u043e\u0432 \u0438 \u0442.\u0434.<\/p>\n<p>\u0417\u0430\u0434\u0430\u0447\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043a\u0440\u0430\u0443\u043b\u0435\u0440 \u0434\u043e\u043b\u0436\u0435\u043d \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0438 \u043d\u0435 \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u043d \u043e\u0431\u043a\u0430\u0447\u0438\u0432\u0430\u0435\u0442.\u00a0<\/p>\n<p>\u041a\u043e\u0434 \u0434\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0430\u0431\u043e\u0442\u044b:<\/p>\n<pre><code class=\"python\">import asyncio from dataclasses import dataclass from typing import Optional  class Pool:     def __init__(self, max_rate: int, interval: int = 1, concurrent_level: Optional[int] = None):         self.max_rate = max_rate         self.interval = interval         self.concurrent_level = concurrent_level          async def start(pool):   await asyncio.sleep(5)      def main():     loop = asyncio.get_event_loop()          try:         loop.run_until_complete(start())     except KeyboardInterrupt:         loop.close()  if __name__ == '__main__':     main()<\/code><\/pre>\n<p>\u041a\u0440\u0430\u0443\u043b\u0435\u0440\u0443 \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0441\u0435\u0442\u0438\u0442\u044c \u0438 \u0441\u043a\u0430\u0447\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446, \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u043c\u043d\u043e\u0433\u043e \u0440\u0430\u0437 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c\u0441\u044f \u043a \u0440\u0435\u0441\u0443\u0440\u0441\u0443. \u041c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442\u044c \u0441\u0435\u0431\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u043d\u043e \u0441\u0435\u0440\u0432\u0438\u0441, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u043c, \u043c\u043e\u0436\u0435\u0442 \u043d\u0435 \u0432\u044b\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0438. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043a \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0443\u0436\u043d\u043e \u0445\u043e\u0434\u0438\u0442\u044c \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c\u043e \u2014 \u0441\u0434\u0435\u043b\u0430\u0442\u044c <em>rate-limit.\u00a0<\/em><\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432 \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u043c\u043e\u043c\u0435\u043d\u0442 \u0437\u0430\u0434\u0430\u0447\u0430 \u043f\u0440\u0435\u0440\u0432\u0430\u043b\u0430\u0441\u044c, \u0438\u043b\u0438 \u043c\u044b \u0441\u0430\u043c\u0438 \u0440\u0435\u0448\u0438\u043b\u0438 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043a\u0440\u0430\u0443\u043b\u0435\u0440, \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u0443\u044e \u0438 \u0430\u043a\u043a\u0443\u0440\u0430\u0442\u043d\u0443\u044e \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443 \u0440\u0430\u0431\u043e\u0442\u044b. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u0447\u0430\u0442\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c\u0441\u044f, \u0430 \u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0438\u0437 \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u0440\u0435\u043a\u0440\u0430\u0442\u0438\u0442\u044c \u043f\u043e\u0441\u0442\u0443\u043f\u0430\u0442\u044c.\u00a0<\/p>\n<h2>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434<\/h2>\n<p>\u0423 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c <em>Pool.<\/em> \u042d\u0442\u0430 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c \u0443\u043c\u0435\u0435\u0442 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u0435\u0434\u0438\u043d\u0438\u0446\u0443 \u0432\u0440\u0435\u043c\u0435\u043d\u0438. Pool \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442:<\/p>\n<ul>\n<li>\n<p><em>max_rate<\/em> \u2014 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/p>\n<\/li>\n<li>\n<p><em>interval<\/em> \u2014 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b. \u0415\u0441\u043b\u0438 \u043c\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f max_rate = 5 \u0438 interval = 1, \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443 \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f 5 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/p>\n<\/li>\n<li>\n<p><em>concurrent_level<\/em> \u2014 \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/p>\n<\/li>\n<\/ul>\n<p>max_rate \u0438 concurrent_level \u043c\u043e\u0433\u0443\u0442 \u043d\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u0432\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0431\u043e\u043b\u044c\u0448\u0435, \u0447\u0435\u043c interval. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043c\u044b \u0434\u0435\u043b\u0430\u0435\u043c 5 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443, \u043a\u0430\u043a \u0437\u0430\u044f\u0432\u043b\u0435\u043d\u043e \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445, \u043d\u043e API \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u0435\u0435. \u0427\u0442\u043e\u0431\u044b \u043d\u0435 \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441, \u043c\u044b \u0432\u0432\u043e\u0434\u0438\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e concurrent_level. <\/p>\n<p><a class=\"anchor\" name=\"3\" id=\"3\"><\/a><\/p>\n<h2>\u041f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a<\/h2>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0434\u0435\u043b\u0430\u0442\u044c \u0440\u043e\u0432\u043d\u043e 5 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443, \u043d\u0435 \u043e\u0431\u0440\u0430\u0449\u0430\u044f \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0432\u0440\u0435\u043c\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0430\u0437\u043e\u0432\u0435\u043c <em>scheduler.<\/em> \u041e\u043d \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0441\u044b\u043f\u0430\u0442\u044c\u0441\u044f \u0440\u0430\u0437 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443 \u0438 \u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u0434\u0430\u0447, \u0440\u0430\u0432\u043d\u043e\u0435 max_rate. \u041f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a \u043d\u0435 \u0436\u0434\u0435\u0442 \u0438\u0445 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f, \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u0435\u0442 5 \u0437\u0430\u0434\u0430\u0447 \u043a\u0430\u0436\u0434\u0443\u044e \u0441\u0435\u043a\u0443\u043d\u0434\u0443.\u00a0<\/p>\n<p>\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u043c class Pool \u0438 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e scheduler:<\/p>\n<pre><code class=\"python\">from task import Task  class Pool:     def __init__(self, max_rate: int, interval: int = 1, concurrent_level: Optional[int] = None):         self.max_rate = max_rate         self.interval = interval         self.concurrent_level = concurrent_level         self.is_running = False               async def _scheduler(self):         while self.is_running:             for _ in range(self.max_rate):                 pass<\/code><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0434\u0432\u0435 \u0432\u0435\u0449\u0438:<\/p>\n<ul>\n<li>\n<p>\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0431\u0435\u0441\u043a\u043e\u043d\u0435\u0447\u043d\u0430\u044f, \u043f\u043e\u043a\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430\u0448 \u043a\u0440\u0430\u0443\u043b\u0435\u0440<\/p>\n<\/li>\n<li>\n<p>\u0440\u0430\u0437 \u0432 \u043f\u0435\u0440\u0438\u043e\u0434 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 max_rate \u0440\u0430\u0437 \u043a\u0430\u043a\u043e\u0435-\u0442\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 <\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"4\" id=\"4\"><\/a><\/p>\n<h2>\u0417\u0430\u0434\u0430\u0447\u0430 \u0434\u043b\u044f \u043a\u0440\u0430\u0443\u043b\u0435\u0440\u0430<\/h2>\n<p>Scheduler \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0442\u043a\u0443\u0434\u0430-\u0442\u043e \u0432\u0437\u044f\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0432\u043e\u0437\u044c\u043c\u0435\u043c \u0438\u0437 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 asyncio. \u041f\u0440\u0438\u043c\u0438\u0442\u0438\u0432 \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f asyncio.Queue(). \u0412 class Pool \u0434\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c:<\/p>\n<pre><code class=\"python\">class Pool:     def __init__(self, max_rate: int, interval: int = 1, concurrent_level: Optional[int] = None):         self.max_rate = max_rate         self.interval = interval         self.concurrent_level = concurrent_level         self.is_running = False         self._queue = asyncio.Queue()<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043f\u0440\u043e\u0441\u044b\u043f\u0430\u0435\u043c\u0441\u044f \u0440\u0430\u0437 \u0432 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u0434\u0430\u0447, \u0440\u0430\u0432\u043d\u043e\u0435 max_rate. \u041d\u043e \u043d\u0443\u0436\u043d\u043e \u0447\u0442\u043e-\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0438 \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u043b\u0438\u0441\u044c.<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432 asyncio \u0435\u0441\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044f <em>create_task. <\/em>\u041e\u043d\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a\u043e\u0440\u0443\u0442\u0438\u043d\u044b, \u043d\u043e \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043d\u0435 \u0434\u043e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f \u0435\u0435 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f, \u0430 \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u0444\u043e\u043d\u043e\u0432\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443. \u0412 create_task \u043f\u0435\u0440\u0435\u0434\u0430\u0434\u0438\u043c \u043c\u0435\u0442\u043e\u0434 <em>perform.<\/em><\/p>\n<pre><code class=\"python\">    async def _scheduler(self):         while self.is_running:             for _ in range(self.max_rate):                 task = await self._queue.get()               asyncio.create_task(task.perform))             await asyncio.sleep(self.interval)<\/code><\/pre>\n<p><a class=\"anchor\" name=\"5\" id=\"5\"><\/a><\/p>\n<h2>\u041f\u0440\u043e\u0431\u043d\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a<\/h2>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0432\u0441\u0435 \u044d\u0442\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c. \u0421\u0434\u0435\u043b\u0430\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <em>start<\/em> \u0438 \u0442\u0430\u043a\u0438\u043c \u0436\u0435 \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c scheduler. \u041d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043d\u0435 \u0436\u0434\u0430\u0442\u044c \u0435\u0433\u043e, \u0430 \u043f\u0440\u043e\u0441\u0442\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0432 \u0444\u043e\u043d\u0435 \u043a\u043e\u0440\u0443\u0442\u0438\u043d\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e create_task:<\/p>\n<pre><code class=\"python\">    async def _scheduler(self):         while self.is_running:             for _ in range(self.max_rate):                 task = await self._queue.get()                 asyncio.create_task(self._worker(task))             await asyncio.sleep(self.interval)          def start(self):         self.is_running = True         asyncio.create_task(self._scheduler())<\/code><\/pre>\n<p>\u0412 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u0434\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0433\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u043a\u0440\u0430\u0443\u043b\u0435\u0440\u0430 \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 scheduler. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0437\u0432\u0430\u0442\u044c cancel \u0443 \u0437\u0430\u0434\u0430\u0447\u0438, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 create_task \u043c\u044b \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e <em>scheduler_task:<\/em><\/p>\n<pre><code class=\"python\">class Pool:     def __init__(self, max_rate: int, interval: int = 1, concurrent_level: Optional[int] = None):         self.max_rate = max_rate         self.interval = interval         self.concurrent_level = concurrent_level         self.is_running = False         self._queue = asyncio.Queue()         self._scheduler_task: Optional[asyncio.Task] = None<\/code><\/pre>\n<p>\u0412\u044b\u0441\u0442\u0430\u0432\u0438\u043c rate-limit \u043d\u0430 3 \u0438 \u0432\u043d\u0443\u0442\u0440\u0438 start \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u043d\u0430\u0448 Pool:<\/p>\n<pre><code class=\"python\">def start(self):     self.is_running = True     self._scheduler_task = asyncio.create_task(self._scheduler())          async def start(pool):     pool = Pool(3)     pool.start()     await asyncio.sleep(5)  <\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0438 \u0432\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u043e:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/af3\/325\/23c\/af332523c7a1e1656a511b7272a33a40.png\" width=\"1756\" height=\"431\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/af3\/325\/23c\/af332523c7a1e1656a511b7272a33a40.png\"\/><\/figure>\n<p>\u042d\u0442\u043e \u043f\u043e\u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435\u0442. \u041c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0441\u0442\u0430\u0440\u0442 \u0438 \u043f\u043e\u0441\u043f\u0430\u043b\u0438 5 \u0441\u0435\u043a\u0443\u043d\u0434, \u0430 \u043d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0438 \u0443 \u043d\u0430\u0441 \u043e\u0441\u0442\u0430\u043b\u0430\u0441\u044c \u0444\u043e\u043d\u043e\u0432\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 scheduler.\u00a0\u00a0<\/p>\n<p><a class=\"anchor\" name=\"6\" id=\"6\"><\/a><\/p>\n<h2>\u041f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u0439 \u0438\u0442\u043e\u0433<\/h2>\n<ol>\n<li>\n<p>\u0423 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c Pool \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438:<br \/>\u2014 <em>\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 max_rate<br \/>\u2014 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u0430\u043a\u0442\u0438\u0432\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u0430 interval<br \/>\u2014 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 concurrent_level<\/em><\/p>\n<\/li>\n<li>\n<p>\u041c\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a scheduler, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e, \u043f\u0440\u043e\u0441\u044b\u043f\u0430\u0435\u0442\u0441\u044f \u0440\u0430\u0437 \u0432 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b, \u0434\u043e\u0441\u0442\u0430\u0435\u0442 \u0438\u0437 \u043e\u0447\u0435\u0440\u0435\u0434\u0438 max_rate \u0437\u0430\u0434\u0430\u0447 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0438\u0445 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435.<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0434\u0430\u0447\u0430 task \u2014 \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u0430\u0442\u0430-\u043a\u043b\u0430\u0441\u0441 \u0441 \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439 perform. \u0414\u043b\u044f \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0438 \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043a\u043b\u0430\u0441\u0441-\u043d\u0430\u0441\u043b\u0435\u0434\u043d\u0438\u043a \u0438 \u0432 \u043d\u0435\u043c \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c perform.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0449\u0435 \u043c\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044e start, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0432\u044b\u0441\u0442\u0430\u0432\u0438\u043b\u0438 \u043f\u0440\u0438\u0437\u043d\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u044b is_running \u0438 \u0432 \u0444\u043e\u043d\u0435 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043b\u0438 \u043d\u0430\u0448 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a.<\/p>\n<\/li>\n<\/ol>\n<p><a class=\"anchor\" name=\"7\" id=\"7\"><\/a><\/p>\n<h2>\u0424\u0443\u043d\u043a\u0446\u0438\u0438 put \u0438 join<\/h2>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c, \u043a\u0430\u043a \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c <em>Pool,<\/em> \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c \u0442\u0443\u0434\u0430 \u0437\u0430\u0434\u0430\u0447\u043a\u0443. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <em>put,<\/em> \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\u0443 \u0438 \u043a\u043b\u0430\u0434\u0435\u0442 \u0435\u0435 \u0432 \u043d\u0430\u0448\u0443 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u044e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c.\u00a0<\/p>\n<p>\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043c tid (task_id) \u0438 print \u0432 \u043a\u043e\u0434 \u0437\u0430\u0434\u0430\u0447\u0438:<\/p>\n<pre><code class=\"python\">import asyncio from dataclasses import dataclass  @dataclass class Task:     tid: int      async def perform(self, pool):         print('start perform', self.tid)         await asyncio.sleep(3)         print('complete perform', self.tid)<\/code><\/pre>\n<p>\u0418 \u0434\u043e\u0431\u0430\u0432\u0438\u043c 10 \u0437\u0430\u0434\u0430\u0447 \u043f\u0435\u0440\u0435\u0434 \u0441\u0442\u0430\u0440\u0442\u043e\u043c pool:<\/p>\n<pre><code class=\"python\">async def start(pool):     pool = Pool(3)     for tid in range(10):         await pool.put(Task(tid))     pool.start()     await asyncio.sleep(5)<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0449\u0435 \u043a\u043e\u0435-\u0447\u0442\u043e. \u0423 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 queue \u0435\u0441\u0442\u044c \u043c\u0435\u0442\u043e\u0434 <em>join.<\/em> \u0422\u043e\u0433\u0434\u0430 \u043a\u0440\u0430\u0443\u043b\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 \u0436\u0434\u0430\u0442\u044c \u043d\u0435 5 \u0441\u0435\u043a\u0443\u043d\u0434, \u043a\u0430\u043a \u043c\u044b \u0443\u043a\u0430\u0437\u0430\u043b\u0438 \u0432 \u043d\u0430\u0447\u0430\u043b\u0435, \u0430 \u0434\u043e \u0442\u0435\u0445 \u043f\u043e\u0440, \u043f\u043e\u043a\u0430 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0435 \u043e\u043f\u0443\u0441\u0442\u0435\u0435\u0442:<\/p>\n<pre><code class=\"python\">async def start(pool):     pool = Pool(3)     for tid in range(10):         await pool.put(Task(tid))     pool.start()     await pool.join()<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0435\u0442:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/901\/288\/0f6\/9012880f61a256f73434d0a6b0b85082.png\" width=\"1742\" height=\"432\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/901\/288\/0f6\/9012880f61a256f73434d0a6b0b85082.png\"\/><\/figure>\n<p>\u0425\u043e\u0442\u044f \u0432\u0441\u0435 \u0437\u0430\u0432\u0438\u0441\u043b\u043e, \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u043b.\u00a0<\/p>\n<p>\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u0447\u0442\u043e \u0437\u0430\u0434\u0430\u0447\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f 3 \u0441\u0435\u043a\u0443\u043d\u0434\u044b. \u0418, \u043d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0435\u0449\u0435 \u043d\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043b\u0438\u0441\u044c, \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u043d\u043e\u0432\u044b\u0435. \u042d\u0442\u043e \u043f\u043b\u043e\u0445\u043e, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0435\u0441\u043b\u0438 API \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u0435\u0435, \u0447\u0435\u043c \u043c\u044b \u0448\u043b\u0435\u043c \u043a \u043d\u0435\u043c\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u0435\u0441\u0442\u044c \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u044c \u00ab\u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c\u00bb \u0441\u0435\u0440\u0432\u0438\u0441. \u042d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u043c\u044b \u0440\u0435\u0448\u0438\u043c \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b join \u043e\u0442\u0440\u0430\u0431\u043e\u0442\u0430\u043b, \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u043c\u0435\u0447\u0430\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u044b\u043c\u0438. \u041d\u0435 \u0431\u0443\u0434\u0435\u043c \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u0442\u044c \u043a\u043e\u0434 scheduler \u0438 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e <em>_worker<\/em>. \u0412 \u043d\u0435\u0435 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0435\u043c perform \u0438 \u043d\u0438\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c <em>self._queue.task_done()<\/em>. \u042d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0437\u0430\u0434\u0430\u0447\u0443 \u043c\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043b\u0438:<\/p>\n<pre><code class=\"python\">async def _worker(self, task: Task):     await task.perform(self)     self._queue.task_done()<\/code><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e _worker \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0431\u0435\u0437 await, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e scheduler \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0436\u0434\u0430\u0442\u044c \u0435\u0433\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f. \u0418\u043d\u0430\u0447\u0435 \u043e\u043d \u043d\u0435 \u0443\u0441\u043f\u0435\u0435\u0442 \u0437\u0430\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438.\u00a0<\/p>\n<p>\u0412 scheduler \u0432\u043c\u0435\u0441\u0442\u043e perform \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c _worker \u0438 task:<\/p>\n<pre><code class=\"python\">async def _scheduler(self):     while self.is_running:         for _ in range(self.max_rate):             task = await self._queue.get()             asyncio.create_task(self._worker(task))         await asyncio.sleep(self.interval)<\/code><\/pre>\n<p>\u0421\u043d\u043e\u0432\u0430 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/4fb\/e7c\/dc9\/4fbe7cdc9e34e61906ac8e0e0c4911e5.png\" alt=\"\" title=\"\" width=\"1759\" height=\"430\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/4fb\/e7c\/dc9\/4fbe7cdc9e34e61906ac8e0e0c4911e5.png\"\/><\/figure>\n<p>\u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043b\u0430\u0441\u044c, \u043d\u043e \u043e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435 \u043e \u0442\u043e\u043c, \u0447\u0442\u043e scheduler \u043e\u0441\u0442\u0430\u043b\u0441\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 \u0444\u043e\u043d\u0435.\u00a0\u0424\u0443\u043d\u043a\u0446\u0438\u044e stop \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435.<\/p>\n<p><a class=\"anchor\" name=\"8\" id=\"8\"><\/a><\/p>\n<h2>Semaphore<\/h2>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u0432\u0438\u0434\u0438\u043c, \u0447\u0442\u043e:<\/p>\n<ul>\n<li>\n<p><em>\u043c\u0435\u0442\u043e\u0434 start \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u043d\u0430\u0448 Pool \u0438 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a scheduler<\/em><\/p>\n<\/li>\n<li>\n<p><em>\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a \u0440\u0430\u0437 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443 \u0441\u0442\u0430\u0432\u0438\u0442 \u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 _worker<\/em><\/p>\n<\/li>\n<li>\n<p><em>_worker \u044d\u0442\u0438 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442<\/em><\/p>\n<\/li>\n<li>\n<p><em>\u043c\u0435\u0442\u043e\u0434 join \u0436\u0434\u0435\u0442, \u043f\u043e\u043a\u0430 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0435 \u0441\u0442\u0430\u043d\u0435\u0442 \u043f\u0443\u0441\u0442\u043e\u0439<\/em><\/p>\n<\/li>\n<\/ul>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447 \u0431\u043e\u043b\u044c\u0448\u0435 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0430 \u0430\u043a\u0442\u0438\u0432\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u0430 (interval), \u043e\u043d \u043d\u0430\u043a\u0438\u0434\u044b\u0432\u0430\u0435\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0441\u0432\u0435\u0440\u0445\u0443 \u0442\u0435\u0445, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0435\u0449\u0435 \u043d\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043b\u0438\u0441\u044c.<\/p>\n<p>\u0412 \u0442\u0430\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u0437\u0430 interval \u0431\u0443\u0434\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 rate_limit. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0443\u0436\u043d\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f <em>concurrent_level,<\/em> \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0440\u0430\u0432\u043d\u0430 rate_limit.<\/p>\n<p>\u0412 asyncio \u0435\u0441\u0442\u044c \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 <em>Semaphore.<\/em> \u0421 \u0435\u0433\u043e \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u043e\u0436\u043d\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u0435\u043c\u044b\u0445 worker. \u0415\u0441\u043b\u0438 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447 \u0431\u043e\u043b\u044c\u0448\u0435 \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u043c\u044b \u0436\u0434\u0435\u043c \u0438\u0445 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f. \u0412 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0437\u0430\u0434\u0430\u0447 3.<\/p>\n<p>\u041e\u0431\u044a\u044f\u0432\u0438\u043c Semaphore \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0434\u0438\u043c \u0432 \u043d\u0435\u0433\u043e \u043b\u0438\u0431\u043e concurrent_level, \u043b\u0438\u0431\u043e max_rate.\u00a0<\/p>\n<p>\u041a\u043e\u0433\u0434\u0430 worker \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043d\u044f\u0442\u044c Semaphore. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u00ab\u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u044b\u0439 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u00bb: <em>async with self._sem. <\/em>\u041c\u044b \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u043c Semaphore, \u043f\u043e\u043a\u0430 \u043d\u0435 \u0437\u0430\u043a\u043e\u043d\u0447\u0430\u0442\u0441\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043d\u0438\u0436\u0435 \u2014 <em>await task.perform(self)<\/em> \u0438 <em>self._queue.task_done().<\/em>\u00a0<\/p>\n<pre><code class=\"python\">async def _worker(self, task: Task):     async with self._sem:         await task.perform(self)         self._queue.task_done()<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c Semaphore \u0432\u043d\u0443\u0442\u0440\u044c scheduler, \u0447\u0442\u043e\u0431\u044b scheduler \u043d\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u043b \u043d\u043e\u0432\u044b\u0435 worker&#8217;\u044b, \u0435\u0441\u043b\u0438 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 worker&#8217;\u043e\u0432 \u0443\u0436\u0435 \u0434\u043e\u0441\u0442\u0438\u0433\u043b\u043e \u043c\u0430\u043a\u0441\u0438\u043c\u0443\u043c\u0430:<\/p>\n<pre><code class=\"python\">async def _scheduler(self):     while self.is_running:         for _ in range(self.max_rate):             async with self._sem:                 task = await self._queue.get()                 asyncio.create_task(self._worker(task))         await asyncio.sleep(self.interval)<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c: <\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/dcf\/4ed\/b56\/dcf4edb5630306b9666845df2d35623a.png\" width=\"1747\" height=\"428\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/dcf\/4ed\/b56\/dcf4edb5630306b9666845df2d35623a.png\"\/><\/figure>\n<p>\u041c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 3 \u0437\u0430\u0434\u0430\u0447\u0438 \u0438 \u0436\u0434\u0435\u043c, \u043f\u043e\u043a\u0430 \u043e\u043d\u0438 \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u0442\u0441\u044f. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043c\u044b \u0441\u043e\u0431\u043b\u044e\u0434\u0430\u0435\u043c \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432.\u00a0<\/p>\n<p><a class=\"anchor\" name=\"9\" id=\"9\"><\/a><\/p>\n<h2>\u041e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0444\u043e\u043d\u043e\u0432\u043e\u0433\u043e \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u0430<\/h2>\n<p>\u0423 \u043d\u0430\u0441 \u043e\u0441\u0442\u0430\u043b\u0430\u0441\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0441 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u043c \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0435\u043c \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u0430. \u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u043a\u0440\u0430\u0443\u043b\u0435\u0440\u0430 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435 \u043e \u043d\u0435\u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e\u0439 \u043a\u043e\u0440\u0443\u0442\u0438\u043d\u0435.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u044d\u0442\u043e\u0433\u043e \u043d\u0435 \u0431\u044b\u043b\u043e, \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <em>stop:<\/em><\/p>\n<pre><code class=\"python\">async def stop(self):     self.is_running = False     self._scheduler_task.cancel()<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0432\u043d\u0443\u0442\u0440\u0438 \u043f\u0443\u043b\u0430 \u0437\u0430\u043a\u043e\u043d\u0447\u0430\u0442\u0441\u044f \u0437\u0430\u0434\u0430\u0447\u0438, \u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u0435\u0442\u043e\u0434 stop \u0432 \u043a\u043e\u043d\u0446\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 start:<\/p>\n<pre><code class=\"python\">async def start():     pool = Pool(3)     for tid in range(10):       await pool.put(Task(tid))     pool.start()     await pool.join()     await pool.stop()<\/code><\/pre>\n<p>\u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b31\/d65\/4dc\/b31d654dc0486886ea313bff205f385e.png\" width=\"1734\" height=\"431\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b31\/d65\/4dc\/b31d654dc0486886ea313bff205f385e.png\"\/><\/figure>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e.\u00a0<\/p>\n<p>\u041c\u044b \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0438 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a, \u043a\u043e\u0433\u0434\u0430 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u043b\u0438\u0441\u044c. \u041d\u043e \u0435\u0441\u043b\u0438 \u043c\u044b \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u043a\u0440\u0430\u0443\u043b\u0435\u0440 \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u044b, \u043d\u0430\u0447\u043d\u0443\u0442 \u043f\u043e\u044f\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u044f \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u043a\u0430\u043a\u0430\u044f-\u0442\u043e \u0437\u0430\u0434\u0430\u0447\u0430 \u043d\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043b\u0430\u0441\u044c:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f7e\/3bd\/7d0\/f7e3bd7d0678b93bf3e8de7da68dd03b.png\" width=\"1734\" height=\"432\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f7e\/3bd\/7d0\/f7e3bd7d0678b93bf3e8de7da68dd03b.png\"\/><\/figure>\n<p>\u0410 \u0447\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0435 \u0432\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f perform, \u0442\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0435 \u0431\u0443\u0434\u0435\u0442 \u0442\u0430\u043a\u0438\u0445 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439.\u00a0<\/p>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043e\u0436\u0438\u0434\u0430\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u0432\u0441\u0435 worker \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0442\u0441\u044f. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432\u0432\u0435\u0434\u0435\u043c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e, \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0430\u044e\u0449\u0443\u044e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u0445 worker: <em>concurrent_workers.<\/em> \u0418\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u043e\u043d\u0430 \u0440\u0430\u0432\u043d\u0430 0. \u041f\u0440\u0438\u00a0\u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u0432\u043e\u0440\u043a\u0435\u0440\u0430 \u043c\u044b \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0435\u043c concurrent_workers \u043d\u0430 1. \u041f\u0440\u0438 \u0432\u044b\u0445\u043e\u0434\u0435, \u043d\u0430\u043e\u0431\u043e\u0440\u043e\u0442, \u0443\u043c\u0435\u043d\u044c\u0448\u0430\u0435\u043c \u043d\u0430 1:<\/p>\n<pre><code class=\"python\">async def _worker(self, task: FetchTask):     async with self._sem:         self._cuncurrent_workers += 1         await task.perform(self)         self._queue.task_done()     self._cuncurrent_workers -= 1<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0443\u0436\u043d\u043e \u043a\u0430\u043a-\u0442\u043e \u0441\u043a\u0430\u0437\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 stop, \u0447\u0442\u043e \u0432\u0441\u0435 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0435 worker \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043b\u0438\u0441\u044c. \u042d\u0442\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0435\u0442, \u043a\u043e\u0433\u0434\u0430 is_running \u0431\u0443\u0434\u0435\u0442 false \u0438 concurrent_workers \u0441\u0442\u0430\u043d\u0435\u0442 \u0440\u0430\u0432\u043d\u043e\u0439 0.<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0435\u0441\u0442\u044c \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 <em>Event. <\/em>\u0412 \u043d\u0430\u0448\u0435\u043c \u043a\u043e\u0434\u0435 \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0433\u043e \u0432 Pool \u0438 \u043d\u0430\u0437\u043e\u0432\u0435\u043c <em>stop_event. <\/em>\u042d\u0442\u043e \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u043e\u0436\u043d\u043e \u0436\u0434\u0430\u0442\u044c <em>await self._stop_event.wait()<\/em> \u0434\u043e \u0442\u0435\u0445 \u043f\u043e\u0440, \u043f\u043e\u043a\u0430 \u043a\u0442\u043e-\u0442\u043e \u043d\u0435 \u0432\u044b\u0437\u043e\u0432\u0435\u0442 <em>self._stop_event.set():<\/em><\/p>\n<pre><code class=\"python\">class Pool:     def __init__(self, max_rate: int, interval: int = 1, concurrent_level: Optional[int] = None):         self.max_rate = max_rate         self.interval = interval         self.concurrent_level = concurrent_level         self.is_running = False         self._queue = asyncio.Queue()         self._scheduler_task: Optional[asyncio.Task] = None         self._sem = asyncio.Semaphore(concurrent_level or max_rate)         self._cuncurrent_workers = 0         self._stop_event = asyncio.Event()<\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u0440\u0430\u0432\u043d\u0430, \u0442\u043e \u0432\u0441\u0435 worker \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043b\u0438 \u0441\u0432\u043e\u044e \u0440\u0430\u0431\u043e\u0442\u0443, \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a \u043e\u0442\u043c\u0435\u043d\u0435\u043d \u0438 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438. \u0412 \u0442\u0430\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0432\u0441\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b Pool \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u0438\u043b\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043b\u0438 \u0441\u0432\u043e\u044e \u0440\u0430\u0431\u043e\u0442\u0443 \u2014 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0442\u044c.<\/p>\n<p>\u041d\u043e \u0435\u0441\u043b\u0438 concurrent_workers \u043d\u0435 \u0440\u0430\u0432\u043d\u0430 0, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u043c\u0435\u0442\u043e\u0434\u0430 stop \u043f\u043e\u0434\u043e\u0436\u0434\u0430\u0442\u044c \u0441\u043e\u0431\u044b\u0442\u0438\u0435 stop_event:<\/p>\n<pre><code class=\"python\">async def stop(self):     self.is_running = False     self._scheduler_task.cancel()     if self._cuncurrent_workers != 0:         await self._stop_event.wait()<\/code><\/pre>\n<p>\u041a\u043e\u0433\u0434\u0430 Pool \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d, \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u0439 worker \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0435:<\/p>\n<pre><code class=\"python\">    async def _worker(self, task: FetchTask):         async with self._sem:             self._cuncurrent_workers += 1             await task.perform(self)             self._queue.task_done()         self._cuncurrent_workers -= 1         if not self.is_running and self._cuncurrent_workers == 0:             self._stop_event.set()<\/code><\/pre>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e main, \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0435 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e:<\/p>\n<pre><code class=\"python\">def main():     loop = asyncio.get_event_loop()     pool = Pool(3)     try:         loop.run_until_complete(start(pool))     except KeyboardInterrupt:         loop.run_until_complete(pool.stop())         loop.close()<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442. \u041f\u043e\u0441\u043b\u0435 \u043d\u0430\u0436\u0430\u0442\u0438\u044f Ctrl + C \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u043e\u0441\u0442\u0430\u0432\u0448\u0438\u0435\u0441\u044f \u0437\u0430\u0434\u0430\u0447\u0438, \u0438 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0435\u0442\u0441\u044f:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/02e\/362\/9dd\/02e3629ddba3d00ff12fa045147b682c.png\" width=\"1731\" height=\"371\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/02e\/362\/9dd\/02e3629ddba3d00ff12fa045147b682c.png\"\/><\/figure>\n<p><a class=\"anchor\" name=\"10\" id=\"10\"><\/a><\/p>\n<h2>\u0420\u0430\u0431\u043e\u0442\u0430 \u043a\u0440\u0430\u0443\u043b\u0435\u0440\u0430 \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043e\u0431\u043a\u0430\u0447\u043a\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u0431\u043b\u043e\u0433\u0430 \u043d\u0430 \u0425\u0430\u0431\u0440\u0435<\/h2>\n<p>\u041c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u043c\u0435\u0445\u0430\u043d\u0438\u043a\u0443 \u043f\u0443\u043b\u0430 \u043d\u0430 \u043d\u0430\u0448\u0435\u0439 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0442\u043d\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u043a\u0435 task.<\/p>\n<p>\u0414\u043b\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u044d\u0442\u0430\u043f\u0430 \u044f \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043b \u0437\u0430\u0434\u0430\u0447\u0443 <em>FetchTask.\u00a0<\/em><\/p>\n<details class=\"spoiler\">\n<summary>fetch_task<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">MAX_DEPTH = 2 PARSED_URLS = set()   @dataclass class FetchTask(Task):     url: URL     depth: int      def parser(self, data: str) -> List['FetchTask']:         if self.depth + 1 > MAX_DEPTH:             return []         soup = BeautifulSoup(data, 'lxml')         res = []         for link in soup.find_all('a', href=True):             new_url = URL(link['href'])             if new_url.host is None and new_url.path.startswith('\/'):                 new_url = URL.build(                     scheme=self.url.scheme,                     host=self.url.host,                     path=new_url.path,                     query_string=new_url.query_string                 )                 if new_url in PARSED_URLS:                     continue                 PARSED_URLS.add(new_url)                 res.append(FetchTask(                     tid=self.tid,                     url=new_url,                     depth=self.depth + 1                 ))         return res      async def perform(self, pool):         async with aiohttp.ClientSession() as session:             async with session.get(self.url) as resp:                 print(self.url, resp.status)                 data = await resp.text()                 res: List[FetchTask] = await asyncio.get_running_loop().run_in_executor(                     None, self.parser, data                 )                 for task in res:                     await pool.put(task)<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0412\u043d\u0443\u0442\u0440\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 parcer \u0435\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f soup,\u00a0 \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0430 \u043a\u0430\u043a <em>soup = BeautifulSoup(data, \u2019lxml\u2019).\u00a0<\/em>\u0414\u0430\u043c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u043f\u043e\u044f\u0441\u043d\u0435\u043d\u0438\u044f. <\/p>\n<p><strong>BeautifulSoup<\/strong> \u2014 \u043f\u0430\u0440\u0441\u0435\u0440 \u0434\u043b\u044f \u0430\u043d\u0430\u043b\u0438\u0437\u0430 HTML\/XML.\u00a0<\/p>\n<p><strong>lxml<\/strong> \u2014 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f HTML\/XML \u043f\u0430\u0440\u0441\u0435\u0440\u0430<strong>.<\/strong> \u0418\u0437-\u0437\u0430 GIL \u043c\u044b \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c res \u0432\u043d\u0443\u0442\u0440\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 perform \u0447\u0435\u0440\u0435\u0437 executor:<\/p>\n<pre><code class=\"python\">async def perform(self, pool):     async with aiohttp.ClientSession() as session:       async with session.get(self.url) as resp:           print(self.url, resp.status)           data = await resp.text()           res: List[FetchTask] = await asyncio.get_running_loop().run_in_executor(             None, self.parser, data           )           for task in res:             await pool.put(task)<\/code><\/pre>\n<p>GIL \u2014 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0437\u0430\u043f\u0440\u0435\u0449\u0430\u0435\u0442 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u0442\u043e\u043a\u0438 \u0432 Python. \u041d\u043e \u0435\u0441\u043b\u0438 \u0432\u044b \u043f\u0438\u0448\u0438\u0442\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u043d\u0430 \u0421, \u0435\u0441\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u00ab\u043e\u0442\u043f\u0443\u0441\u0442\u0438\u0442\u044c\u00bb GIL.<\/p>\n<p>\u041f\u0430\u0440\u0441\u0435\u0440 lxml \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430 \u0421. \u0423 \u0441\u0435\u0431\u044f \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c \u043e\u043d \u0443\u043c\u0435\u0435\u0442 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u0442\u044c GIL \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u043f\u043e\u0442\u043e\u043a\u0435. \u042d\u0442\u043e \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0441\u044f \u0438 \u043a \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0434\u0440\u0443\u0433\u0438\u043c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f\u043c: <a href=\"https:\/\/lxml.de\/2.0\/FAQ.html#id1\"><u>https:\/\/lxml.de\/2.0\/FAQ.html#id1<\/u><\/a><\/p>\n<p>\u0412 fetch_task \u0442\u0430\u043a\u0436\u0435 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e perform, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043d\u0443\u0436\u043d\u043e \u0441\u0445\u043e\u0434\u0438\u0442\u044c \u0432 \u0441\u0435\u0442\u044c. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u044f \u0432\u0437\u044f\u043b aiohttp client.<\/p>\n<p>\u0412 \u0437\u0430\u0434\u0430\u0447\u0435 FetchTask \u043c\u044b \u0438\u0434\u0435\u043c \u043d\u0430 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 URL, \u043e\u0442\u0442\u0443\u0434\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c executor \u0434\u043b\u044f \u0438\u0445 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438. \u041d\u0443\u0436\u043d\u043e \u0432\u0437\u044f\u0442\u044c \u0432\u0441\u0435 \u0441\u0441\u044b\u043b\u043a\u0438 \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0435, \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043d\u0430 \u043d\u0438\u0445 \u0438 \u0442\u043e\u0436\u0435 \u043e\u0431\u043a\u0430\u0447\u0430\u0442\u044c:<\/p>\n<pre><code class=\"python\">    def parser(self, data: str) -> List['FetchTask']:         if self.depth + 1 > MAX_DEPTH:             return []         soup = BeautifulSoup(data, 'lxml')         res = []         for link in soup.find_all('a', href=True):             new_url = URL(link['href'])             if new_url.host is None and new_url.path.startswith('\/'):                 new_url = URL.build(                     scheme=self.url.scheme,                     host=self.url.host,                     path=new_url.path,                     query_string=new_url.query_string                 )                 if new_url in PARSED_URLS:                     continue                 PARSED_URLS.add(new_url)                 res.append(FetchTask(                     tid=self.tid,                     url=new_url,                     depth=self.depth + 1                 ))         return res<\/code><\/pre>\n<p>\u0412 \u043a\u043e\u043d\u0446\u0435 \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043d\u043e\u0432\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443 \u0438 \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0435\u043c \u043d\u0430 1 \u0433\u043b\u0443\u0431\u0438\u043d\u0443 depth.<\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043b\u0438 \u0437\u0430\u0434\u0430\u0447\u043a\u0443 habr.com, \u0433\u043b\u0443\u0431\u0438\u043d\u0430 \u0431\u044b\u043b\u0430 \u0440\u0430\u0432\u043d\u0430 1. \u041c\u044b \u0441\u043a\u0430\u0447\u0430\u043b\u0438 \u044d\u0442\u043e\u0442 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0435\u0441\u0442\u044c \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0441\u0441\u044b\u043b\u043a\u0438: \u0431\u043b\u043e\u0433\u0438 Mail.ru, Yandex \u0438\u043b\u0438 KTS. \u041a\u043e\u0433\u0434\u0430 \u043c\u044b \u0441\u0442\u0430\u043b\u0438 \u043e\u0431\u043a\u0430\u0447\u0438\u0432\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u0433\u043b\u0443\u0431\u0438\u043d\u0430 \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u043b\u0430\u0441\u044c \u0434\u043e 2. \u042d\u0442\u043e\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u043e\u0431\u043a\u0430\u0447\u0438\u0432\u0430\u0435\u043c\u044b\u0445 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432, \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u2014 \u0433\u043b\u0443\u0431\u0438\u043d\u044b.\u00a0<\/p>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u0441\u0435\u0449\u0435\u043d\u043d\u044b\u0445 \u0441\u0442\u0440\u0430\u043d\u0438\u0447\u0435\u043a <em>PARSED_URLS. <\/em>\u0422\u0430\u043a \u043c\u044b \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u0434\u0432\u0430\u0436\u0434\u044b \u043f\u043e\u0441\u0435\u0449\u0430\u0442\u044c \u043e\u0434\u043d\u0438 \u0438 \u0442\u0435 \u0436\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u043a\u0440\u0430\u0443\u043b\u0435\u0440 \u0438\u0437 fetch_task \u0438 \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u043c start:<\/p>\n<pre><code class=\"python\">async def start(pool):     await pool.put(         FetchTask(URL('https:\/\/habr.com\/ru\/company\/kts\/blog\/'), 1)     )     pool.start()     await pool.join()     await pool.stop()<\/code><\/pre>\n<p>\u0412\u044b\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c 3 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443 \u0438 \u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u043a\u0430\u043a \u043d\u0430\u0448 \u043a\u0440\u0430\u0443\u043b\u0435\u0440 \u043f\u043e\u0442\u0438\u0445\u043e\u043d\u044c\u043a\u0443 \u043e\u0431\u043a\u0430\u0447\u0438\u0432\u0430\u0435\u0442 \u0425\u0430\u0431\u0440:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/1eb\/4b1\/9a3\/1eb4b19a37f38488bdbda86f7e5259e0.png\" width=\"1752\" height=\"436\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1eb\/4b1\/9a3\/1eb4b19a37f38488bdbda86f7e5259e0.png\"\/><\/figure>\n<p><a class=\"anchor\" name=\"11\" id=\"11\"><\/a><\/p>\n<h2>\u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/h2>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u0432\u0441\u0435! \u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0432\u0441\u0435\u043c, \u043a\u0442\u043e \u0434\u043e\u0447\u0438\u0442\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u044e.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u043b\u0438\u0441\u044c \u0441 \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u043c\u0438 \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u0435\u0441\u044c \u0441\u0432\u043e\u0438\u043c \u043e\u043f\u044b\u0442\u043e\u043c \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445.<\/p>\n<hr\/>\n<p><strong>\u0414\u0440\u0443\u0433\u0438\u0435 \u043d\u0430\u0448\u0438 \u0441\u0442\u0430\u0442\u044c\u0438 \u043f\u043e \u0431\u044d\u043a\u0435\u043d\u0434\u0443 \u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u043c\u0443 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u0434\u043b\u044f \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0449\u0438\u0445:<\/strong><\/p>\n<ul>\n<li>\n<p>\u0426\u0438\u043a\u043b \u0441\u0442\u0430\u0442\u0435\u0439 \u00ab\u041f\u0435\u0440\u0432\u044b\u0435 \u0448\u0430\u0433\u0438 \u0432 aiohttp\u00bb:\u00a0<a href=\"https:\/\/habr.com\/ru\/companies\/kts\/articles\/560058\/\">\u043f\u0438\u0448\u0435\u043c \u043f\u0435\u0440\u0432\u043e\u0435 hello-world-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/a>,\u00a0<a href=\"https:\/\/habr.com\/ru\/companies\/kts\/articles\/579214\/\">\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445<\/a>,\u00a0<a href=\"https:\/\/habr.com\/ru\/companies\/kts\/articles\/580202\/\">\u0432\u044b\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442 \u0432 \u0418\u043d\u0442\u0435\u0440\u043d\u0435\u0442<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/companies\/kts\/articles\/727528\/\">\u0412\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f 5 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u0432 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438 \u043d\u0430 Python<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/companies\/kts\/articles\/713084\/\">\u0420\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u043c\u0441\u044f \u0432 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0441\u0442\u0438: \u0433\u0434\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u043e, \u0430 \u0433\u0434\u0435 \u2014 \u043d\u0435\u0442?<\/a><\/p>\n<\/li>\n<\/ul>\n<p><strong>\u0414\u0440\u0443\u0433\u0438\u0435 \u043d\u0430\u0448\u0438 \u0441\u0442\u0430\u0442\u044c\u0438 \u043f\u043e \u0431\u044d\u043a\u0435\u043d\u0434\u0443 \u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u043c\u0443 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u043e\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f:<\/strong><\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/companies\/kts\/articles\/598575\/\">\u041f\u0438\u0448\u0435\u043c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u0422\u0435\u043b\u0435\u0433\u0440\u0430\u043c-\u0431\u043e\u0442\u0430<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/companies\/kts\/articles\/659341\/\">\u041f\u0438\u0448\u0435\u043c Websocket-\u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043b\u044f \u0433\u0435\u043e\u043b\u043e\u043a\u0430\u0446\u0438\u0438 \u043d\u0430 asyncio<\/a><\/p>\n<\/li>\n<\/ul>\n<hr\/>\n<p><a class=\"anchor\" name=\"11\" id=\"11\"><\/a><\/p>\n<h3>\u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043b\u043e\u0432 \u043e \u043a\u0443\u0440\u0441\u0435 \u043f\u043e \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u043c\u0443 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u043d\u0430 Python ?<\/h3>\n<p><a href=\"https:\/\/vk.cc\/cmUy1v\">\u042d\u0442\u043e\u0442 \u043a\u0443\u0440\u0441<\/a> \u2014 \u043c\u0430\u0441\u0442 \u0445\u0435\u0432 \u0434\u043b\u044f \u0442\u0435\u0445, \u043a\u0442\u043e \u0445\u043e\u0447\u0435\u0442 \u043f\u0440\u043e\u043a\u0430\u0447\u0430\u0442\u044c \u0445\u0430\u0440\u0434\u044b \u0438 \u0441\u0442\u0430\u0442\u044c \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0441\u0442\u043e\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0435 \u0431\u043e\u0438\u0442\u0441\u044f \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432.<\/p>\n<ul>\n<li>\n<p>\u0412\u044b \u0440\u0430\u0437\u0431\u0435\u0440\u0451\u0442\u0435\u0441\u044c, \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u00ad\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438 \u0433\u0434\u0435 \u0435\u0433\u043e \u043b\u0443\u0447\u0448\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c.<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0443\u0447\u0438\u0442\u0435\u0441\u044c \u043c\u044b\u0441\u043b\u0438\u0442\u044c \u043d\u0435\u043b\u0438\u043d\u0435\u0439\u043d\u043e \u0438 \u0441\u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u0440\u043e\u0434\u0443\u043c\u044b\u0432\u0430\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u044b\u0435 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439.<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u043b\u0443\u0447\u0438\u0442\u0435 \u043e\u043f\u044b\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438 \u0438 \u0443\u0437\u043d\u0430\u0435\u0442\u0435 best practices \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u043d\u0430 Python.<\/p>\n<\/li>\n<\/ul>\n<p>? <strong>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u043a\u0443\u0440\u0441\u0430 \u0438 \u0434\u0430\u0442\u0443 \u0441\u0442\u0430\u0440\u0442\u0430:<\/strong> <a href=\"https:\/\/vk.cc\/cmUy1v\">https:\/\/vk.cc\/cmUy1v<\/a><\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/583036\/\"> https:\/\/habr.com\/ru\/articles\/583036\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><\/figure>\n<p>\u041f\u0440\u0438\u0432\u0435\u0442!<\/p>\n<p>\u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440, \u044f \u0440\u0443\u043a\u043e\u0432\u043e\u0436\u0443 backend-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u0432 <a href=\"https:\/\/habr.com\/ru\/company\/kts\/profile\/\"><u>\u041a\u0422S<\/u><\/a>. \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043a\u0440\u0430\u0443\u043b\u0435\u0440.<\/p>\n<p>\u0422\u0430\u043a\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u0447\u0430\u0441\u0442\u043e \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435, \u043a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044e\/\u043e\u0431\u043a\u0430\u0447\u043a\u0443 \u043c\u0435\u0436\u0434\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438.\u00a0<\/p>\n<p>\u0421\u0442\u0430\u0442\u044c\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0430 \u043f\u043e \u043c\u043e\u0442\u0438\u0432\u0430\u043c \u0432\u0435\u0431\u0438\u043d\u0430\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u043b\u0438 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043a\u0443\u0440\u0441\u0430 <a href=\"https:\/\/vk.cc\/cmUy1v\"><u>\u00ab\u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430 Python \u0434\u043b\u044f \u0434\u0436\u0443\u043d\u0438\u043e\u0440-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432\u00bb.<\/u><\/a>  \u0415\u0441\u043b\u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e, \u0437\u0430\u0433\u043b\u044f\u043d\u0438\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c.<\/p>\n<p>\u0427\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0432 \u0441\u0442\u0430\u0442\u044c\u0435:<\/p>\n<ul>\n<li>\n<p><a href=\"#1\">\u0426\u0435\u043b\u044c<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#2\">\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#3\">\u041f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#4\">\u0417\u0430\u0434\u0430\u0447\u0430 \u0434\u043b\u044f \u043a\u0440\u0430\u0443\u043b\u0435\u0440\u0430<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#5\">\u041f\u0440\u043e\u0431\u043d\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#6\">\u041f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u0439 \u0438\u0442\u043e\u0433<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#7\">\u0424\u0443\u043d\u043a\u0446\u0438\u0438 put \u0438 join<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#8\">Semaphore<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#9\">\u041e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0444\u043e\u043d\u043e\u0432\u043e\u0433\u043e \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u0430<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#10\">\u0420\u0430\u0431\u043e\u0442\u0430 \u043a\u0440\u0430\u0443\u043b\u0435\u0440\u0430 \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043e\u0431\u043a\u0430\u0447\u043a\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u0431\u043b\u043e\u0433\u0430 \u043d\u0430 \u0425\u0430\u0431\u0440\u0435<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#11\">\u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043b\u043e\u0432 \u043e \u043a\u0443\u0440\u0441\u0435 \u043f\u043e \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u043c\u0443 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u043d\u0430 Python <\/a><\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"1\" id=\"1\"><\/a><\/p>\n<h2>\u0426\u0435\u043b\u044c<\/h2>\n<p>\u0423 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u043a\u0440\u0430\u0443\u043b\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u043a\u0430\u0447\u0438\u0432\u0430\u0435\u0442 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b. \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0439 \u0431\u043e\u0442 Google, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0445\u043e\u0434\u0438\u0442 \u043f\u043e \u0441\u0430\u0439\u0442\u0430\u043c, \u0441\u043a\u0430\u0447\u0438\u0432\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435, \u043a\u043b\u0430\u0434\u0435\u0442 \u0432 \u0431\u0430\u0437\u0443 \u0438 \u0438\u043d\u0434\u0435\u043a\u0441\u0438\u0440\u0443\u0435\u0442, \u0438\u043b\u0438 \u043a\u0430\u043a\u043e\u0439-\u043d\u0438\u0431\u0443\u0434\u044c \u0430\u0433\u0440\u0435\u0433\u0430\u0442\u043e\u0440: \u0430\u043f\u0442\u0435\u043a, \u043c\u0430\u0440\u043a\u0435\u0442\u043f\u043b\u0435\u0439\u0441\u043e\u0432 \u0438 \u0442.\u0434.<\/p>\n<p>\u0417\u0430\u0434\u0430\u0447\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043a\u0440\u0430\u0443\u043b\u0435\u0440 \u0434\u043e\u043b\u0436\u0435\u043d \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0438 \u043d\u0435 \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u043d \u043e\u0431\u043a\u0430\u0447\u0438\u0432\u0430\u0435\u0442.\u00a0<\/p>\n<p>\u041a\u043e\u0434 \u0434\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0430\u0431\u043e\u0442\u044b:<\/p>\n<pre><code class=\"python\">import asyncio from dataclasses import dataclass from typing import Optional  class Pool:     def __init__(self, max_rate: int, interval: int = 1, concurrent_level: Optional[int] = None):         self.max_rate = max_rate         self.interval = interval         self.concurrent_level = concurrent_level          async def start(pool):   await asyncio.sleep(5)      def main():     loop = asyncio.get_event_loop()          try:         loop.run_until_complete(start())     except KeyboardInterrupt:         loop.close()  if __name__ == '__main__':     main()<\/code><\/pre>\n<p>\u041a\u0440\u0430\u0443\u043b\u0435\u0440\u0443 \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0441\u0435\u0442\u0438\u0442\u044c \u0438 \u0441\u043a\u0430\u0447\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446, \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u043c\u043d\u043e\u0433\u043e \u0440\u0430\u0437 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c\u0441\u044f \u043a \u0440\u0435\u0441\u0443\u0440\u0441\u0443. \u041c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442\u044c \u0441\u0435\u0431\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u043d\u043e \u0441\u0435\u0440\u0432\u0438\u0441, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u043c, \u043c\u043e\u0436\u0435\u0442 \u043d\u0435 \u0432\u044b\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0438. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043a \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0443\u0436\u043d\u043e \u0445\u043e\u0434\u0438\u0442\u044c \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c\u043e \u2014 \u0441\u0434\u0435\u043b\u0430\u0442\u044c <em>rate-limit.\u00a0<\/em><\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432 \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u043c\u043e\u043c\u0435\u043d\u0442 \u0437\u0430\u0434\u0430\u0447\u0430 \u043f\u0440\u0435\u0440\u0432\u0430\u043b\u0430\u0441\u044c, \u0438\u043b\u0438 \u043c\u044b \u0441\u0430\u043c\u0438 \u0440\u0435\u0448\u0438\u043b\u0438 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043a\u0440\u0430\u0443\u043b\u0435\u0440, \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u0443\u044e \u0438 \u0430\u043a\u043a\u0443\u0440\u0430\u0442\u043d\u0443\u044e \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443 \u0440\u0430\u0431\u043e\u0442\u044b. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u0447\u0430\u0442\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c\u0441\u044f, \u0430 \u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0438\u0437 \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u0440\u0435\u043a\u0440\u0430\u0442\u0438\u0442\u044c \u043f\u043e\u0441\u0442\u0443\u043f\u0430\u0442\u044c.\u00a0<\/p>\n<h2>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434<\/h2>\n<p>\u0423 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c <em>Pool.<\/em> \u042d\u0442\u0430 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c \u0443\u043c\u0435\u0435\u0442 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u0435\u0434\u0438\u043d\u0438\u0446\u0443 \u0432\u0440\u0435\u043c\u0435\u043d\u0438. Pool \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442:<\/p>\n<ul>\n<li>\n<p><em>max_rate<\/em> \u2014 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/p>\n<\/li>\n<li>\n<p><em>interval<\/em> \u2014 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b. \u0415\u0441\u043b\u0438 \u043c\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f max_rate = 5 \u0438 interval = 1, \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443 \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f 5 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/p>\n<\/li>\n<li>\n<p><em>concurrent_level<\/em> \u2014 \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/p>\n<\/li>\n<\/ul>\n<p>max_rate \u0438 concurrent_level \u043c\u043e\u0433\u0443\u0442 \u043d\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u0432\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0431\u043e\u043b\u044c\u0448\u0435, \u0447\u0435\u043c interval. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043c\u044b \u0434\u0435\u043b\u0430\u0435\u043c 5 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443, \u043a\u0430\u043a \u0437\u0430\u044f\u0432\u043b\u0435\u043d\u043e \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445, \u043d\u043e API \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u0435\u0435. \u0427\u0442\u043e\u0431\u044b \u043d\u0435 \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441, \u043c\u044b \u0432\u0432\u043e\u0434\u0438\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e concurrent_level. <\/p>\n<p><a class=\"anchor\" name=\"3\" id=\"3\"><\/a><\/p>\n<h2>\u041f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a<\/h2>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0434\u0435\u043b\u0430\u0442\u044c \u0440\u043e\u0432\u043d\u043e 5 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443, \u043d\u0435 \u043e\u0431\u0440\u0430\u0449\u0430\u044f \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0432\u0440\u0435\u043c\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0430\u0437\u043e\u0432\u0435\u043c <em>scheduler.<\/em> \u041e\u043d \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0441\u044b\u043f\u0430\u0442\u044c\u0441\u044f \u0440\u0430\u0437 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443 \u0438 \u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u0434\u0430\u0447, \u0440\u0430\u0432\u043d\u043e\u0435 max_rate. \u041f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a \u043d\u0435 \u0436\u0434\u0435\u0442 \u0438\u0445 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f, \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u0435\u0442 5 \u0437\u0430\u0434\u0430\u0447 \u043a\u0430\u0436\u0434\u0443\u044e \u0441\u0435\u043a\u0443\u043d\u0434\u0443.\u00a0<\/p>\n<p>\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u043c class Pool \u0438 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e scheduler:<\/p>\n<pre><code class=\"python\">from task import Task  class Pool:     def __init__(self, max_rate: int, interval: int = 1, concurrent_level: Optional[int] = None):         self.max_rate = max_rate         self.interval = interval         self.concurrent_level = concurrent_level         self.is_running = False               async def _scheduler(self):         while self.is_running:             for _ in range(self.max_rate):                 pass<\/code><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0434\u0432\u0435 \u0432\u0435\u0449\u0438:<\/p>\n<ul>\n<li>\n<p>\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0431\u0435\u0441\u043a\u043e\u043d\u0435\u0447\u043d\u0430\u044f, \u043f\u043e\u043a\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430\u0448 \u043a\u0440\u0430\u0443\u043b\u0435\u0440<\/p>\n<\/li>\n<li>\n<p>\u0440\u0430\u0437 \u0432 \u043f\u0435\u0440\u0438\u043e\u0434 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 max_rate \u0440\u0430\u0437 \u043a\u0430\u043a\u043e\u0435-\u0442\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 <\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"4\" id=\"4\"><\/a><\/p>\n<h2>\u0417\u0430\u0434\u0430\u0447\u0430 \u0434\u043b\u044f \u043a\u0440\u0430\u0443\u043b\u0435\u0440\u0430<\/h2>\n<p>Scheduler \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0442\u043a\u0443\u0434\u0430-\u0442\u043e \u0432\u0437\u044f\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0432\u043e\u0437\u044c\u043c\u0435\u043c \u0438\u0437 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 asyncio. \u041f\u0440\u0438\u043c\u0438\u0442\u0438\u0432 \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f asyncio.Queue(). \u0412 class Pool \u0434\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c:<\/p>\n<pre><code class=\"python\">class Pool:     def __init__(self, max_rate: int, interval: int = 1, concurrent_level: Optional[int] = None):         self.max_rate = max_rate         self.interval = interval         self.concurrent_level = concurrent_level         self.is_running = False         self._queue = asyncio.Queue()<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043f\u0440\u043e\u0441\u044b\u043f\u0430\u0435\u043c\u0441\u044f \u0440\u0430\u0437 \u0432 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u0434\u0430\u0447, \u0440\u0430\u0432\u043d\u043e\u0435 max_rate. \u041d\u043e \u043d\u0443\u0436\u043d\u043e \u0447\u0442\u043e-\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0438 \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u043b\u0438\u0441\u044c.<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432 asyncio \u0435\u0441\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044f <em>create_task. <\/em>\u041e\u043d\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a\u043e\u0440\u0443\u0442\u0438\u043d\u044b, \u043d\u043e \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043d\u0435 \u0434\u043e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f \u0435\u0435 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f, \u0430 \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u0444\u043e\u043d\u043e\u0432\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443. \u0412 create_task \u043f\u0435\u0440\u0435\u0434\u0430\u0434\u0438\u043c \u043c\u0435\u0442\u043e\u0434 <em>perform.<\/em><\/p>\n<pre><code class=\"python\">    async def _scheduler(self):         while self.is_running:             for _ in range(self.max_rate):                 task = await self._queue.get()               asyncio.create_task(task.perform))             await asyncio.sleep(self.interval)<\/code><\/pre>\n<p><a class=\"anchor\" name=\"5\" id=\"5\"><\/a><\/p>\n<h2>\u041f\u0440\u043e\u0431\u043d\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a<\/h2>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0432\u0441\u0435 \u044d\u0442\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c. \u0421\u0434\u0435\u043b\u0430\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <em>start<\/em> \u0438 \u0442\u0430\u043a\u0438\u043c \u0436\u0435 \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c scheduler. \u041d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043d\u0435 \u0436\u0434\u0430\u0442\u044c \u0435\u0433\u043e, \u0430 \u043f\u0440\u043e\u0441\u0442\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0432 \u0444\u043e\u043d\u0435 \u043a\u043e\u0440\u0443\u0442\u0438\u043d\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e create_task:<\/p>\n<pre><code class=\"python\">    async def _scheduler(self):         while self.is_running:             for _ in range(self.max_rate):                 task = await self._queue.get()                 asyncio.create_task(self._worker(task))             await asyncio.sleep(self.interval)          def start(self):         self.is_running = True         asyncio.create_task(self._scheduler())<\/code><\/pre>\n<p>\u0412 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u0434\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0433\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u043a\u0440\u0430\u0443\u043b\u0435\u0440\u0430 \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 scheduler. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0437\u0432\u0430\u0442\u044c cancel \u0443 \u0437\u0430\u0434\u0430\u0447\u0438, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 create_task \u043c\u044b \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e <em>scheduler_task:<\/em><\/p>\n<pre><code class=\"python\">class Pool:     def __init__(self, max_rate: int, interval: int = 1, concurrent_level: Optional[int] = None):         self.max_rate = max_rate         self.interval = interval         self.concurrent_level = concurrent_level         self.is_running = False         self._queue = asyncio.Queue()         self._scheduler_task: Optional[asyncio.Task] = None<\/code><\/pre>\n<p>\u0412\u044b\u0441\u0442\u0430\u0432\u0438\u043c rate-limit \u043d\u0430 3 \u0438 \u0432\u043d\u0443\u0442\u0440\u0438 start \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u043d\u0430\u0448 Pool:<\/p>\n<pre><code class=\"python\">def start(self):     self.is_running = True     self._scheduler_task = asyncio.create_task(self._scheduler())          async def start(pool):     pool = Pool(3)     pool.start()     await asyncio.sleep(5)  <\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0438 \u0432\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u043e:<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u042d\u0442\u043e \u043f\u043e\u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435\u0442. \u041c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0441\u0442\u0430\u0440\u0442 \u0438 \u043f\u043e\u0441\u043f\u0430\u043b\u0438 5 \u0441\u0435\u043a\u0443\u043d\u0434, \u0430 \u043d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0438 \u0443 \u043d\u0430\u0441 \u043e\u0441\u0442\u0430\u043b\u0430\u0441\u044c \u0444\u043e\u043d\u043e\u0432\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 scheduler.\u00a0\u00a0<\/p>\n<p><a class=\"anchor\" name=\"6\" id=\"6\"><\/a><\/p>\n<h2>\u041f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u0439 \u0438\u0442\u043e\u0433<\/h2>\n<ol>\n<li>\n<p>\u0423 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c Pool \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438:<br \/>\u2014 <em>\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 max_rate<br \/>\u2014 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u0430\u043a\u0442\u0438\u0432\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u0430 interval<br \/>\u2014 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 concurrent_level<\/em><\/p>\n<\/li>\n<li>\n<p>\u041c\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a scheduler, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e, \u043f\u0440\u043e\u0441\u044b\u043f\u0430\u0435\u0442\u0441\u044f \u0440\u0430\u0437 \u0432 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b, \u0434\u043e\u0441\u0442\u0430\u0435\u0442 \u0438\u0437 \u043e\u0447\u0435\u0440\u0435\u0434\u0438 max_rate \u0437\u0430\u0434\u0430\u0447 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0438\u0445 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435.<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0434\u0430\u0447\u0430 task \u2014 \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u0430\u0442\u0430-\u043a\u043b\u0430\u0441\u0441 \u0441 \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439 perform. \u0414\u043b\u044f \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0438 \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043a\u043b\u0430\u0441\u0441-\u043d\u0430\u0441\u043b\u0435\u0434\u043d\u0438\u043a \u0438 \u0432 \u043d\u0435\u043c \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c perform.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0449\u0435 \u043c\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044e start, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0432\u044b\u0441\u0442\u0430\u0432\u0438\u043b\u0438 \u043f\u0440\u0438\u0437\u043d\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u044b is_running \u0438 \u0432 \u0444\u043e\u043d\u0435 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043b\u0438 \u043d\u0430\u0448 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a.<\/p>\n<\/li>\n<\/ol>\n<p><a class=\"anchor\" name=\"7\" id=\"7\"><\/a><\/p>\n<h2>\u0424\u0443\u043d\u043a\u0446\u0438\u0438 put \u0438 join<\/h2>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c, \u043a\u0430\u043a \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c <em>Pool,<\/em> \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c \u0442\u0443\u0434\u0430 \u0437\u0430\u0434\u0430\u0447\u043a\u0443. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <em>put,<\/em> \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\u0443 \u0438 \u043a\u043b\u0430\u0434\u0435\u0442 \u0435\u0435 \u0432 \u043d\u0430\u0448\u0443 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u044e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c.\u00a0<\/p>\n<p>\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043c tid (task_id) \u0438 print \u0432 \u043a\u043e\u0434 \u0437\u0430\u0434\u0430\u0447\u0438:<\/p>\n<pre><code class=\"python\">import asyncio from dataclasses import dataclass  @dataclass class Task:     tid: int      async def perform(self, pool):         print('start perform', self.tid)         await asyncio.sleep(3)         print('complete perform', self.tid)<\/code><\/pre>\n<p>\u0418 \u0434\u043e\u0431\u0430\u0432\u0438\u043c 10 \u0437\u0430\u0434\u0430\u0447 \u043f\u0435\u0440\u0435\u0434 \u0441\u0442\u0430\u0440\u0442\u043e\u043c pool:<\/p>\n<pre><code class=\"python\">async def start(pool):     pool = Pool(3)     for tid in range(10):         await pool.put(Task(tid))     pool.start()     await asyncio.sleep(5)<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0449\u0435 \u043a\u043e\u0435-\u0447\u0442\u043e. \u0423 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 queue \u0435\u0441\u0442\u044c \u043c\u0435\u0442\u043e\u0434 <em>join.<\/em> \u0422\u043e\u0433\u0434\u0430 \u043a\u0440\u0430\u0443\u043b\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 \u0436\u0434\u0430\u0442\u044c \u043d\u0435 5 \u0441\u0435\u043a\u0443\u043d\u0434, \u043a\u0430\u043a \u043c\u044b \u0443\u043a\u0430\u0437\u0430\u043b\u0438 \u0432 \u043d\u0430\u0447\u0430\u043b\u0435, \u0430 \u0434\u043e \u0442\u0435\u0445 \u043f\u043e\u0440, \u043f\u043e\u043a\u0430 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0435 \u043e\u043f\u0443\u0441\u0442\u0435\u0435\u0442:<\/p>\n<pre><code class=\"python\">async def start(pool):     pool = Pool(3)     for tid in range(10):         await pool.put(Task(tid))     pool.start()     await pool.join()<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0435\u0442:<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u0425\u043e\u0442\u044f \u0432\u0441\u0435 \u0437\u0430\u0432\u0438\u0441\u043b\u043e, \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u043b.\u00a0<\/p>\n<p>\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u0447\u0442\u043e \u0437\u0430\u0434\u0430\u0447\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f 3 \u0441\u0435\u043a\u0443\u043d\u0434\u044b. \u0418, \u043d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0435\u0449\u0435 \u043d\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043b\u0438\u0441\u044c, \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u043d\u043e\u0432\u044b\u0435. \u042d\u0442\u043e \u043f\u043b\u043e\u0445\u043e, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0435\u0441\u043b\u0438 API \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u0435\u0435, \u0447\u0435\u043c \u043c\u044b \u0448\u043b\u0435\u043c \u043a \u043d\u0435\u043c\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u0435\u0441\u0442\u044c \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u044c \u00ab\u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c\u00bb \u0441\u0435\u0440\u0432\u0438\u0441. \u042d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u043c\u044b \u0440\u0435\u0448\u0438\u043c \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b join \u043e\u0442\u0440\u0430\u0431\u043e\u0442\u0430\u043b, \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u043c\u0435\u0447\u0430\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u044b\u043c\u0438. \u041d\u0435 \u0431\u0443\u0434\u0435\u043c \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u0442\u044c \u043a\u043e\u0434 scheduler \u0438 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e <em>_worker<\/em>. \u0412 \u043d\u0435\u0435 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0435\u043c perform \u0438 \u043d\u0438\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c <em>self._queue.task_done()<\/em>. \u042d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0437\u0430\u0434\u0430\u0447\u0443 \u043c\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043b\u0438:<\/p>\n<pre><code class=\"python\">async def _worker(self, task: Task):     await task.perform(self)     self._queue.task_done()<\/code><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e _worker \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0431\u0435\u0437 await, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e scheduler \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0436\u0434\u0430\u0442\u044c \u0435\u0433\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f. \u0418\u043d\u0430\u0447\u0435 \u043e\u043d \u043d\u0435 \u0443\u0441\u043f\u0435\u0435\u0442 \u0437\u0430\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438.\u00a0<\/p>\n<p>\u0412 scheduler \u0432\u043c\u0435\u0441\u0442\u043e perform \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c _worker \u0438 task:<\/p>\n<pre><code class=\"python\">async def _scheduler(self):     while self.is_running:         for _ in range(self.max_rate):             task = await self._queue.get()             asyncio.create_task(self._worker(task))         await asyncio.sleep(self.interval)<\/code><\/pre>\n<p>\u0421\u043d\u043e\u0432\u0430 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c:<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043b\u0430\u0441\u044c, \u043d\u043e \u043e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435 \u043e \u0442\u043e\u043c, \u0447\u0442\u043e scheduler \u043e\u0441\u0442\u0430\u043b\u0441\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 \u0444\u043e\u043d\u0435.\u00a0\u0424\u0443\u043d\u043a\u0446\u0438\u044e stop \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435.<\/p>\n<p><a class=\"anchor\" name=\"8\" id=\"8\"><\/a><\/p>\n<h2>Semaphore<\/h2>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u0432\u0438\u0434\u0438\u043c, \u0447\u0442\u043e:<\/p>\n<ul>\n<li>\n<p><em>\u043c\u0435\u0442\u043e\u0434 start \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u043d\u0430\u0448 Pool \u0438 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a scheduler<\/em><\/p>\n<\/li>\n<li>\n<p><em>\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a \u0440\u0430\u0437 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443 \u0441\u0442\u0430\u0432\u0438\u0442 \u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 _worker<\/em><\/p>\n<\/li>\n<li>\n<p><em>_worker \u044d\u0442\u0438 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442<\/em><\/p>\n<\/li>\n<li>\n<p><em>\u043c\u0435\u0442\u043e\u0434 join \u0436\u0434\u0435\u0442, \u043f\u043e\u043a\u0430 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0435 \u0441\u0442\u0430\u043d\u0435\u0442 \u043f\u0443\u0441\u0442\u043e\u0439<\/em><\/p>\n<\/li>\n<\/ul>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447 \u0431\u043e\u043b\u044c\u0448\u0435 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0430 \u0430\u043a\u0442\u0438\u0432\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u0430 (interval), \u043e\u043d \u043d\u0430\u043a\u0438\u0434\u044b\u0432\u0430\u0435\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0441\u0432\u0435\u0440\u0445\u0443 \u0442\u0435\u0445, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0435\u0449\u0435 \u043d\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043b\u0438\u0441\u044c.<\/p>\n<p>\u0412 \u0442\u0430\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u0437\u0430 interval \u0431\u0443\u0434\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 rate_limit. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0443\u0436\u043d\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f <em>concurrent_level,<\/em> \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0440\u0430\u0432\u043d\u0430 rate_limit.<\/p>\n<p>\u0412 asyncio \u0435\u0441\u0442\u044c \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 <em>Semaphore.<\/em> \u0421 \u0435\u0433\u043e \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u043e\u0436\u043d\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e<\/p>\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-391197","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/391197","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=391197"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/391197\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=391197"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=391197"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=391197"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}