{"id":415884,"date":"2024-06-30T00:37:36","date_gmt":"2024-06-30T00:37:36","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=415884"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=415884","title":{"rendered":"<span>\u041d\u0430\u0447\u0430\u043b\u043e \u0440\u0430\u0431\u043e\u0442\u044b \u0441 MongoDB \u0438 Redis \u043d\u0430 Rust<\/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-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u043a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u0442\u044c Rust \u0431\u044d\u043a\u044d\u043d\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 <a href=\"https:\/\/www.mongodb.com\/\" rel=\"nofollow noopener noreferrer\">MongoDB<\/a>, \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e-\u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u0411\u0414, \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 <a href=\"https:\/\/redis.io\/\" rel=\"nofollow noopener noreferrer\">Redis<\/a> \u0434\u043b\u044f \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 HTTP \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0438 \u043d\u043e\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0414\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0435\u0439 \u043d\u0430\u0433\u043b\u044f\u0434\u043d\u043e\u0441\u0442\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0442\u0430\u043a\u0436\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c REST API. \u0412 \u0438\u0442\u043e\u0433\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/cf\/pi\/hi\/cfpihitbrguto9xkakvrcwae4pq.png\" alt=\"architecture\" data-src=\"https:\/\/habrastorage.org\/webt\/cf\/pi\/hi\/cfpihitbrguto9xkakvrcwae4pq.png\"\/><\/p>\n<p>  <\/p>\n<p>MongoDB \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435\u043c, \u0432 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a Redis \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u043a\u044d\u0448 (\u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f)<\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Rate_limiting\" rel=\"nofollow noopener noreferrer\">\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430<\/a> HTTP \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/li>\n<li>\u043d\u043e\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u0430 <a href=\"https:\/\/en.wikipedia.org\/wiki\/Publish%E2%80%93subscribe_pattern\" rel=\"nofollow noopener noreferrer\">publish-subscribe<\/a><\/li>\n<\/ul>\n<p><a name=\"habracut\"><\/a>  <\/p>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0435 \u043e\u0437\u043d\u0430\u0447\u0430\u044e\u0442, \u0447\u0442\u043e \u0434\u043b\u044f \u043f\u043e\u0445\u043e\u0436\u0435\u0433\u043e \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434, \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u0432 \u0441\u0442\u0430\u0442\u044c\u0435. \u041f\u0440\u0438\u043c\u0435\u0440\u044b \u0432 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0438\u043c\u0435\u044e\u0442 \u0446\u0435\u043b\u044c\u044e \u043f\u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c \u0432\u0430\u0441 \u0441 MongoDB \u0438 Redis.<\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/tree\/master\/mongodb-redis\" rel=\"nofollow noopener noreferrer\">\u041f\u0440\u043e\u0435\u043a\u0442<\/a> \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/github.com\/mongodb\/mongo-rust-driver\" rel=\"nofollow noopener noreferrer\">MongoDB Rust driver<\/a> \u0438 \u043a\u0440\u0435\u0439\u0442\u0430 <a href=\"https:\/\/github.com\/mitsuhiko\/redis-rs\" rel=\"nofollow noopener noreferrer\"><code>redis-rs<\/code><\/a>.<\/p>\n<p>  <\/p>\n<p>\u0412\u044b \u0441\u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c REST API \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043e\u043d\u043e \u0440\u0430\u0437\u0432\u0451\u0440\u043d\u0443\u0442\u043e \u043d\u0430 Google Cloud Platform.<\/p>\n<p>  <\/p>\n<p>\u0414\u043e\u043c\u0435\u043d\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e \u043f\u043b\u0430\u043d\u0435\u0442\u0430\u0445 \u0421\u043e\u043b\u043d\u0435\u0447\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0438 \u0438\u0445 \u0441\u043f\u0443\u0442\u043d\u0438\u043a\u0430\u0445.<\/p>\n<p>  <\/p>\n<h2 id=\"zapusk-mongodb-i-redis\">\u0417\u0430\u043f\u0443\u0441\u043a MongoDB \u0438 Redis<\/h2>\n<p>  <\/p>\n<p>\u042d\u0442\u043e\u0442 \u0440\u0430\u0437\u0434\u0435\u043b \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043d\u0430\u0432\u044b\u043a\u043e\u0432 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430 Rust \u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d \u0432\u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u044f\u0437\u044b\u043a\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>  <\/p>\n<p>\u041e\u0431\u0435 \u0442\u0443\u043b\u044b \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u044b \u043a\u0430\u043a Docker \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440:<\/p>\n<p>  <\/p>\n<p><em><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/docker-compose.yml\" rel=\"nofollow noopener noreferrer\"><code>docker-compose.yml<\/code><\/a><\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">version: '3.8'  services:    ...    mongodb:     image: mongo     container_name: mongodb     restart: always     environment:       MONGO_INITDB_ROOT_USERNAME: $MONGODB_USERNAME       MONGO_INITDB_ROOT_PASSWORD: $MONGODB_PASSWORD       MONGO_INITDB_DATABASE: solar_system_info     ports:       - 27017:27017    mongodb-seed:     image: mongo     container_name: mongodb-seed     depends_on:       - mongodb     volumes:       - .\/mongodb-init:\/mongodb-init     links:       - mongodb     command:       mongoimport --host mongodb --db solar_system_info --collection planets --authenticationDatabase admin --username $MONGODB_USERNAME --password $MONGODB_PASSWORD --drop --jsonArray --file \/mongodb-init\/init.json    redis:     image: redis:alpine     container_name: redis     ports:       - 6379:6379<\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 <code>mongodb-seed<\/code> \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u0434\u0430\u043b\u0435\u0435.<\/p>\n<p>  <\/p>\n<p>\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a <code>mongo<\/code> <a href=\"https:\/\/docs.mongodb.com\/manual\/mongo\/\" rel=\"nofollow noopener noreferrer\">shell<\/a> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u044b:<\/p>\n<p>  <\/p>\n<p><code>docker exec -it mongodb mongo --username admin --password password<\/code><\/p>\n<p>  <\/p>\n<p>(\u0433\u0434\u0435 <code>mongodb<\/code> \u2014 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 Docker \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430, <code>mongo<\/code> \u2014 shell)<\/p>\n<p>  <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u044b, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0431\u0430\u0437 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>show dbs<\/code><\/li>\n<li>\u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0432\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u043e\u0439 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445:<br \/> \n<ul>\n<li><code>use solar_system_info<\/code><\/li>\n<li><code>show collections<\/code><\/li>\n<li><code>db.planets.find()<\/code><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u0414\u043e\u0441\u0442\u0443\u043f \u043a <a href=\"https:\/\/redis.io\/topics\/rediscli\" rel=\"nofollow noopener noreferrer\">Redis CLI<\/a> \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0435\u043d \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u044b:<\/p>\n<p>  <\/p>\n<p><code>docker exec -it redis redis-cli<\/code><\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0438\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<p>  <\/p>\n<p><em>\u041f\u0440\u0438\u043c\u0435\u0440 \u043a\u043e\u043c\u0430\u043d\u0434\u044b Redis<\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">> set mykey somevalue OK > get mykey \"somevalue\"<\/code><\/pre>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u043a\u043b\u044e\u0447\u0435\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <code>keys *<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 \u0431\u043e\u043b\u044c\u0448\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432 \u043a\u043e\u043c\u0430\u043d\u0434 \u0434\u043b\u044f Redis CLI <a href=\"https:\/\/redis.io\/topics\/data-types-intro\" rel=\"nofollow noopener noreferrer\">\u0432 \u044d\u0442\u043e\u043c \u0433\u0430\u0439\u0434\u0435<\/a>.<\/p>\n<p>  <\/p>\n<h3 id=\"inicializaciya-dannyh\">\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445<\/h3>\n<p>  <\/p>\n<p>MongoDB \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442\u0441\u044f <a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/mongodb-init\/init.json\" rel=\"nofollow noopener noreferrer\">\u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 JSON<\/a> \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 <code>mongodb-seed<\/code> \u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>mongoimport<\/code>:<\/p>\n<p>  <\/p>\n<p><em><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/docker-compose.yml\" rel=\"nofollow noopener noreferrer\"><code>docker-compose.yml<\/code><\/a><\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">mongodb-seed:   image: mongo   container_name: mongodb-seed   depends_on:     - mongodb   volumes:     - .\/mongodb-init:\/mongodb-init   links:     - mongodb   command:     mongoimport --host mongodb --db solar_system_info --collection planets --authenticationDatabase admin --username $MONGODB_USERNAME --password $MONGODB_PASSWORD --drop --jsonArray --file \/mongodb-init\/init.json<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0411\u0414 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c <a href=\"https:\/\/docs.mongodb.com\/manual\/tutorial\/write-scripts-for-the-mongo-shell\" rel=\"nofollow noopener noreferrer\">JavaScript \u0444\u0430\u0439\u043b\u0430<\/a>.<\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0442\u0430\u043a\u0436\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441 <a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/tree\/master\/mongodb-redis\/images\" rel=\"nofollow noopener noreferrer\">\u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c\u0438<\/a> \u043f\u043b\u0430\u043d\u0435\u0442. \u041f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u044f \u0441\u043e\u0431\u0438\u0440\u0430\u043b\u0441\u044f \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438\u0445 \u0432 MongoDB; \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u0434\u0435\u043b\u0430\u043d\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u044b:<\/p>\n<p>  <\/p>\n<p><code>mongofiles --host mongodb --db solar_system_info --authenticationDatabase admin --username $MONGODB_USERNAME --password $MONGODB_PASSWORD put \/mongodb-init\/images\/*.jpg<\/code><\/p>\n<p>  <\/p>\n<p>\u041e\u0434\u043d\u0430\u043a\u043e \u0432\u0441\u043a\u043e\u0440\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u043b\u043e\u0441\u044c, \u0447\u0442\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043d\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u044b \u0438\u0437 \u0411\u0414 \u0438\u0437-\u0437\u0430 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 <a href=\"https:\/\/docs.mongodb.com\/manual\/core\/gridfs\/\" rel=\"nofollow noopener noreferrer\">GridFS<\/a> \u0432 MongoDB Rust Driver (<a href=\"https:\/\/jira.mongodb.org\/browse\/RUST-527\" rel=\"nofollow noopener noreferrer\">\u043e\u0442\u043a\u0440\u044b\u0442\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430<\/a>). \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u0440\u0435\u0439\u0442 <a href=\"https:\/\/github.com\/pyros2097\/rust-embed\" rel=\"nofollow noopener noreferrer\">rust_embed<\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 \u0431\u0438\u043d\u0430\u0440\u043d\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u0435\u043c\u044b\u0439 \u0444\u0430\u0439\u043b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438 (\u043f\u0440\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u0441\u044f \u0438\u0437 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b). (\u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0442\u0430\u043a\u0436\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u043e\u0442 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f; \u043f\u0430\u043f\u043a\u0430 <code>images<\/code> \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0441\u043c\u043e\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0430 \u043a\u0430\u043a volume \u0432 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0438 Docker Compose \u0441\u0435\u0440\u0432\u0438\u0441\u0430)<\/p>\n<p>  <\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u043a\u0430\u043a \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c MongoDB \u0438 Redis \u0432 Rust \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438.<\/p>\n<p>  <\/p>\n<h2 id=\"implementaciya-prilozheniya\">\u0418\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/h2>\n<p>  <\/p>\n<h3 id=\"zavisimosti\">\u0417\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438<\/h3>\n<p>  <\/p>\n<p>\u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e:<\/p>\n<p>  <\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/mongodb\/mongo-rust-driver\" rel=\"nofollow noopener noreferrer\">MongoDB Rust driver<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/mitsuhiko\/redis-rs\" rel=\"nofollow noopener noreferrer\">redis-rs<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/actix\/actix-web\" rel=\"nofollow noopener noreferrer\">Actix Web<\/a><\/li>\n<\/ul>\n<p>  <\/p>\n<p><em><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/grpc-telegram-bot\/rpc\/Cargo.toml\" rel=\"nofollow noopener noreferrer\"><code>Cargo.toml<\/code><\/a><\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">[package] name = \"mongodb-redis\" version = \"0.1.0\" edition = \"2018\"  [dependencies] mongodb = \"2.0.0-beta.1\" redis = { version = \"0.20.2\", features = [\"tokio-comp\", \"connection-manager\"] } actix-web = \"4.0.0-beta.7\" tokio = \"1.7.1\" tokio-stream = \"0.1.6\" chrono = { version = \"0.4.19\", features = [\"serde\"] } serde = \"1.0.126\" serde_json = \"1.0.64\" dotenv = \"0.15.0\" derive_more = \"0.99.14\" log = \"0.4.14\" env_logger = \"0.8.4\" rust-embed = \"5.9.0\" mime = \"0.3.16\"<\/code><\/pre>\n<p>  <\/p>\n<h3 id=\"struktura-proekta\">\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<p>  <\/p>\n<p><em><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/tree\/master\/mongodb-redis\" rel=\"nofollow noopener noreferrer\">\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a><\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">\u251c\u2500\u2500\u2500images \u2502 \u251c\u2500\u2500\u2500mongodb-init \u2502       init.json \u2502 \u2514\u2500\u2500\u2500src         db.rs         dto.rs         errors.rs         handlers.rs         index.html         main.rs         model.rs         redis.rs         services.rs<\/code><\/pre>\n<p>  <\/p>\n<h3 id=\"funkciya-main\">\u0424\u0443\u043d\u043a\u0446\u0438\u044f <code>main<\/code><\/h3>\n<p>  <\/p>\n<p><em><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/src\/main.rs\" rel=\"nofollow noopener noreferrer\"><code>\u0424\u0443\u043d\u043a\u0446\u0438\u044f main<\/code><\/a><\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"rust\">#[actix_web::main] async fn main() -> std::io::Result&lt;()> {     dotenv::from_filename(\".env.local\").ok();     env_logger::init();      info!(\"Starting MongoDB &amp; Redis demo server\");      let mongodb_uri = env::var(\"MONGODB_URI\").expect(\"MONGODB_URI env var should be specified\");     let mongodb_client = MongoDbClient::new(mongodb_uri).await;      let redis_uri = env::var(\"REDIS_URI\").expect(\"REDIS_URI env var should be specified\");     let redis_client = redis::create_client(redis_uri)         .await         .expect(\"Can't create Redis client\");     let redis_connection_manager = redis_client         .get_tokio_connection_manager()         .await         .expect(\"Can't create Redis connection manager\");      let planet_service = Arc::new(PlanetService::new(         mongodb_client,         redis_client,         redis_connection_manager.clone(),     ));     let rate_limiting_service = Arc::new(RateLimitingService::new(redis_connection_manager));      ... }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0417\u0434\u0435\u0441\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 <code>MongoDbClient<\/code>, \u043a\u043b\u0438\u0435\u043d\u0442 Redis \u0438 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 Redis.<\/p>\n<p>  <\/p>\n<h3 id=\"rabota-s-mongodb\">\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 MongoDB<\/h3>\n<p>  <\/p>\n<p>\u041d\u0430\u0447\u043d\u0451\u043c \u0441 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0449\u0435\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u043b\u0430\u043d\u0435\u0442, \u0445\u0440\u0430\u043d\u044f\u0449\u0438\u0445\u0441\u044f \u0432 \u0411\u0414, \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0435\u0439 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 API:<\/p>\n<p>  <\/p>\n<p><em><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/src\/db.rs\" rel=\"nofollow noopener noreferrer\">\u0424\u0443\u043d\u043a\u0446\u0438\u044f, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0449\u0430\u044f \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u043b\u0430\u043d\u0435\u0442<\/a><\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"rust\">const DB_NAME: &amp;str = \"solar_system_info\"; const COLLECTION_NAME: &amp;str = \"planets\";  pub async fn get_planets(     &amp;self,     planet_type: Option&lt;PlanetType>, ) -> Result&lt;Vec&lt;Planet>, CustomError> {     let filter = planet_type.map(|pt| {         doc! { \"type\": pt.to_string() }     });      let mut planets = self.get_planets_collection().find(filter, None).await?;      let mut result: Vec&lt;Planet> = Vec::new();     while let Some(planet) = planets.next().await {         result.push(planet?);     }      Ok(result) }  fn get_planets_collection(&amp;self) -> Collection&lt;Planet> {     self.client         .database(DB_NAME)         .collection::&lt;Planet>(COLLECTION_NAME) }<\/code><\/pre>\n<p>  <\/p>\n<p><code>get_planets<\/code> \u0442\u0430\u043a\u0436\u0435 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u0440 \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 MongoDB \u043f\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u043e\u043c\u0443 \u043a\u0440\u0438\u0442\u0435\u0440\u0438\u044e.<\/p>\n<p>  <\/p>\n<p>\u041c\u043e\u0434\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<p>  <\/p>\n<p><em><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/src\/model.rs\" rel=\"nofollow noopener noreferrer\">\u041c\u043e\u0434\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445<\/a><\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"rust\">#[derive(Serialize, Deserialize, Debug)] pub struct Planet {     #[serde(rename = \"_id\", skip_serializing_if = \"Option::is_none\")]     pub id: Option&lt;ObjectId>,     pub name: String,     pub r#type: PlanetType,     pub mean_radius: f32,     pub satellites: Option&lt;Vec&lt;Satellite>>, }  #[derive(Copy, Clone, Eq, PartialEq, Serialize, Deserialize, Debug)] pub enum PlanetType {     TerrestrialPlanet,     GasGiant,     IceGiant,     DwarfPlanet, }  #[derive(Serialize, Deserialize, Debug)] pub struct Satellite {     pub name: String,     pub first_spacecraft_landing_date: Option&lt;mongodb::bson::DateTime>, }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442 \u043f\u043e\u043b\u044f &#171;\u043e\u0431\u044b\u0447\u043d\u044b\u0445&#187; \u0442\u0438\u043f\u043e\u0432 (<code>string<\/code>, <code>f32<\/code>), \u0430 \u0442\u0430\u043a\u0436\u0435:<\/p>\n<p>  <\/p>\n<ul>\n<li><a href=\"https:\/\/docs.mongodb.com\/manual\/reference\/bson-types\/#objectid\" rel=\"nofollow noopener noreferrer\"><code>ObjectId<\/code><\/a> (<code>Planet.id<\/code>)<\/li>\n<li>\u0441\u043f\u0438\u0441\u043e\u043a (<code>Planet.satellites<\/code>)<\/li>\n<li>\u0434\u0430\u0442\u0430\/timestamp (<code>Satellite.first_spacecraft_landing_date<\/code>)<\/li>\n<li>\u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 (<code>Planet.type<\/code>)<\/li>\n<li>nullable \u043f\u043e\u043b\u044f (<code>Planet.id<\/code>, <code>Planet.satellites<\/code>)<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041f\u0440\u043e\u0435\u043a\u0442 \u0442\u0430\u043a\u0436\u0435 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u0440\u044b <a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/src\/db.rs\" rel=\"nofollow noopener noreferrer\">\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f, \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f, \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f<\/a> MongoDB \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432. \u042f \u043d\u0435 \u0431\u0443\u0434\u0443 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u044d\u0442\u0438\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u0445 \u0432\u0432\u0438\u0434\u0443 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u043e\u0441\u0442\u0438 \u043a\u043e\u0434\u0430 \u0438\u0445 \u0438\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438. \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f REST API:<\/p>\n<p>  <\/p>\n<ul>\n<li>\n<p>\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0433\u043e \u0441\u043f\u0438\u0441\u043a\u0430<\/p>\n<p>  <\/p>\n<p><code>GET<\/code> <code>http:\/\/localhost:9000\/planets<\/code><\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u0441 \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u0435\u0439:<\/p>\n<p>  <\/p>\n<p><code>GET<\/code> <code>http:\/\/localhost:9000\/planets?type=IceGiant<\/code><\/p>\n<p>  <\/li>\n<li>\n<p>\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435<\/p>\n<p>  <\/p>\n<p><code>POST<\/code> <code>http:\/\/localhost:9000\/planets<\/code><\/p>\n<p>  <\/p>\n<p>Body:<\/p>\n<p>  <\/p>\n<pre><code class=\"rust\">{     \"name\": \"Pluto\",     \"type\": \"DwarfPlanet\",     \"mean_radius\": 1188,     \"satellites\": null }<\/code><\/pre>\n<p>  <\/li>\n<li>\n<p>\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043f\u043e id<\/p>\n<p>  <\/p>\n<p><code>GET<\/code> <code>http:\/\/localhost:9000\/{planet_id}<\/code><\/p>\n<p>  <\/li>\n<li>\n<p>\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435<\/p>\n<p>  <\/p>\n<p><code>PUT<\/code> <code>http:\/\/localhost:9000\/{planet_id}<\/code><\/p>\n<p>  <\/p>\n<p>Body:<\/p>\n<p>  <\/p>\n<pre><code class=\"rust\">{     \"name\": \"Mercury\",     \"type\": \"TerrestrialPlanet\",     \"mean_radius\": 2439.7,     \"satellites\": null }<\/code><\/pre>\n<p>  <\/li>\n<li>\n<p>\u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435<\/p>\n<p>  <\/p>\n<p><code>DELETE<\/code> <code>http:\/\/localhost:9000\/{planet_id}<\/code><\/p>\n<p>  <\/li>\n<li>\n<p>\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043f\u043b\u0430\u043d\u0435\u0442\u044b<\/p>\n<p>  <\/p>\n<p><code>GET<\/code> <code>http:\/\/localhost:9000\/planets\/{planet_id}\/image<\/code><\/p>\n<p>  <\/p>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u044d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Redis<\/p>\n<p>  <\/li>\n<\/ul>\n<p>  <\/p>\n<p>MongoDB \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 <a href=\"https:\/\/docs.mongodb.com\/manual\/reference\/bson-types\/\" rel=\"nofollow noopener noreferrer\">BSON<\/a>.<\/p>\n<p>  <\/p>\n<h3 id=\"rabota-s-redis\">\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 Redis<\/h3>\n<p>  <\/p>\n<p>Redis \u043a\u043b\u0438\u0435\u043d\u0442 \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<p>  <\/p>\n<p><em><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/src\/redis.rs\" rel=\"nofollow noopener noreferrer\">\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 Redis \u043a\u043b\u0438\u0435\u043d\u0442\u0430<\/a><\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"rust\">pub async fn create_client(redis_uri: String) -> Result&lt;Client, RedisError> {     Ok(Client::open(redis_uri)?) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041c\u0435\u043d\u0435\u0434\u0436\u0435\u0440 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 Redis \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d \u0442\u0430\u043a:<\/p>\n<p>  <\/p>\n<p><em><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/src\/main.rs\" rel=\"nofollow noopener noreferrer\">\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u0430 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 Redis<\/a><\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"rust\">let redis_client = redis::create_client(redis_uri)     .await     .expect(\"Can't create Redis client\"); let redis_connection_manager = redis_client     .get_tokio_connection_manager()     .await     .expect(\"Can't create Redis connection manager\");<\/code><\/pre>\n<p>  <\/p>\n<h4 id=\"keshirovanie\">\u041a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h4>\n<p>  <\/p>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u044f, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0443\u044e\u0441\u044f \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043f\u043b\u0430\u043d\u0435\u0442\u044b \u043f\u043e id:<\/p>\n<p>  <\/p>\n<p><em><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/src\/services.rs\" rel=\"nofollow noopener noreferrer\">\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043f\u043b\u0430\u043d\u0435\u0442\u044b \u043f\u043e id<\/a><\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"rust\">pub async fn get_planet(&amp;self, planet_id: &amp;str) -> Result&lt;Planet, CustomError> {     let cache_key = self.get_planet_cache_key(planet_id);     let mut con = self.redis_client.get_async_connection().await?;      let cached_planet = con.get(&amp;cache_key).await?;     match cached_planet {         Value::Nil => {             debug!(\"Use database to retrieve a planet by id: {}\", &amp;planet_id);             let result: Planet = self                 .mongodb_client                 .get_planet(ObjectId::from_str(planet_id)?)                 .await?;              let _: () = redis::pipe()                 .atomic()                 .set(&amp;cache_key, &amp;result)                 .expire(&amp;cache_key, 60)                 .query_async(&amp;mut con)                 .await?;              Ok(result)         }         Value::Data(val) => {             debug!(\"Use cache to retrieve a planet by id: {}\", planet_id);             Ok(serde_json::from_slice(&amp;val)?)         }         _ => Err(RedisError {             message: \"Unexpected response from Redis\".to_string(),         }),     } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0415\u0441\u043b\u0438 \u043a\u043b\u044e\u0447\u0430 \u043d\u0435\u0442 \u0432 \u043a\u044d\u0448\u0435 (\u0432\u0435\u0442\u0432\u044c <code>Nil<\/code>), \u043f\u0430\u0440\u0430 \u043a\u043b\u044e\u0447-\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043c\u0435\u0449\u0430\u0435\u0442\u0441\u044f \u0432 Redis \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>set<\/code> (\u0441 \u0430\u0432\u0442\u043e\u044d\u043a\u0441\u043f\u0438\u0440\u0430\u0446\u0438\u0435\u0439 (\u0435\u0451 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u0434\u0430\u043b\u0435\u0435)); \u0432\u043e \u0432\u0442\u043e\u0440\u043e\u0439 \u0432\u0435\u0442\u0432\u0438 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f <code>match<\/code> \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 \u0446\u0435\u043b\u0435\u0432\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443.<\/p>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u043f\u043e\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432 \u043a\u044d\u0448 \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0438\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0440\u0435\u0439\u0442 <code>ToRedisArgs<\/code> \u0434\u043b\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b:<\/p>\n<p>  <\/p>\n<p><em><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/src\/redis.rs\" rel=\"nofollow noopener noreferrer\"><code>\u0418\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u0442\u0440\u0435\u0439\u0442\u0430 ToRedisArgs<\/code><\/a><\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"rust\">impl ToRedisArgs for &amp;Planet {     fn write_redis_args&lt;W>(&amp;self, out: &amp;mut W)     where         W: ?Sized + RedisWrite,     {         out.write_arg_fmt(serde_json::to_string(self).expect(\"Can't serialize Planet as string\"))     } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0412 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>get_planet<\/code> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 Redis. \u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u0442 \u0434\u0440\u0443\u0433\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434, <code>ConnectionManager<\/code>, \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u043a\u044d\u0448\u0430 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>del<\/code>:<\/p>\n<p>  <\/p>\n<p><em><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/src\/services.rs\" rel=\"nofollow noopener noreferrer\">\u041f\u0440\u0438\u043c\u0435\u0440 \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u043a\u044d\u0448\u0430<\/a><\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"rust\">pub async fn update_planet(     &amp;self,     planet_id: &amp;str,     planet: Planet, ) -> Result&lt;Planet, CustomError> {     let updated_planet = self         .mongodb_client         .update_planet(ObjectId::from_str(planet_id)?, planet)         .await?;      let cache_key = self.get_planet_cache_key(planet_id);     self.redis_connection_manager.clone().del(cache_key).await?;      Ok(updated_planet) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0415\u0441\u0442\u044c \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u044c, \u0447\u0442\u043e \u0447\u0442\u043e-\u0442\u043e \u043f\u043e\u0439\u0434\u0451\u0442 \u043d\u0435 \u0442\u0430\u043a \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f (\u0438\u043b\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f) \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438; \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0441 \u0441\u0435\u0442\u044c\u044e, \u0447\u0442\u043e \u043f\u0440\u0438\u0432\u0435\u0434\u0451\u0442 \u043a \u043e\u0448\u0438\u0431\u043a\u0435 \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 Redis, \u0438\u043b\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\/\u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0442\u0430\u043a, \u0447\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>del<\/code> \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u043d\u0430. \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043a \u043d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u043c \u0434\u0430\u043d\u043d\u044b\u043c \u0432 \u043a\u044d\u0448\u0435; \u043f\u043e\u0441\u043b\u0435\u0434\u0441\u0442\u0432\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0443\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u044b \u0437\u0430 \u0441\u0447\u0451\u0442 \u0430\u0432\u0442\u043e\u044d\u043a\u0441\u043f\u0438\u0440\u0430\u0446\u0438\u0438 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0437\u0430\u043f\u0438\u0441\u0435\u0439, \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0439 \u0440\u0430\u043d\u0435\u0435.<\/p>\n<p>  <\/p>\n<p><code>ConnectionManager<\/code> <a href=\"https:\/\/docs.rs\/redis\/0.20.2\/redis\/aio\/struct.ConnectionManager.html\" rel=\"nofollow noopener noreferrer\">\u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043a\u043b\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d<\/a>. \u041e\u043d \u0442\u0430\u043a\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432\u043e \u0432\u0441\u0435\u0445 \u043e\u0441\u0442\u0430\u0432\u0448\u0438\u0445\u0441\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f Redis \u0432\u043c\u0435\u0441\u0442\u043e Redis \u043a\u043b\u0438\u0435\u043d\u0442\u0430.<\/p>\n<p>  <\/p>\n<p>\u041a\u044d\u0448 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0438\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d \u0442\u0430\u043a \u0436\u0435, \u043a\u0430\u043a \u0438 \u043a\u044d\u0448\u0438 \u0434\u0440\u0443\u0433\u0438\u0445 \u0442\u0438\u043f\u043e\u0432 \u0434\u0430\u043d\u043d\u044b\u0445 (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>set<\/code>\/<code>get<\/code>):<\/p>\n<p>  <\/p>\n<p><em><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/src\/services.rs\" rel=\"nofollow noopener noreferrer\">\u041a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439<\/a><\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"rust\">pub async fn get_image_of_planet(&amp;self, planet_id: &amp;str) -> Result&lt;Vec&lt;u8>, CustomError> {     let cache_key = self.get_image_cache_key(planet_id);     let mut redis_connection_manager = self.redis_connection_manager.clone();      let cached_image = redis_connection_manager.get(&amp;cache_key).await?;     match cached_image {         Value::Nil => {             debug!(                 \"Use database to retrieve an image of a planet by id: {}\",                 &amp;planet_id             );             let planet = self                 .mongodb_client                 .get_planet(ObjectId::from_str(planet_id)?)                 .await?;             let result = crate::db::get_image_of_planet(&amp;planet.name).await;              let _: () = redis::pipe()                 .atomic()                 .set(&amp;cache_key, result.clone())                 .expire(&amp;cache_key, 60)                 .query_async(&amp;mut redis_connection_manager)                 .await?;              Ok(result)         }         Value::Data(val) => {             debug!(                 \"Use cache to retrieve an image of a planet by id: {}\",                 &amp;planet_id             );             Ok(val)         }         _ => Err(RedisError {             message: \"Unexpected response from Redis\".to_string(),         }),     } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043e \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c REST API, \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0433\u043e \u0432\u044b\u0448\u0435.<\/p>\n<p>  <\/p>\n<h4 id=\"ogranichenie-kolichestva-http-zaprosov\">\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 HTTP \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/h4>\n<p>  <\/p>\n<p>\u042d\u0442\u0430 \u0444\u0438\u0447\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 <a href=\"https:\/\/redislabs.com\/redis-best-practices\/basic-rate-limiting\/\" rel=\"nofollow noopener noreferrer\">\u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u043c \u0433\u0430\u0439\u0434\u043e\u043c<\/a> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<p>  <\/p>\n<p><em><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/src\/services.rs\" rel=\"nofollow noopener noreferrer\">\u0418\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f rate limiter<\/a><\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"rust\">#[derive(Clone)] pub struct RateLimitingService {     redis_connection_manager: ConnectionManager, }  impl RateLimitingService {     pub fn new(redis_connection_manager: ConnectionManager) -> Self {         RateLimitingService {             redis_connection_manager,         }     }      pub async fn assert_rate_limit_not_exceeded(&amp;self, ip_addr: String) -> Result&lt;(), CustomError> {         let current_minute = Utc::now().minute();         let rate_limit_key = format!(\"{}:{}:{}\", RATE_LIMIT_KEY_PREFIX, ip_addr, current_minute);          let (count,): (u64,) = redis::pipe()             .atomic()             .incr(&amp;rate_limit_key, 1)             .expire(&amp;rate_limit_key, 60)             .ignore()             .query_async(&amp;mut self.redis_connection_manager.clone())             .await?;          if count > MAX_REQUESTS_PER_MINUTE {             Err(TooManyRequests {                 actual_count: count,                 permitted_count: MAX_REQUESTS_PER_MINUTE,             })         } else {             Ok(())         }     } }<\/code><\/pre>\n<p>  <\/p>\n<p>Redis \u043a\u043b\u044e\u0447 \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f \u043d\u0430 \u043a\u0430\u0436\u0434\u0443\u044e \u043c\u0438\u043d\u0443\u0442\u0443 + IP \u0430\u0434\u0440\u0435\u0441 \u043a\u043b\u0438\u0435\u043d\u0442\u0430. \u041f\u043e\u0441\u043b\u0435 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>assert_rate_limit_not_exceeded<\/code> \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0435 \u043a\u043b\u044e\u0447\u0443, \u0438\u043d\u043a\u0440\u0435\u043c\u0435\u043d\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043d\u0430 1. \u0427\u0442\u043e\u0431\u044b \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043d\u0435 \u043f\u0435\u0440\u0435\u043f\u043e\u043b\u043d\u0438\u043b\u043e\u0441\u044c \u0438\u0437-\u0437\u0430 \u0431\u043e\u043b\u044c\u0448\u043e\u0433\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0440\u0430\u043d\u0435\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u0430\u0440 \u043a\u043b\u044e\u0447-\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u043a\u043b\u044e\u0447 &#171;\u044d\u043a\u0441\u043f\u0430\u0439\u0440\u0438\u0442\u0441\u044f&#187; \u0447\u0435\u0440\u0435\u0437 \u043c\u0438\u043d\u0443\u0442\u0443.<\/p>\n<p>  <\/p>\n<p>Rate limiter \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d \u0432 Actix \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<p>  <\/p>\n<p><em><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/src\/handlers.rs\" rel=\"nofollow noopener noreferrer\">\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 rate limiter<\/a><\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"rust\">pub async fn get_planets(     req: HttpRequest,     web::Query(query_params): web::Query&lt;GetPlanetsQueryParams>,     rate_limit_service: web::Data&lt;Arc&lt;RateLimitingService>>,     planet_service: web::Data&lt;Arc&lt;PlanetService>>, ) -> Result&lt;HttpResponse, CustomError> {     rate_limit_service         .assert_rate_limit_not_exceeded(get_ip_addr(&amp;req)?)         .await?;      let planets = planet_service.get_planets(query_params.r#type).await?;     Ok(HttpResponse::Ok().json(planets.into_iter().map(PlanetDto::from).collect::&lt;Vec&lt;_>>())) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u043f\u043b\u0430\u043d\u0435\u0442 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0447\u0430\u0441\u0442\u043e, \u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/mj\/dw\/mz\/mjdwmz1ul-2yys5rlgizhva9pxc.png\" alt=\"rate limiting\" data-src=\"https:\/\/habrastorage.org\/webt\/mj\/dw\/mz\/mjdwmz1ul-2yys5rlgizhva9pxc.png\"\/><\/p>\n<p>  <\/p>\n<h4 id=\"notifikacii\">\u041d\u043e\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438<\/h4>\n<p>  <\/p>\n<p>\u0412 \u044d\u0442\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043d\u043e\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/redis.io\/topics\/pubsub\" rel=\"nofollow noopener noreferrer\">Redis Pub\/Sub<\/a> \u0438 <a href=\"https:\/\/en.wikipedia.org\/wiki\/Server-sent_events\" rel=\"nofollow noopener noreferrer\">Server-Sent Events<\/a> \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e.<\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u0442\u0441\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u0435:<\/p>\n<p>  <\/p>\n<p><em><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/src\/services.rs\" rel=\"nofollow noopener noreferrer\">\u041f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0432 Redis<\/a><\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"rust\">pub async fn create_planet(&amp;self, planet: Planet) -> Result&lt;Planet, CustomError> {     let planet = self.mongodb_client.create_planet(planet).await?;     self.redis_connection_manager         .clone()         .publish(             NEW_PLANETS_CHANNEL_NAME,             serde_json::to_string(&amp;PlanetMessage::from(&amp;planet))?,         )         .await?;     Ok(planet) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u043e\u0434\u043f\u0438\u0441\u043a\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442\u0441\u044f \u0442\u0430\u043a:<\/p>\n<p>  <\/p>\n<p><em><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/src\/services.rs\" rel=\"nofollow noopener noreferrer\">\u041f\u0440\u0438\u043c\u0435\u0440 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438 \u0432 Redis<\/a><\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"rust\">pub async fn get_new_planets_stream(     &amp;self, ) -> Result&lt;Receiver&lt;Result&lt;Bytes, CustomError>>, CustomError> {     let (tx, rx) = mpsc::channel::&lt;Result&lt;Bytes, CustomError>>(100);      tx.send(Ok(Bytes::from(\"data: Connected\\n\\n\")))         .await         .expect(\"Can't send a message to the stream\");      let mut pubsub_con = self         .redis_client         .get_async_connection()         .await?         .into_pubsub();     pubsub_con.subscribe(NEW_PLANETS_CHANNEL_NAME).await?;      tokio::spawn(async move {         while let Some(msg) = pubsub_con.on_message().next().await {             let payload = msg.get_payload().expect(\"Can't get payload of message\");             let payload: String = FromRedisValue::from_redis_value(&amp;payload)                 .expect(\"Can't convert from Redis value\");             let msg = Bytes::from(format!(\"data: Planet created: {:?}\\n\\n\", payload));             tx.send(Ok(msg))                 .await                 .expect(\"Can't send a message to the stream\");         }     });      Ok(rx) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u043e\u0434\u043f\u0438\u0441\u043a\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 Actix \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0435 \u0442\u0430\u043a:<\/p>\n<p>  <\/p>\n<p><em><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/src\/handlers.rs\" rel=\"nofollow noopener noreferrer\">\u041f\u0440\u0438\u043c\u0435\u0440 SSE handler<\/a><\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"rust\">pub async fn sse(     planet_service: web::Data&lt;Arc&lt;PlanetService>>, ) -> Result&lt;HttpResponse, CustomError> {     let new_planets_stream = planet_service.get_new_planets_stream().await?;     let response_stream = tokio_stream::wrappers::ReceiverStream::new(new_planets_stream);      Ok(HttpResponse::build(StatusCode::OK)         .insert_header(header::ContentType(mime::TEXT_EVENT_STREAM))         .streaming(response_stream)) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u043e\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0438 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u0431\u044b\u0442\u0438\u0435. \u0414\u0430\u043b\u0435\u0435 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u044b \u0434\u0432\u0430 \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e; \u0432 \u043e\u0431\u043e\u0438\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0435 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c cURL:<\/p>\n<p>  <\/p>\n<ul>\n<li>\n<p>\u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0430 \u0438\u0437 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430<\/p>\n<p>  <\/p>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 <code>http:\/\/localhost:9000\/<\/code>, \u0433\u0434\u0435 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f <a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/src\/index.html\" rel=\"nofollow noopener noreferrer\">HTML \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430<\/a>:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/m4\/fj\/js\/m4fjjs0ts3aooolri50nsmbfyqw.gif\" alt=\"sse notifications browser\" data-src=\"https:\/\/habrastorage.org\/webt\/m4\/fj\/js\/m4fjjs0ts3aooolri50nsmbfyqw.gif\"\/><\/p>\n<p>  <\/li>\n<li>\n<p>\u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0430 \u0438\u0437 \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c cURL<\/p>\n<p>  <\/p>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 <code>curl -X GET localhost:9000\/events<\/code>:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/0g\/aj\/3w\/0gaj3wtnxppasepaxvoe9dqaybk.gif\" alt=\"sse notifications curl\" data-src=\"https:\/\/habrastorage.org\/webt\/0g\/aj\/3w\/0gaj3wtnxppasepaxvoe9dqaybk.gif\"\/><\/p>\n<p>  <\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 cURL \u0437\u0430\u043f\u0440\u043e\u0441:<\/p>\n<p>  <\/p>\n<p><em>\u0417\u0430\u043f\u0440\u043e\u0441 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u043e\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0439<\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">curl -X POST -H 'Content-Type: application\/json' -d '{     \\\"name\\\": \\\"Pluto\\\",     \\\"type\\\": \\\"DwarfPlanet\\\",     \\\"mean_radius\\\": 1188,     \\\"satellites\\\": null }' localhost:9000\/planets<\/code><\/pre>\n<p>  <\/p>\n<h3 id=\"veb-prilozhenie\">\u0412\u0435\u0431 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/h3>\n<p>  <\/p>\n<p>\u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0430\u0441\u043f\u0435\u043a\u0442\u044b \u044d\u0442\u043e\u0439 \u0442\u0435\u043c\u044b \u0431\u044b\u043b\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0432 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u044b, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0437\u0434\u0435\u0441\u044c \u0431\u0443\u0434\u0443\u0442 \u043e\u0441\u0432\u0435\u0449\u0435\u043d\u044b \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0437 \u043e\u0441\u0442\u0430\u0432\u0448\u0438\u0445\u0441\u044f \u0442\u0435\u043c.<\/p>\n<p>  <\/p>\n<h4 id=\"rest-api-obrabotchiki\">REST API \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438<\/h4>\n<p>  <\/p>\n<p>REST API \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u044b \u0442\u0430\u043a:<\/p>\n<p>  <\/p>\n<p><em><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/src\/main.rs\" rel=\"nofollow noopener noreferrer\">\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 REST API \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432<\/a><\/em><\/p>\n<p>  <\/p>\n<pre><code class=\"rust\">#[actix_web::main] async fn main() -> std::io::Result&lt;()> {     ...      let enable_write_handlers = env::var(\"ENABLE_WRITE_HANDLERS\")         .expect(\"ENABLE_WRITE_HANDLERS env var should be specified\")         .parse::&lt;bool>()         .expect(\"Can't parse ENABLE_WRITE_HANDLERS\");      HttpServer::new(move || {         let mut app = App::new()             .route(\"\/planets\", web::get().to(handlers::get_planets))             .route(\"\/planets\/{planet_id}\", web::get().to(handlers::get_planet))             .route(                 \"\/planets\/{planet_id}\/image\",                 web::get().to(handlers::get_image_of_planet),             )             .route(\"\/events\", web::get().to(handlers::sse))             .route(\"\/\", web::get().to(handlers::index))             .data(Arc::clone(&amp;planet_service))             .data(Arc::clone(&amp;rate_limiting_service));          if enable_write_handlers {             app = app                 .route(\"\/planets\", web::post().to(handlers::create_planet))                 .route(                     \"\/planets\/{planet_id}\",                     web::put().to(handlers::update_planet),                 )                 .route(                     \"\/planets\/{planet_id}\",                     web::delete().to(handlers::delete_planet),                 );         }          app     })     .bind(\"0.0.0.0:9000\")?     .run()     .await }<\/code><\/pre>\n<p>  <\/p>\n<h4 id=\"obrabotka-oshibok\">\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a<\/h4>\n<p>  <\/p>\n<p><a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/src\/errors.rs\" rel=\"nofollow noopener noreferrer\">\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a<\/a> \u0438\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0430 \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 <a href=\"https:\/\/actix.rs\/docs\/errors\/\" rel=\"nofollow noopener noreferrer\">\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439<\/a>.<\/p>\n<p>  <\/p>\n<h2 id=\"zapusk-i-testirovanie\">\u0417\u0430\u043f\u0443\u0441\u043a \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h2>\n<p>  <\/p>\n<p>\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0435\u043a\u0442 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0437\u0430\u043f\u0443\u0449\u0435\u043d \u0434\u0432\u0443\u043c\u044f \u0441\u043f\u043e\u0441\u043e\u0431\u0430\u043c\u0438:<\/p>\n<p>  <\/p>\n<ul>\n<li>\n<p>\u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Docker Compose (<a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/mongodb-redis\/docker-compose.yml\" rel=\"nofollow noopener noreferrer\"><code>docker-compose.yml<\/code><\/a>):<\/p>\n<p>  <\/p>\n<p><code>docker compose up<\/code> (\u0438\u043b\u0438 <code>docker-compose up<\/code> \u0432 \u0431\u043e\u043b\u0435\u0435 \u0441\u0442\u0430\u0440\u044b\u0445 \u0432\u0435\u0440\u0441\u0438\u044f\u0445 Docker)<\/p>\n<p>  <\/li>\n<li>\n<p>\u0431\u0435\u0437 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f Docker<\/p>\n<p>  <\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>cargo run<\/code> (\u0432 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0441\u0435\u0440\u0432\u0438\u0441 <code>mongodb-redis<\/code> \u0432 <code>docker-compose.yml<\/code> \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043e\u0442\u043a\u043b\u044e\u0447\u0451\u043d)<\/p>\n<p>  <\/li>\n<\/ul>\n<p>  <\/p>\n<h2 id=\"cicd\">CI\/CD<\/h2>\n<p>  <\/p>\n<p>CI\/CD \u0441\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e GitHub Actions <a href=\"https:\/\/github.com\/rkudryashov\/exploring-rust-ecosystem\/blob\/master\/.github\/workflows\/mongodb-redis.yml\" rel=\"nofollow noopener noreferrer\">workflow<\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442 Docker \u043e\u0431\u0440\u0430\u0437 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u0440\u0430\u0437\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442 \u0435\u0433\u043e \u043d\u0430 Google Cloud Platform.<\/p>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a REST API \u0440\u0430\u0437\u0432\u0451\u0440\u043d\u0443\u0442\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0438\u0437 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 <code>GET<\/code> \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u043e\u0432, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<p>  <\/p>\n<p><code>GET http:\/\/demo.romankudryashov.com:9000\/planets<\/code><\/p>\n<p>  <\/p>\n<p>\u041f\u0438\u0448\u0443\u0449\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u044b REST API \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u043d\u0430 production \u0441\u0440\u0435\u0434\u0435.<\/p>\n<p>  <\/p>\n<h2 id=\"zaklyuchenie\">\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n<p>  <\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u043e\u043a\u0430\u0437\u0430\u043b \u043a\u0430\u043a \u043d\u0430\u0447\u0430\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 MongoDB \u0438 Redis \u0438 \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u0438\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 Rust \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438. \u041d\u0435 \u0441\u0442\u0435\u0441\u043d\u044f\u0439\u0442\u0435\u0441\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043c\u043d\u0435, \u0435\u0441\u043b\u0438 \u043d\u0430\u0448\u043b\u0438 \u043a\u0430\u043a\u0438\u0435-\u043b\u0438\u0431\u043e \u043e\u0448\u0438\u0431\u043a\u0438 \u0432 \u0441\u0442\u0430\u0442\u044c\u0435 \u0438\u043b\u0438 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u043c \u043a\u043e\u0434\u0435. \u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435!<\/p>\n<p>  <\/p>\n<h2 id=\"poleznye-ssylki\">\u041f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 \u0441\u0441\u044b\u043b\u043a\u0438<\/h2>\n<p>  <\/p>\n<ul>\n<li><a href=\"https:\/\/developer.mongodb.com\/quickstart\/rust-crud-tutorial\/\" rel=\"nofollow noopener noreferrer\">Quick Start: Up and Running with Rust and MongoDB<\/a><\/li>\n<li><a href=\"https:\/\/redis.io\/topics\/quickstart\" rel=\"nofollow noopener noreferrer\">https:\/\/redis.io\/topics\/quickstart<\/a><\/li>\n<li><a href=\"https:\/\/redis.io\/topics\/data-types-intro\" rel=\"nofollow noopener noreferrer\">https:\/\/redis.io\/topics\/data-types-intro<\/a><\/li>\n<\/ul>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/568856\/\"> https:\/\/habr.com\/ru\/articles\/568856\/<\/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-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u043a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u0442\u044c Rust \u0431\u044d\u043a\u044d\u043d\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 <a href=\"https:\/\/www.mongodb.com\/\" rel=\"nofollow noopener noreferrer\">MongoDB<\/a>, \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e-\u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u0411\u0414, \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 <a href=\"https:\/\/redis.io\/\" rel=\"nofollow noopener noreferrer\">Redis<\/a> \u0434\u043b\u044f \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 HTTP \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0438 \u043d\u043e\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0414\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0435\u0439 \u043d\u0430\u0433\u043b\u044f\u0434\u043d\u043e\u0441\u0442\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0442\u0430\u043a\u0436\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c REST API. \u0412 \u0438\u0442\u043e\u0433\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/cf\/pi\/hi\/cfpihitbrguto9xkakvrcwae4pq.png\" alt=\"architecture\" data-src=\"https:\/\/habrastorage.org\/webt\/cf\/pi\/hi\/cfpihitbrguto9xkakvrcwae4pq.png\"\/><\/p>\n<p>  <\/p>\n<p>MongoDB \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435\u043c, \u0432 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a Redis \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u043a\u044d\u0448 (\u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f)<\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Rate_limiting\" rel=\"nofollow noopener noreferrer\">\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430<\/a> HTTP \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/li>\n<li>\u043d\u043e\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u0430 <a href=\"https:\/\/en.wikipedia.org\/wiki\/Publish%E2%80%93subscribe_pattern\" rel=\"nofollow noopener noreferrer\">publish-subscribe<\/a><\/li>\n<\/ul>\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-415884","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/415884","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=415884"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/415884\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=415884"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=415884"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=415884"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}