{"id":214425,"date":"2014-03-11T14:39:03","date_gmt":"2014-03-11T10:39:03","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=214425"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=214425","title":{"rendered":"<span class=\"post_title\">\u041f\u0438\u0448\u0435\u043c \u0432\u0435\u0431 \u0441\u0435\u0440\u0432\u0438\u0441 \u043d\u0430 Go (\u0447\u0430\u0441\u0442\u044c \u0432\u0442\u043e\u0440\u0430\u044f)<\/span>"},"content":{"rendered":"<div class=\"content html_format\">   \t\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u044c\u0438 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043f\u043e\u043b\u043d\u043e\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 Go.<\/p>\n<p>  \u0412 <a href=\"http:\/\/habrahabr.ru\/post\/208680\/\">\u043f\u0435\u0440\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438<\/a> \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 REST API \u0438 \u043d\u0430\u0443\u0447\u0438\u043b\u0438\u0441\u044c \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0449\u0438\u0435 HTTP \u0437\u0430\u043f\u0440\u043e\u0441\u044b. \u0412 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u0438, \u043c\u044b \u043f\u043e\u043a\u0440\u043e\u0435\u043c \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0442\u0435\u0441\u0442\u0430\u043c\u0438, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u0440\u0430\u0441\u0438\u0432\u044b\u0439 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 AngularJS \u0438 Bootstrap, \u0438 \u0432\u043d\u0435\u0434\u0440\u0438\u043c \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0434\u043b\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.<br \/>  <a name=\"habracut\"><\/a><\/p>\n<p>  \u0412 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043d\u0430\u0441 \u0436\u0434\u0443\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u044d\u0442\u0430\u043f\u044b:  <\/p>\n<ol>\n<li>\u0428\u0430\u0433 \u0447\u0435\u0442\u0432\u0451\u0440\u0442\u044b\u0439. \u0410 \u043a\u0430\u043a \u0436\u0435 \u0442\u0435\u0441\u0442\u044b?<\/li>\n<li>\u0428\u0430\u0433 \u043f\u044f\u0442\u044b\u0439\u2014 \u0443\u043a\u0440\u0430\u0448\u0430\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u0430 \u0438 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441;<\/li>\n<li>\u0428\u0430\u0433 \u0448\u0435\u0441\u0442\u043e\u0439. \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u043e\u0441\u0442\u0438.<\/li>\n<li>\u0428\u0430\u0433 \u0441\u0435\u0434\u044c\u043c\u043e\u0439. \u041e\u0447\u0438\u0449\u0430\u0435\u043c \u043d\u0435\u043d\u0443\u0436\u043d\u043e\u0435;<\/li>\n<li>\u0428\u0430\u0433 \u0432\u043e\u0441\u044c\u043c\u043e\u0439. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c Redis \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f.<\/li>\n<\/ol>\n<h4>\u0428\u0430\u0433 \u0447\u0435\u0442\u0432\u0451\u0440\u0442\u044b\u0439. \u0410 \u043a\u0430\u043a \u0436\u0435 \u0442\u0435\u0441\u0442\u044b?<\/h4>\n<p>  \u041b\u044e\u0431\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u043f\u043e\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u0430\u043c\u0438, \u043a\u0430\u043a\u043e\u0433\u043e \u0431\u044b \u0440\u0430\u0437\u043c\u0435\u0440\u0430 \u043e\u043d\u043e \u043d\u0438 \u0431\u044b\u043b\u043e.\u0412 Go \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0442\u0435\u0441\u0442\u0430\u043c\u0438. \u041c\u043e\u0436\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u0430\u043a \u043e\u0431\u044b\u0447\u043d\u044b\u0435 \u044e\u043d\u0438\u0442-\u0442\u0435\u0441\u0442\u044b (unit tests), \u0442\u0430\u043a \u0438, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0442\u0435\u0441\u0442\u044b \u043d\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c (benchmark tests). \u0422\u0430\u043a \u0436\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u043a\u043e\u0434\u0430 \u0442\u0435\u0441\u0442\u0430\u043c\u0438.<\/p>\n<p>  \u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043f\u0430\u043a\u0435\u0442 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0442\u0435\u0441\u0442\u0430\u043c\u0438 \u2014 \u044d\u0442\u043e <a href=\"http:\/\/golang.org\/pkg\/testing\/\">testing<\/a>. \u0414\u0432\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0442\u0438\u043f\u0430 \u0437\u0434\u0435\u0441\u044c \u2014 <code>T<\/code> \u0434\u043b\u044f \u043e\u0431\u044b\u0447\u043d\u044b\u0445 \u044e\u043d\u0438\u0442 \u0442\u0435\u0441\u0442\u043e\u0432 \u0438 <code>B<\/code> \u0434\u043b\u044f \u043d\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432. \u0422\u0435\u0441\u0442\u044b \u0432 Go \u043f\u0438\u0448\u0443\u0442\u0441\u044f \u0432 \u0442\u043e\u043c \u0436\u0435 \u043f\u0430\u043a\u0435\u0442\u0435, \u0447\u0442\u043e \u0438 \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430, \u0441 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c \u0441\u0443\u0444\u0444\u0438\u043a\u0441\u0430 <code>_test<\/code>. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043b\u044e\u0431\u044b\u0435 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0435 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0435 \u0432\u043d\u0443\u0442\u0440\u0438 \u043f\u0430\u043a\u0435\u0442\u0430, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u0438 \u0432\u043d\u0443\u0442\u0440\u0438 \u0442\u0435\u0441\u0442\u043e\u0432 (\u0442\u0430\u043a \u0436\u0435 \u0432\u0435\u0440\u043d\u043e, \u0447\u0442\u043e \u0442\u0435\u0441\u0442\u044b \u0438\u043c\u0435\u044e\u0442 \u043e\u0431\u0449\u0443\u044e \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u0443\u044e \u043e\u0431\u043b\u0430\u0441\u0442\u044c \u0432\u0438\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u0431\u043e\u0439). \u041f\u0440\u0438 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0444\u0430\u0439\u043b\u044b \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u044e\u0442\u0441\u044f.<\/p>\n<p>  \u041f\u043e\u043c\u0438\u043c\u043e \u0431\u0430\u0437\u043e\u0432\u043e\u0433\u043e \u043f\u0430\u043a\u0435\u0442\u0430 testing, \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0449\u0438\u0445 \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0442\u0435\u0441\u0442\u043e\u0432 \u043b\u0438\u0431\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u0445 \u043f\u0438\u0441\u0430\u0442\u044c \u0432 \u0442\u043e\u043c \u0438\u043b\u0438 \u0438\u043d\u043e\u043c \u0441\u0442\u0438\u043b\u0435 (\u0434\u0430\u0436\u0435 \u0432 \u0441\u0442\u0438\u043b\u0435 <a href=\"http:\/\/en.wikipedia.org\/wiki\/Behavior-driven_development\">BDD<\/a>). \u0412\u043e\u0442, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <a href=\"http:\/\/blog.stretchr.com\/2014\/03\/05\/test-driven-development-specifically-in-golang\/\">\u0445\u043e\u0440\u043e\u0448\u0430\u044f \u0432\u0432\u043e\u0434\u043d\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f<\/a> \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043f\u0438\u0441\u0430\u0442\u044c \u043d\u0430 Go \u0432 \u0441\u0442\u0438\u043b\u0435 <a href=\"http:\/\/ru.wikipedia.org\/wiki\/\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430_\u0447\u0435\u0440\u0435\u0437_\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\">TDD<\/a>.<\/p>\n<p>  \u041d\u0430 GitHub \u0435\u0441\u0442\u044c <a href=\"https:\/\/github.com\/shageman\/gotestit\">\u0442\u0430\u0431\u043b\u0438\u0447\u043a\u0430<\/a> \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, \u0441\u0440\u0435\u0434\u0438 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0435\u0441\u0442\u044c \u0442\u0430\u043a\u0438\u0435 \u043c\u043e\u043d\u0441\u0442\u0440\u044b, \u043a\u0430\u043a <a href=\"http:\/\/goconvey.co\/\">goconvey<\/a>, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0439 \u0435\u0449\u0451 \u0438 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u043e \u043f\u0440\u043e\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u0438 \u0442\u0435\u0441\u0442\u043e\u0432. \u041d\u043e, \u0434\u0430\u0431\u044b \u043d\u0435 \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u0442\u044c, \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043c\u044b \u0432\u043e\u0437\u044c\u043c\u0451\u043c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <a href=\"https:\/\/github.com\/stretchr\/testify\">testify<\/a>, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e\u0449\u0443\u044e \u043b\u0438\u0448\u044c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432\u043e\u0432 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0443\u0441\u043b\u043e\u0432\u0438\u0439 \u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f mock \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432.<\/p>\n<p>  \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u043c \u043a\u043e\u0434 \u0434\u043b\u044f \u0447\u0435\u0442\u0432\u0451\u0440\u0442\u043e\u0433\u043e \u0448\u0430\u0433\u0430:<\/p>\n<pre><code>git checkout step-4 <\/code><\/pre>\n<p>  \u041d\u0430\u0447\u043d\u0451\u043c \u0441 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0442\u0435\u0441\u0442\u043e\u0432 \u043a \u043c\u043e\u0434\u0435\u043b\u044f\u043c. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b models_test.go. \u0427\u0442\u043e\u0431\u044b \u0431\u044b\u0442\u044c \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u043c\u0438 \u0443\u0442\u0438\u043b\u0438\u0442\u043e\u0439 go test, \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0441 \u0442\u0435\u0441\u0442\u0430\u043c\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0434\u043e\u0432\u043b\u0435\u0442\u0432\u043e\u0440\u044f\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c\u0443 \u0448\u0430\u0431\u043b\u043e\u043d\u0443:<\/p>\n<pre><code>func TestXxx(*testing.T) <\/code><\/pre>\n<p>  \u041d\u0430\u043f\u0438\u0448\u0435\u043c \u043d\u0430\u0448 \u043f\u0435\u0440\u0432\u044b\u0439 \u0442\u0435\u0441\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 Bin:<\/p>\n<pre><code>func TestNewBin(t *testing.T) {      now := time.Now().Unix()      bin := NewBin()      if assert.NotNil(t, bin) {           assert.Equal(t, len(bin.Name), 6)           assert.Equal(t, bin.RequestCount, 0)           assert.Equal(t, bin.Created, bin.Updated)           assert.True(t, bin.Created &lt; (now+1))           assert.True(t, bin.Created &gt; (now-1))      } } <\/code><\/pre>\n<p>  \u0412\u0441\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0432 testify \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0442 \u043f\u0435\u0440\u0432\u044b\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c \u043e\u0431\u044a\u0435\u043a\u0442 *testing.T.<br \/>  \u0414\u0430\u043b\u0435\u0435 \u043c\u044b \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c \u0432\u0441\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438, \u043d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u044f \u043f\u0440\u043e \u043e\u0448\u0438\u0431\u043e\u0447\u043d\u044b\u0435 \u043f\u0443\u0442\u0438 \u0438 \u043f\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f. \u042f \u043d\u0435 \u0431\u0443\u0434\u0443 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u043a\u043e\u0434 \u0432\u0441\u0435\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u0432 \u0441\u0442\u0430\u0442\u044c\u0435, \u0442\u0430\u043a \u043a\u0430\u043a \u0438\u0445 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043c\u043d\u043e\u0433\u043e, \u0438 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u043d\u0438\u043c\u0438 \u0432 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438, \u0437\u0430\u0442\u0440\u043e\u043d\u0443 \u043b\u0438\u0448\u044c \u0441\u0430\u043c\u044b\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u043c\u043e\u043c\u0435\u043d\u0442\u044b.<\/p>\n<p>  \u041e\u0431\u0440\u0430\u0442\u0438\u043c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0444\u0430\u0439\u043b api_test.go, \u0432 \u043d\u0451\u043c \u043c\u044b \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c \u043d\u0430\u0448\u0435 REST API. \u0427\u0442\u043e\u0431\u044b \u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0442\u044c \u043e\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u043d\u0430\u0448\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c <a href=\"http:\/\/ru.wikipedia.org\/wiki\/Mock-\u043e\u0431\u044a\u0435\u043a\u0442\">mock \u043e\u0431\u044a\u0435\u043a\u0442<\/a>, \u0438\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0438\u0440\u0443\u044e\u0449\u0438\u0439 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 Storage. \u0414\u0435\u043b\u0430\u0435\u043c \u043c\u044b \u044d\u0442\u043e \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 <a href=\"https:\/\/github.com\/stretchr\/testify#mock-package\">mock \u043f\u0430\u043a\u0435\u0442\u0430<\/a> testify. \u041e\u043d \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0434\u043b\u044f \u043b\u0451\u0433\u043a\u043e\u0433\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f mock \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0442\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u043c\u0435\u0441\u0442\u043e \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u043f\u0440\u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<p>  \u0412\u043e\u0442 \u0435\u0433\u043e \u043a\u043e\u0434:<\/p>\n<pre><code>type MockedStorage struct{      mock.Mock }  func (s *MockedStorage) CreateBin(_ *Bin) error {      args := s.Mock.Called()      return args.Error(0) }  func (s *MockedStorage) UpdateBin(bin *Bin) error {      args := s.Mock.Called(bin)      return args.Error(0) }  func (s *MockedStorage) LookupBin(name string) (*Bin, error) {      args := s.Mock.Called(name)      return args.Get(0).(*Bin), args.Error(1) }  func (s *MockedStorage) LookupBins(names []string) ([]*Bin, error) {      args := s.Mock.Called(names)      return args.Get(0).([]*Bin), args.Error(1) }  func (s *MockedStorage) LookupRequest(binName, id string) (*Request, error) {      args := s.Mock.Called(binName, id)      return args.Get(0).(*Request), args.Error(1) }  func (s *MockedStorage) CreateRequest(bin *Bin, req *Request) error {      args := s.Mock.Called(bin)      return args.Error(0) }  func (s *MockedStorage) LookupRequests(binName string, from, to int) ([]*Request, error) {      args := s.Mock.Called(binName, from, to)      return args.Get(0).([]*Request), args.Error(1) } <\/code><\/pre>\n<p>  \u0414\u0430\u043b\u0435\u0435 \u0432 \u0441\u0430\u043c\u0438\u0445 \u0442\u0435\u0441\u0442\u0430\u0445, \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 API, \u043c\u044b \u0438\u043d\u0436\u0435\u043a\u0442\u0438\u043c \u043d\u0430\u0448 mock \u043e\u0431\u044a\u0435\u043a\u0442:<\/p>\n<pre><code>\t\treq, _ := http.NewRequest(&quot;GET&quot;, &quot;\/api\/v1\/bins\/&quot;, nil) \t\tapi = GetApi() \t\tmockedStorage := &MockedStorage{} \t\tapi.MapTo(mockedStorage, (*Storage)(nil)) \t\tres = httptest.NewRecorder() \t\tmockedStorage.On(&quot;LookupBins&quot;, []string{}).Return([]*Bin(nil), errors.New(&quot;Storage error&quot;)) \t\tapi.ServeHTTP(res, req) \t\tmockedStorage.AssertExpectations(t) \t\tif assert.Equal(t, res.Code, 500) { \t\t\tassert.Contains(t, res.Body.String(), &quot;Storage error&quot;) \t\t} <\/code><\/pre>\n<p>  \u0412 \u0442\u0435\u0441\u0442\u0435 \u043c\u044b \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a mock \u043e\u0431\u044a\u0435\u043a\u0442\u0443 \u0438 \u043d\u0443\u0436\u043d\u044b\u0435 \u043d\u0430\u043c \u043e\u0442\u0432\u0435\u0442\u044b \u043d\u0430 \u043d\u0438\u0445. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432 \u0442\u043e\u0442 \u043c\u043e\u043c\u0435\u043d\u0442, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u0432\u043d\u0443\u0442\u0440\u0438 \u043c\u0435\u0442\u043e\u0434\u0430 mock \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u043c\u0435\u0442\u043e\u0434 <code>s.Mock.Called(names)<\/code>, \u043e\u043d \u043f\u044b\u0442\u0430\u0435\u0442\u0441\u044f \u043d\u0430\u0439\u0442\u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435 \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0438 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u043c\u0435\u0442\u043e\u0434\u0430, \u0430 \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c args.Get(0) \u2014 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0432\u044b\u0439 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442, \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0439 \u0432 Return, \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 realBin. \u041f\u043e\u043c\u0438\u043c\u043e \u043c\u0435\u0442\u043e\u0434\u0430 Get, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0449\u0435\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442 \u0442\u0438\u043f\u0430 interface{}, \u0435\u0441\u0442\u044c \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b Int, String, Bool, Error, \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u044e\u0449\u0438\u0435 interface \u0432 \u043d\u0443\u0436\u043d\u044b\u0439 \u043d\u0430\u043c \u0442\u0438\u043f. \u041c\u0435\u0442\u043e\u0434 mockedStorage.AssertExpectations(t) \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0432\u0441\u0435 \u043b\u0438 \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0431\u044b\u043b\u0438 \u0432\u044b\u0437\u0432\u0430\u043d\u044b \u043d\u0430\u043c\u0438 \u043f\u0440\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438.<\/p>\n<p>  \u0415\u0449\u0451 \u0437\u0434\u0435\u0441\u044c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0435\u043d \u043e\u0431\u044a\u0435\u043a\u0442 <a href=\"http:\/\/golang.org\/pkg\/net\/http\/httptest\/#ResponseRecorder\">ResponseRecorder<\/a> \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0435\u043c\u044b\u0439 \u0432 httptest.NewRecorder, \u043e\u043d \u0438\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0438\u0440\u0443\u0435\u0442 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 ResponseWriter \u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u043c, \u043d\u0435 \u0432\u044b\u0432\u043e\u0434\u044f \u043d\u0438\u043a\u0443\u0434\u0430 \u0434\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c, \u0447\u0442\u043e \u0432 \u0438\u0442\u043e\u0433\u0435 \u0432\u0435\u0440\u043d\u0451\u0442\u0441\u044f (\u043a\u043e\u0434 \u043e\u0442\u0432\u0435\u0442\u0430, \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u0438 \u0442\u0435\u043b\u043e \u043e\u0442\u0432\u0435\u0442\u0430).<\/p>\n<p>  \u0427\u0442\u043e\u0431\u044b \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0442\u0435\u0441\u0442\u044b, \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u0443:<\/p>\n<pre><code>&gt; go test .\/src\/skimmer ok  \t_\/...\/src\/skimmer\t0.032s <\/code><\/pre>\n<p>  \u0423 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0442\u0435\u0441\u0442\u043e\u0432 \u0435\u0441\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0444\u043b\u0430\u0433\u043e\u0432, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u043d\u0438\u043c\u0438 \u043c\u043e\u0436\u043d\u043e \u0432\u043e\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code>&gt; go help testflag <\/code><\/pre>\n<p>  \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u0438\u0433\u0440\u0430\u0442\u044c\u0441\u044f \u0441 \u043d\u0438\u043c\u0438, \u043d\u043e \u0441\u0435\u0439\u0447\u0430\u0441 \u043d\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u0430 (\u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e \u0434\u043b\u044f Go \u0432\u0435\u0440\u0441\u0438\u0438 1.2):<\/p>\n<pre><code>&gt; go test .\/src\/skimmer\/ -coverprofile=c.out && go tool cover -html=c.out <\/code><\/pre>\n<p>  \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043d\u0435 \u0437\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043d\u0443\u0436\u043d\u043e \u0434\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c coverage tool<\/p>\n<pre><code>&gt; go get code.google.com\/p\/go.tools\/cmd\/cover <\/code><\/pre>\n<p>  \u042d\u0442\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0442\u0435\u0441\u0442\u044b \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u043f\u0440\u043e\u0444\u0438\u043b\u044c \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f \u0442\u0435\u0441\u0442\u0430\u043c\u0438 \u0432 \u0444\u0430\u0439\u043b c.out, \u0430 \u0437\u0430\u0442\u0435\u043c \u0443\u0442\u0438\u043b\u0438\u0442\u043e\u0439 <code>go tool<\/code> \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f html \u0432\u0435\u0440\u0441\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435.  <\/p>\n<blockquote><p>\u041f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u0442\u0435\u0441\u0442\u0430\u043c\u0438 \u0432 Go, \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e. \u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c \u043a\u0430\u043a \u0441\u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u0434, \u0438\u0437\u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b, \u0432 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u0432\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0441\u0447\u0451\u0442\u0447\u0438\u043a\u0438. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0442\u0430\u043a\u043e\u0439 \u0432\u043e\u0442 \u043a\u043e\u0434:<\/p>\n<pre><code>func Size(a int) string {     switch {     case a &lt; 0:         return &quot;negative&quot;     case a == 0:         return &quot;zero&quot;     }     return &quot;enormous&quot; } <\/code><\/pre>\n<p>  \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u0432\u043e\u0442 \u0432 \u0442\u0430\u043a\u043e\u0439:<\/p>\n<pre><code>func Size(a int) string {     GoCover.Count[0] = 1     switch {     case a &lt; 0:         GoCover.Count[2] = 1         return &quot;negative&quot;     case a == 0:         GoCover.Count[3] = 1         return &quot;zero&quot;     }     GoCover.Count[1] = 1     return &quot;enormous&quot; } <\/code><\/pre>\n<p>  \u0422\u0430\u043a \u0436\u0435 \u0435\u0441\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c, \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435, \u043d\u043e \u0438 \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437 \u043a\u0430\u0436\u0434\u044b\u0439 \u0443\u0447\u0430\u0441\u0442\u043e\u043a \u043a\u043e\u0434\u0430 \u043f\u043e\u0434\u0432\u0435\u0440\u0433\u0430\u0435\u0442\u0441\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e. \u041a\u0430\u043a \u0432\u0441\u0435\u0433\u0434\u0430, \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0432 <a href=\"http:\/\/blog.golang.org\/cover\">\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438<\/a>.<\/p><\/blockquote>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u043e\u0435 REST API, \u0434\u0430 \u0435\u0449\u0451 \u0438 \u043f\u043e\u043a\u0440\u044b\u0442\u043e\u0435 \u0442\u0435\u0441\u0442\u0430\u043c\u0438, \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0430\u0442\u044c \u043a \u0443\u043a\u0440\u0430\u0448\u0430\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u0430\u043c \u0438 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044e \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430.<\/p>\n<h4>\u0428\u0430\u0433 \u043f\u044f\u0442\u044b\u0439 \u2014 \u0443\u043a\u0440\u0430\u0448\u0430\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u0430 \u0438 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.<\/h4>\n<p>  \u0412 \u043f\u043e\u0441\u0442\u0430\u0432\u043a\u0435 Go \u0435\u0441\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 <a href=\"http:\/\/golang.org\/pkg\/html\/template\/\">html \u0448\u0430\u0431\u043b\u043e\u043d\u0430\u043c\u0438<\/a>, \u043d\u043e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u043c\u043e\u0435 \u043e\u0434\u043d\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0435\u0435 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0441 API \u0447\u0435\u0440\u0435\u0437 javascript. \u041f\u043e\u043c\u043e\u0436\u0435\u0442 \u043d\u0430\u043c \u0432 \u044d\u0442\u043e\u043c <a href=\"http:\/\/angularjs.org\/\">AngularJS<\/a>.<\/p>\n<p>  \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043a\u043e\u0434 \u0434\u043b\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u0448\u0430\u0433\u0430:<\/p>\n<pre><code>&gt; git checkout step-5 <\/code><\/pre>\n<p>  \u041a\u0430\u043a \u0431\u044b\u043b\u043e \u0443\u043f\u043e\u043c\u044f\u043d\u0443\u0442\u043e \u0435\u0449\u0451 \u0432 \u043f\u0435\u0440\u0432\u043e\u0439 \u0433\u043b\u0430\u0432\u0435, \u0432 Martini \u0435\u0441\u0442\u044c \u0445\u0435\u043d\u0434\u043b\u0435\u0440 \u0434\u043b\u044f \u0440\u0430\u0437\u0434\u0430\u0447\u0438 \u0441\u0442\u0430\u0442\u0438\u043a\u0438, \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043e\u043d \u0440\u0430\u0437\u0434\u0430\u0451\u0442 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0444\u0430\u0439\u043b\u044b \u0438\u0437 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 public. \u041f\u043e\u043b\u043e\u0436\u0438\u043c \u0442\u0443\u0434\u0430 \u043d\u0443\u0436\u043d\u044b\u0435 \u0442\u0430\u043c js \u0438 css \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. \u041e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0430 \u044f \u0431\u0443\u0434\u0443, \u0442\u0430\u043a \u043a\u0430\u043a \u044d\u0442\u043e \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0446\u0435\u043b\u044c\u044e \u043d\u0430\u0448\u0435\u0439 \u0441\u0442\u0430\u0442\u044c\u0438, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b, \u0434\u043b\u044f \u043b\u044e\u0434\u0435\u0439, \u0437\u043d\u0430\u043a\u043e\u043c\u044b\u0445 \u0441 angular, \u0442\u0430\u043c \u0432\u0441\u0451 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e.<\/p>\n<p>  \u0414\u043b\u044f \u0432\u044b\u0432\u043e\u0434\u0430 \u0433\u043b\u0430\u0432\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a:<\/p>\n<pre><code>\tapi.Get(&quot;**&quot;, func(r render.Render){ \t\t\tr.HTML(200, &quot;index&quot;, nil) \t\t}) <\/code><\/pre>\n<p>  Glob \u0441\u0438\u043c\u0432\u043e\u043b\u044b <code>**<\/code> \u0433\u043e\u0432\u043e\u0440\u044f\u0442, \u0447\u0442\u043e \u0434\u043b\u044f \u043b\u044e\u0431\u043e\u0433\u043e \u0430\u0434\u0440\u0435\u0441\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u0444\u0430\u0439\u043b index.html. \u0414\u043b\u044f \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0448\u0430\u0431\u043b\u043e\u043d\u0430\u043c\u0438 \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 Renderer \u043e\u043f\u0446\u0438\u0438, \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0449\u0438\u0435 \u043e\u0442\u043a\u0443\u0434\u0430 \u0431\u0440\u0430\u0442\u044c \u0448\u0430\u0431\u043b\u043e\u043d\u044b. \u041f\u043b\u044e\u0441, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0431\u044b\u043b\u043e \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043e\u0432 \u0441 angular \u0448\u0430\u0431\u043b\u043e\u043d\u0430\u043c\u0438, \u043f\u0435\u0440\u0435\u043d\u0430\u0437\u043d\u0430\u0447\u0438\u043b\u0438 {{ }} \u043d\u0430 {[{ }]}.<\/p>\n<pre><code>\tapi.Use(render.Renderer(render.Options{ \t\tDirectory: &quot;public\/static\/views&quot;, \t\tExtensions: []string{&quot;.html&quot;}, \t\tDelims: render.Delims{&quot;{[{&quot;, &quot;}]}&quot;}, \t})) <\/code><\/pre>\n<p>  \u041f\u043e\u043c\u0438\u043c\u043e \u044d\u0442\u043e\u0433\u043e, \u0432 \u043c\u043e\u0434\u0435\u043b\u044c Bin \u0431\u044b\u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b \u043f\u043e\u043b\u044f \u0421olor (\u0442\u0440\u0438 \u0431\u0430\u0439\u0442\u0430, \u0445\u0440\u0430\u043d\u044f\u0449\u0438\u0435 RGB \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0446\u0432\u0435\u0442\u0430) \u0438 Favicon (data uri \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430, \u043d\u0443\u0436\u043d\u043e \u0446\u0432\u0435\u0442\u0430), \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c\u044b\u0435 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430, \u0447\u0442\u043e\u0431\u044b \u0440\u0430\u0437\u043b\u0438\u0447\u0430\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u0435 bin \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u043f\u043e \u0446\u0432\u0435\u0442\u0430\u043c.<\/p>\n<pre><code>type Bin struct { ... \tColor\t\t [3]byte `json:&quot;color&quot;` \tFavicon      string  `json:&quot;favicon&quot;` }  func NewBin() *Bin { \tcolor:= RandomColor() \tbin := Bin{ ... \t\tColor:\t\t  color, \t\tFavicon:      Solid16x16gifDatauri(color), \t} ... } <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0443 \u043d\u0430\u0441 \u043f\u043e\u0447\u0442\u0438 \u043f\u043e\u043b\u043d\u043e\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043c\u043e\u0436\u043d\u043e \u0435\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c:<\/p>\n<pre><code>&gt; go run .\/src\/main.go <\/code><\/pre>\n<p>  \u0418 \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 (<code>http:\/\/127.0.0.1:3000<\/code>), \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0438\u0433\u0440\u0430\u0442\u044c\u0441\u044f.<\/p>\n<p>  \u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u043f\u043e\u043a\u0430 \u0435\u0449\u0451 \u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0442 \u0434\u0432\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b: \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b \u0432\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0442\u0435\u0440\u044f\u044e\u0442\u0441\u044f \u0438 \u0443 \u043d\u0430\u0441 \u043d\u0435\u0442 \u043d\u0438\u043a\u0430\u043a\u043e\u0433\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043f\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c, \u0432\u0441\u0435 \u0432\u0438\u0434\u044f\u0442 \u043e\u0434\u043d\u043e \u0438 \u0442\u043e\u0436\u0435. \u0427\u0442\u043e\u0436, \u0437\u0430\u0439\u043c\u0451\u043c\u0441\u044f \u044d\u0442\u0438\u043c.<\/p>\n<h5>\u0428\u0430\u0433 \u0448\u0435\u0441\u0442\u043e\u0439. \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u043e\u0441\u0442\u0438.<\/h5>\n<p>  \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u043c \u043a\u043e\u0434 \u0434\u043b\u044f \u0448\u0435\u0441\u0442\u043e\u0433\u043e \u0448\u0430\u0433\u0430:<\/p>\n<pre><code>&gt; git checkout step-6 <\/code><\/pre>\n<p>  \u041e\u0442\u0434\u0435\u043b\u044f\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0434\u0440\u0443\u0433 \u043e\u0442 \u0434\u0440\u0443\u0433\u0430 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u0441\u0435\u0441\u0441\u0438\u0439. \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0432\u044b\u0431\u0435\u0440\u0435\u043c \u0433\u0434\u0435 \u0438\u0445 \u0445\u0440\u0430\u043d\u0438\u0442\u044c. \u0421\u0435\u0441\u0441\u0438\u0438 \u0432 <a href=\"https:\/\/github.com\/martini-contrib\/sessions\">martini-contrib<\/a> \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u044b \u043d\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u0435\u0441\u0441\u0438\u0439 web \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <a href=\"http:\/\/www.gorillatoolkit.org\/\">gorilla<\/a>.  <\/p>\n<blockquote><p>Gorilla \u2014 \u044d\u0442\u043e \u043d\u0430\u0431\u043e\u0440 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u0435\u0431-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432. \u0412\u0441\u0435 \u044d\u0442\u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0441\u043b\u0430\u0431\u043e \u0441\u0432\u044f\u0437\u0430\u043d\u044b \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u0431\u043e\u0439, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0431\u0440\u0430\u0442\u044c \u043b\u044e\u0431\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u0438 \u0432\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u043a \u0441\u0435\u0431\u0435.<\/p><\/blockquote>\n<p>  \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0443\u0436\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0432 gorilla \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430. \u041d\u0430\u0448\u0435 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 cookie.<\/p>\n<p>  \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0441\u0435\u0441\u0441\u0438\u0438:<\/p>\n<pre><code>func GetApi(config *Config) *martini.ClassicMartini { ... \tstore := sessions.NewCookieStore([]byte(config.SessionSecret)) ... <\/code><\/pre>\n<p>  <\/p>\n<blockquote><p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f NewCookieStore \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043f\u0430\u0440\u044b \u043a\u043b\u044e\u0447\u0435\u0439, \u043f\u0435\u0440\u0432\u044b\u0439 \u043a\u043b\u044e\u0447 \u0432 \u043f\u0430\u0440\u0435 \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u0430 \u0432\u0442\u043e\u0440\u043e\u0439 \u0434\u043b\u044f \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u0412\u0442\u043e\u0440\u043e\u0439 \u043a\u043b\u044e\u0447 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0442\u044c. \u0427\u0442\u043e\u0431\u044b \u0438\u043c\u0435\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0440\u043e\u0442\u0430\u0446\u0438\u0438 \u043a\u043b\u044e\u0447\u0435\u0439 \u0431\u0435\u0437 \u043f\u043e\u0442\u0435\u0440\u0438 \u0441\u0435\u0441\u0441\u0438\u0439, \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0430\u0440 \u043a\u043b\u044e\u0447\u0435\u0439. \u041f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u0441\u0435\u0441\u0441\u0438\u0438 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043a\u043b\u044e\u0447\u0438 \u043f\u0435\u0440\u0432\u043e\u0439 \u043f\u0430\u0440\u044b, \u043d\u043e \u043f\u0440\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0437\u0430\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0442\u0441\u044f \u0432\u0441\u0435 \u043a\u043b\u044e\u0447\u0438 \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443, \u043d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 \u043f\u0435\u0440\u0432\u043e\u0439 \u043f\u0430\u0440\u044b.<\/p><\/blockquote>\n<p>  \u0422\u0430\u043a \u043a\u0430\u043a \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u044b \u0440\u0430\u0437\u043d\u044b\u0435 \u043a\u043b\u044e\u0447\u0438 \u0434\u043b\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439, \u0432\u044b\u043d\u0435\u0441\u0435\u043c \u044d\u0442\u043e\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0432 \u043e\u0431\u044a\u0435\u043a\u0442 Config, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u043d\u0430\u043c \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u0444\u043b\u0430\u0433\u043e\u0432 \u0437\u0430\u043f\u0443\u0441\u043a\u0430.<\/p>\n<p>  \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u043d\u0430\u0448\u0435 API \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0439 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0441\u0435\u0441\u0441\u0438\u044f\u043c\u0438:<\/p>\n<pre><code>\/\/ Sessions is a Middleware that maps a session.Session service into the Martini handler chain. \/\/ Sessions can use a number of storage solutions with the given store. func Sessions(name string, store Store) martini.Handler { \treturn func(res http.ResponseWriter, r *http.Request, c martini.Context, l *log.Logger) { \t\t\/\/ Map to the Session interface \t\ts := &session{name, r, l, store, nil, false} \t\tc.MapTo(s, (*Session)(nil)) \t\t\/\/ Use before hook to save out the session \t\trw := res.(martini.ResponseWriter) \t\trw.Before(func(martini.ResponseWriter) { \t\t\tif s.Written() { \t\t\t\tcheck(s.Session().Save(r, res), l) \t\t\t} \t\t}) ...  \t\tc.Next() \t} } <\/code><\/pre>\n<p>  \u041a\u0430\u043a \u0432\u0438\u0434\u043d\u043e \u0438\u0437 \u043a\u043e\u0434\u0430, \u0441\u0435\u0441\u0441\u0438\u044f \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u041f\u043e \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043f\u0440\u044f\u043c\u043e \u043f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c, \u043a\u0430\u043a \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u044b \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0431\u0443\u0444\u0435\u0440\u0430, \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u0435\u0441\u0441\u0438\u0438, \u0435\u0441\u043b\u0438 \u043e\u043d\u0438 \u0431\u044b\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u044b.<\/p>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0435\u0440\u0435\u043f\u0438\u0448\u0435\u043c \u043d\u0430\u0448\u0443 \u0438\u0441\u0442\u043e\u0440\u0438\u044e (\u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0440\u0430\u043d\u044c\u0448\u0435 \u0431\u044b\u043b\u0430 \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u043b\u0430\u0439\u0441\u043e\u043c), \u0444\u0430\u0439\u043b history.go:<\/p>\n<pre><code>type History interface { \tAll() []string \tAdd(string) }  type SessionHistory struct { \tsize    int \tname    string \tsession sessions.Session \tdata    []string }  func (history *SessionHistory) All() []string { \tif history.data == nil { \t\thistory.load() \t} \treturn history.data }  func (history *SessionHistory) Add(name string) { \tif history.data == nil { \t\thistory.load() \t} \thistory.data = append(history.data, &quot;&quot;) \tcopy(history.data[1:], history.data) \thistory.data[0] = name \thistory.save() }  func (history *SessionHistory) save() { \tsize := history.size \tif size &gt; len(history.data){ \t\tsize = len(history.data) \t} \thistory.session.Set(history.name, history.data[:size]) }  func (history *SessionHistory) load() { \tsessionValue := history.session.Get(history.name) \thistory.data = []string{} \tif sessionValue != nil { \t\tif values, ok := sessionValue.([]string); ok { \t\t\thistory.data = append(history.data, values...) \t\t} \t}  }  func NewSessionHistoryHandler(size int, name string) martini.Handler { \treturn func(c martini.Context, session sessions.Session) { \t\thistory := &SessionHistory{size: size, name: name, session: session} \t\tc.MapTo(history, (*History)(nil)) \t} } <\/code><\/pre>\n<p>  \u0412 \u043c\u0435\u0442\u043e\u0434\u0435 NewSessionHistoryHandler \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043e\u0431\u044a\u0435\u043a\u0442 SessionHistory, \u0438\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0438\u0440\u0443\u044e\u0449\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 History (\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0449\u0438\u0439 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0438 \u0437\u0430\u043f\u0440\u043e\u0441 \u0432\u0441\u0435\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0438\u0441\u0442\u043e\u0440\u0438\u0438), \u0438 \u0437\u0430\u0442\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0435\u0433\u043e \u0432 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0423 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 SessionHistory \u0435\u0441\u0442\u044c \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b load \u0438 save, \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0449\u0438\u0435 \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044e\u0449\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0441\u0435\u0441\u0441\u0438\u044e. \u041f\u0440\u0438\u0447\u0451\u043c \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u0441\u0435\u0441\u0441\u0438\u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044e. \u0422\u0435\u043f\u0435\u0440\u044c \u0432\u043e \u0432\u0441\u0435\u0445 \u043c\u0435\u0442\u043e\u0434\u0430\u0445 API, \u0433\u0434\u0435 \u0440\u0430\u043d\u044c\u0448\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f \u0441\u043b\u0430\u0439\u0441 history \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u043e\u0432\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0442\u0438\u043f\u0430 History.<\/p>\n<p>  \u0421 \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u043c\u0435\u043d\u0442\u0430 \u0443 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u0441\u0432\u043e\u044f \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f Bin \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432, \u043d\u043e \u043f\u043e \u043f\u0440\u044f\u043c\u043e\u0439 \u0441\u0441\u044b\u043b\u043a\u0435 \u043c\u044b \u0432\u0441\u0451 \u0442\u0430\u043a \u0436\u0435 \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043b\u044e\u0431\u043e\u0439 Bin. \u0418\u0441\u043f\u0440\u0430\u0432\u0438\u043c \u044d\u0442\u043e, \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0435 Bin \u043e\u0431\u044a\u0435\u043a\u0442\u044b.<\/p>\n<p>  \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0432 Bin \u0434\u0432\u0430 \u043d\u043e\u0432\u044b\u0445 \u043f\u043e\u043b\u044f:<\/p>\n<pre><code>type Bin struct { ... \tPrivate      bool    `json:&quot;private&quot;` \tSecretKey    string  `json:&quot;-&quot;` } <\/code><\/pre>\n<p>  \u0412 \u043f\u043e\u043b\u0435 SecretKey \u0431\u0443\u0434\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u043a\u043b\u044e\u0447, \u0434\u0430\u044e\u0449\u0438\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u043c Bin (\u0442\u0435\u043c, \u0433\u0434\u0435 \u0444\u043b\u0430\u0433 Private \u043f\u0440\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u0432 true). \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0430\u043a \u0436\u0435 \u043c\u0435\u0442\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u0435\u043b\u0430\u0435\u0442 \u043d\u0430\u0448 \u043e\u0431\u044a\u0435\u043a\u0442 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u043c:<\/p>\n<pre><code>func (bin *Bin) SetPrivate() { \tbin.Private = true \tbin.SecretKey = rs.Generate(32) } <\/code><\/pre>\n<p>  \u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0435 Bin, \u043d\u0430\u0448 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434, \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430, \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u0441\u044b\u043b\u0430\u0442\u044c json \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u0444\u043b\u0430\u0433\u043e\u043c private. \u0427\u0442\u043e\u0431\u044b \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u044c \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0449\u0438\u0435 json, \u043c\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043c\u0435\u0442\u043e\u0434 DecodeJsonPayload, \u0447\u0438\u0442\u0430\u044e\u0449\u0438\u0439 \u0442\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438 \u0440\u0430\u0441\u043f\u0430\u043a\u043e\u0432\u044b\u0432\u0430\u044e\u0449\u0438\u0439 \u0435\u0433\u043e \u0432 \u043d\u0443\u0436\u043d\u0443\u044e \u043d\u0430\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443:<\/p>\n<pre><code>func DecodeJsonPayload(r *http.Request, v interface{}) error { \tcontent, err := ioutil.ReadAll(r.Body) \tr.Body.Close() \tif err != nil { \t\treturn err \t} \terr = json.Unmarshal(content, v) \tif err != nil { \t\treturn err \t} \treturn nil } <\/code><\/pre>\n<p>  \u0418\u0437\u043c\u0435\u043d\u0438\u043c \u0442\u0435\u043f\u0435\u0440\u044c API, \u0447\u0442\u043e\u0431\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u043e\u0432\u043e\u0435 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435:<\/p>\n<pre><code>\tapi.Post(&quot;\/api\/v1\/bins\/&quot;, func(r render.Render, storage Storage, history History, session sessions.Session, req *http.Request){ \t\t\tpayload := Bin{} \t\t\tif err := DecodeJsonPayload(req, &payload); err != nil { \t\t\t\tr.JSON(400, ErrorMsg{fmt.Sprintf(&quot;Decoding payload error: %s&quot;, err)}) \t\t\t\treturn \t\t\t} \t\t\tbin := NewBin() \t\t\tif payload.Private { \t\t\t\tbin.SetPrivate() \t\t\t} \t\t\tif err := storage.CreateBin(bin); err == nil { \t\t\t\thistory.Add(bin.Name) \t\t\t\tif bin.Private { \t\t\t\t\tsession.Set(fmt.Sprintf(&quot;pr_%s&quot;, bin.Name), bin.SecretKey) \t\t\t\t} \t\t\t\tr.JSON(http.StatusCreated, bin) \t\t\t} else { \t\t\t\tr.JSON(http.StatusInternalServerError, ErrorMsg{err.Error()}) \t\t\t} \t\t}) <\/code><\/pre>\n<p>  \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043e\u0431\u044a\u0435\u043a\u0442 payload \u0442\u0438\u043f\u0430 Bin, \u043f\u043e\u043b\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 DecodeJsonPayload \u0438\u0437 \u0442\u0435\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e, \u0435\u0441\u043b\u0438 \u0432\u043e \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0430 \u043e\u043f\u0446\u0438\u044f \u00abprivate\u00bb, \u043c\u044b \u0434\u0435\u043b\u0430\u0435\u043c \u043d\u0430\u0448 bin \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u043c. \u0414\u0430\u043b\u0435\u0435, \u0434\u043b\u044f \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u043c\u044b \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u043b\u044e\u0447\u0430 \u0432 \u0441\u0435\u0441\u0441\u0438\u044e <code>session.Set(fmt.Sprintf(&quot;pr_%s&quot;, bin.Name), bin.SecretKey)<\/code>. \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0443\u0436\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0434\u0440\u0443\u0433\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u044b API \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u043b\u0438 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043b\u044e\u0447\u0430 \u0432 \u0441\u0435\u0441\u0441\u0438\u0438 \u0434\u043b\u044f \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u0445 Bin \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432. <\/p>\n<p>  \u0414\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u044d\u0442\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0432\u043e\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code>\tapi.Get(&quot;\/api\/v1\/bins\/:bin&quot;, func(r render.Render, params martini.Params, session sessions.Session, storage Storage){ \t\t\tif bin, err := storage.LookupBin(params[&quot;bin&quot;]); err == nil{ \t\t\t\tif bin.Private && bin.SecretKey != session.Get(fmt.Sprintf(&quot;pr_%s&quot;, bin.Name)){ \t\t\t\t\tr.JSON(http.StatusForbidden, ErrorMsg{&quot;The bin is private&quot;}) \t\t\t\t} else { \t\t\t\t\tr.JSON(http.StatusOK, bin) \t\t\t\t} \t\t\t} else { \t\t\t\tr.JSON(http.StatusNotFound, ErrorMsg{err.Error()}) \t\t\t} \t\t}) <\/code><\/pre>\n<p>  \u041f\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0438 \u0441\u0434\u0435\u043b\u0430\u043d\u043e \u0438 \u0432 \u0434\u0440\u0443\u0433\u0438\u0445 \u043c\u0435\u0442\u043e\u0434\u0430\u0445. \u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u0442\u0430\u043a \u0436\u0435 \u0431\u044b\u043b\u0438 \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u044b, \u0447\u0442\u043e\u0431\u044b \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u043e\u0432\u043e\u0435 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435, \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432 \u043a\u043e\u0434\u0435.<\/p>\n<p>  \u0415\u0441\u043b\u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u0439\u0447\u0430\u0441 \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432 \u0440\u0430\u0437\u043d\u044b\u0445 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430\u0445 \u0438\u043b\u0438 \u0432 \u0438\u043d\u043a\u043e\u0433\u043d\u0438\u0442\u043e \u0440\u0435\u0436\u0438\u043c\u0435, \u043c\u043e\u0436\u043d\u043e \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0438\u0441\u0442\u043e\u0440\u0438\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f, \u0430 \u043a \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u044b\u043c Bin \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u043c \u0434\u043e\u0441\u0442\u0443\u043f \u0438\u043c\u0435\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u043e\u0442 \u0431\u0440\u0430\u0443\u0437\u0435\u0440, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043e\u043d \u0441\u043e\u0437\u0434\u0430\u043d.<\/p>\n<p>  \u0412\u0441\u0451 \u0445\u043e\u0440\u043e\u0448\u043e, \u043d\u043e \u0441\u0435\u0439\u0447\u0430\u0441 \u0432\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0432 \u043d\u0430\u0448\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0436\u0438\u0432\u0443\u0442 \u043f\u043e\u0447\u0442\u0438 \u0432\u0435\u0447\u043d\u043e, \u0447\u0442\u043e \u043d\u0430\u0432\u0435\u0440\u043d\u043e\u0435 \u043d\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e, \u0442\u0430\u043a \u043a\u0430\u043a \u043f\u0430\u043c\u044f\u0442\u044c \u0432\u0435\u0447\u043d\u043e\u0439 \u0431\u044b\u0442\u044c \u043d\u0435 \u043c\u043e\u0436\u0435\u0442, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0442\u044c \u0432\u0440\u0435\u043c\u044f \u0438\u0445 \u0436\u0438\u0437\u043d\u0438.<\/p>\n<h4>\u0428\u0430\u0433 \u0441\u0435\u0434\u044c\u043c\u043e\u0439. \u041e\u0447\u0438\u0449\u0430\u0435\u043c \u043d\u0435\u043d\u0443\u0436\u043d\u043e\u0435.<\/h4>\n<p>  \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u043c \u043a\u043e\u0434 \u0441\u0435\u0434\u044c\u043c\u043e\u0433\u043e \u0448\u0430\u0433\u0430:<\/p>\n<pre><code>git checkout step-7 <\/code><\/pre>\n<p>  \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0431\u0430\u0437\u043e\u0432\u043e\u0433\u043e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0435\u0449\u0451 \u043e\u0434\u043d\u043e \u043f\u043e\u043b\u0435:<\/p>\n<pre><code>type BaseStorage struct { ... \tbinLifetime\t\t  int64 } <\/code><\/pre>\n<p>  \u0412 \u043d\u0451\u043c \u0431\u0443\u0434\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0436\u0438\u0437\u043d\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 Bin \u0438 \u0441\u043e\u043f\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0435\u043c\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432. \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0435\u0440\u0435\u043f\u0438\u0448\u0435\u043c \u043d\u0430\u0448\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u2014 memory.go. \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u0432\u0441\u0435\u0445 binRecords \u043d\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0432\u0448\u0438\u0445\u0441\u044f \u0431\u043e\u043b\u044c\u0448\u0435 \u0447\u0435\u043c binLifetime \u0441\u0435\u043a\u0443\u043d\u0434:<\/p>\n<pre><code>func (storage *MemoryStorage) clean() { \tstorage.Lock() \tdefer storage.Unlock() \tnow := time.Now().Unix() \tfor name, binRecord := range storage.binRecords { \t\tif binRecord.bin.Updated &lt; (now - storage.binLifetime) { \t\t\tdelete(storage.binRecords, name) \t\t} \t} } <\/code><\/pre>\n<p>  \u0422\u0430\u043a \u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u0442\u0438\u043f MemoryStorage \u0442\u0430\u0439\u043c\u0435\u0440 \u0438 \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043d\u0438\u043c:<\/p>\n<pre><code>type MemoryStorage struct { ... \tcleanTimer *time.Timer }  func (storage *MemoryStorage) StartCleaning(timeout int) { \tdefer func(){ \t\tstorage.cleanTimer = time.AfterFunc(time.Duration(timeout) * time.Second, func(){storage.StartCleaning(timeout)}) \t}() \tstorage.clean() }  func (storage *MemoryStorage) StopCleaning() { \tif storage.cleanTimer != nil { \t\tstorage.cleanTimer.Stop() \t} } <\/code><\/pre>\n<p>  \u041c\u0435\u0442\u043e\u0434 \u043f\u0430\u043a\u0435\u0442\u0430 time AfterFunc \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u0433\u043e\u0440\u0443\u0442\u0438\u043d\u0435 \u0437\u0430\u0434\u0430\u043d\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e (\u043e\u043d\u0430 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0431\u0435\u0437 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u0437\u0434\u0435\u0441\u044c \u0437\u0430\u043c\u044b\u043a\u0430\u043d\u0438\u0435\u043c \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 timeout) \u043f\u043e\u0441\u043b\u0435 \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u0430, \u0442\u0438\u043f\u0430 time.Duration, \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0432 \u043f\u0435\u0440\u0432\u043e\u043c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0435.<\/p>\n<p>  \u0414\u043b\u044f \u0433\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0435\u0433\u043e \u043d\u0430 \u0440\u0430\u0437\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u0430\u0445, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445. \u0412\u043e\u0437\u044c\u043c\u0451\u043c \u0434\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u2014 Redis.<\/p>\n<h4>\u0428\u0430\u0433 \u0432\u043e\u0441\u044c\u043c\u043e\u0439. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c Redis \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f.<\/h4>\n<p>  \u041e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u043f\u043e Redis<a href=\"http:\/\/redis.io\/clients\"> \u0441\u043e\u0432\u0435\u0442\u0443\u0435\u0442<\/a> \u043d\u0430\u043c \u043e\u0431\u0448\u0438\u0440\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f Go. \u041d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u044c\u0438, \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u043c\u044b\u043c\u0438 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f <a href=\"https:\/\/github.com\/fzzy\/radix\">radix<\/a> \u0438 <a href=\"https:\/\/github.com\/garyburd\/redigo\">redigo<\/a>. \u041c\u044b \u0432\u044b\u0431\u0435\u0440\u0435\u043c redigo, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d \u0430\u043a\u0442\u0438\u0432\u043d\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0438 \u0438\u043c\u0435\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u0435\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e.<\/p>\n<p>  \u041f\u0435\u0440\u0435\u0439\u0434\u0451\u043c \u043a \u043d\u0443\u0436\u043d\u043e\u043c\u0443 \u043a\u043e\u0434\u0443:<\/p>\n<pre><code>git checkout step-8 <\/code><\/pre>\n<p>  \u0417\u0430\u0433\u043b\u044f\u043d\u0435\u043c \u0432 \u0444\u0430\u0439\u043b\u0435 redis.go, \u0432 \u043d\u0451\u043c \u0438 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0448\u0430 \u0438\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 Storage \u0434\u043b\u044f Redis. \u0411\u0430\u0437\u043e\u0432\u0430\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u0430:<\/p>\n<pre><code>type RedisStorage struct { \tBaseStorage \tpool       *redis.Pool \tprefix     string \tcleanTimer *time.Timer } <\/code><\/pre>\n<p>  \u0412 pool \u0431\u0443\u0434\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u043f\u0443\u043b \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 \u043a \u0440\u0435\u0434\u0438\u0441\u0443, \u0432 prefix \u2014 \u043e\u0431\u0449\u0438\u0439 \u043f\u0440\u0435\u0444\u0438\u043a\u0441 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043a\u043b\u044e\u0447\u0435\u0439. \u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0443\u043b\u0430 \u0432\u043e\u0437\u044c\u043c\u0451\u043c \u043a\u043e\u0434 \u0438\u0437 \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432 redigo:<\/p>\n<pre><code>func getPool(server string, password string) (pool *redis.Pool) { \tpool = &redis.Pool{ \t\tMaxIdle:     3, \t\tIdleTimeout: 240 * time.Second, \t\tDial: func() (redis.Conn, error) { \t\t\tc, err := redis.Dial(&quot;tcp&quot;, server) \t\t\tif err != nil { \t\t\t\treturn nil, err \t\t\t} \t\t\tif password != &quot;&quot; { \t\t\t\tif _, err := c.Do(&quot;AUTH&quot;, password); err != nil { \t\t\t\t\tc.Close() \t\t\t\t\treturn nil, err \t\t\t\t} \t\t\t} \t\t\treturn c, err \t\t}, \t\tTestOnBorrow: func(c redis.Conn, _ time.Time) error { \t\t\t_, err := c.Do(&quot;PING&quot;) \t\t\treturn err \t\t}, \t} \treturn pool } <\/code><\/pre>\n<p>  \u0412 Dial \u043c\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u0441\u043b\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c Redis, \u043f\u043e\u043f\u044b\u0442\u0430\u0435\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f, \u0435\u0441\u043b\u0438 \u0443\u043a\u0430\u0437\u0430\u043d \u043f\u0430\u0440\u043e\u043b\u044c. \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u043e\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435. \u0424\u0443\u043d\u043a\u0446\u0438\u044f TestOnBorrow \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0438\u0437 \u043f\u0443\u043b\u0430, \u0432 \u043d\u0435\u0439 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043d\u0430 \u0436\u0438\u0437\u043d\u0435\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c. \u0412\u0442\u043e\u0440\u043e\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440, \u044d\u0442\u043e \u0432\u0440\u0435\u043c\u044f \u0441 \u043c\u043e\u043c\u0435\u043d\u0442\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0442\u0430 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0432 \u043f\u0443\u043b. \u041c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u0438\u043d\u0433 \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437.<\/p>\n<p>  \u0422\u0430\u043a \u0436\u0435 \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 \u0443 \u043d\u0430\u0441 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442:<\/p>\n<pre><code>const ( \tKEY_SEPARATOR    = &quot;|&quot; \/\/ \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c \u043a\u043b\u044e\u0447\u0435\u0439 \tBIN_KEY          = &quot;bins&quot; \/\/ \u043a\u043b\u044e\u0447 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 Bin \tREQUESTS_KEY     = &quot;rq&quot;  \/\/ \u043a\u043b\u044e\u0447 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \tREQUEST_HASH_KEY = &quot;rhsh&quot; \/\/ \u043a\u043b\u044e\u0447 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u0445\u044d\u0448 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 \tCLEANING_SET\t = &quot;cln&quot; \/\/ \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0443\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u044b Bin \u0434\u043b\u044f \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \tCLEANING_FACTOR  = 3 \/\/ \u043c\u043d\u043e\u0436\u0438\u0442\u0435\u043b\u044c \u043f\u0440\u0435\u0432\u044b\u0448\u0435\u043d\u0438\u044f \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 ) <\/code><\/pre>\n<p>  \u041a\u043b\u044e\u0447\u0438 \u0443 \u043d\u0430\u0441 \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442\u0441\u044f \u0432\u043e\u0442 \u043f\u043e \u0442\u0430\u043a\u043e\u043c\u0443 \u0448\u0430\u0431\u043b\u043e\u043d\u0443:<\/p>\n<pre><code>func (storage *RedisStorage) getKey(keys ...string) string { \treturn fmt.Sprintf(&quot;%s%s%s&quot;, storage.prefix, KEY_SEPARATOR, strings.Join(keys, KEY_SEPARATOR)) } <\/code><\/pre>\n<p>  \u0427\u0442\u043e\u0431\u044b \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043d\u0430\u0448\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0440\u0435\u0434\u0438\u0441\u0435, \u0438\u0445 \u043d\u0443\u0436\u043d\u043e \u0447\u0435\u043c \u0442\u043e \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c. \u041c\u044b \u0432\u044b\u0431\u0435\u0440\u0435\u043c \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 <a href=\"http:\/\/msgpack.org\/\">msgpack<\/a> \u0438 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u043e\u0439 <a href=\"https:\/\/github.com\/ugorji\/go\">codec<\/a>. <\/p>\n<p>  \u041e\u043f\u0438\u0448\u0435\u043c \u043c\u0435\u0442\u043e\u0434\u044b, \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0443\u044e\u0449\u0438\u0435 \u0432\u0441\u0451 \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0432 \u0431\u0438\u043d\u0430\u0440\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u043e\u0431\u0440\u0430\u0442\u043d\u043e:<\/p>\n<pre><code>func (storage *RedisStorage) Dump(v interface{}) (data []byte, err error) { \tvar ( \t\tmh codec.MsgpackHandle \t\th  = &mh \t) \terr = codec.NewEncoderBytes(&data, h).Encode(v) \treturn }  func (storage *RedisStorage) Load(data []byte, v interface{}) error { \tvar ( \t\tmh codec.MsgpackHandle \t\th  = &mh \t) \treturn codec.NewDecoderBytes(data, h).Decode(v) } <\/code><\/pre>\n<p>  \u041e\u043f\u0438\u0448\u0435\u043c \u0442\u0435\u043f\u0435\u0440\u044c \u0434\u0440\u0443\u0433\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u044b.<\/p>\n<h5>C\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 Bin<\/h5>\n<p>  <\/p>\n<pre><code>func (storage *RedisStorage) UpdateBin(bin *Bin) (err error) { \tdumpedBin, err := storage.Dump(bin) \tif err != nil { \t\treturn \t} \tconn := storage.pool.Get() \tdefer conn.Close() \tkey := storage.getKey(BIN_KEY, bin.Name)  \tconn.Send(&quot;SET&quot;, key, dumpedBin) \tconn.Send(&quot;EXPIRE&quot;, key, storage.binLifetime) \tconn.Flush() \treturn err }  func (storage *RedisStorage) CreateBin(bin *Bin) error { \tif err := storage.UpdateBin(bin); err != nil { \t\treturn err \t} \treturn nil } <\/code><\/pre>\n<p>  \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043c\u044b \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0443\u0435\u043c bin \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u043c\u0435\u0442\u043e\u0434\u0430 Dump. \u041f\u043e\u0442\u043e\u043c \u0431\u0435\u0440\u0451\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0440\u0435\u0434\u0438\u0441\u0430 \u0438\u0437 \u043f\u0443\u043b\u0430 (\u043d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u044f \u0435\u0433\u043e \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 defer).  <\/p>\n<blockquote><p>Redigo \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0440\u0435\u0436\u0438\u043c pipeline, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u0431\u0443\u0444\u0435\u0440 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0447\u0435\u0440\u0435\u0437 \u043c\u0435\u0442\u043e\u0434 Send, \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0432\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0431\u0443\u0444\u0435\u0440\u0430 \u043c\u0435\u0442\u043e\u0434\u043e\u043c Flush \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432 Receive. \u041a\u043e\u043c\u0430\u043d\u0434\u0430 Do \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u0442 \u0432\u0441\u0435 \u0442\u0440\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0432 \u043e\u0434\u043d\u0443. \u0422\u0430\u043a \u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u043e\u043d\u043d\u043e\u0441\u0442\u044c, \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0432 <a href=\"http:\/\/godoc.org\/github.com\/garyburd\/redigo\/redis#hdr-Pipelining\">\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438<\/a> redigo.<\/p><\/blockquote>\n<p>  \u041c\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0434\u0432\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b, \u00abSET\u00bb \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 Bin \u043f\u043e \u0435\u0433\u043e \u0438\u043c\u0435\u043d\u0438 \u0438 Expire, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0432\u0440\u0435\u043c\u044f \u0436\u0438\u0437\u043d\u0438 \u044d\u0442\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438.<\/p>\n<h5>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 Bin<\/h5>\n<p>  <\/p>\n<pre><code>func (storage *RedisStorage) LookupBin(name string) (bin *Bin, err error) { \tconn := storage.pool.Get() \tdefer conn.Close() \treply, err := redis.Bytes(conn.Do(&quot;GET&quot;, storage.getKey(BIN_KEY, name))) \tif err != nil { \t\tif err == redis.ErrNil { \t\t\terr = errors.New(&quot;Bin was not found&quot;) \t\t} \t\treturn \t} \terr = storage.Load(reply, &bin) \treturn } <\/code><\/pre>\n<p>  \u0412\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 redis.Bytes \u043f\u044b\u0442\u0430\u0435\u0442\u0441\u044f \u0441\u0447\u0438\u0442\u0430\u0442\u044c \u043f\u0440\u0438\u0448\u0435\u0434\u0448\u0438\u0439 \u043e\u0442\u0432\u0435\u0442 \u043e\u0442 conn.Do \u0432 \u043c\u0430\u0441\u0441\u0438\u0432 \u0431\u0430\u0439\u0442\u043e\u0432. \u0415\u0441\u043b\u0438 \u043e\u0431\u044a\u0435\u043a\u0442 \u0431\u044b\u043b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d, \u0440\u0435\u0434\u0438\u0441 \u0432\u043e\u0437\u0432\u0440\u0430\u0442\u0438\u0442 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u0442\u0438\u043f \u043e\u0448\u0438\u0431\u043a\u0438 redis.ErrNil. \u0415\u0441\u043b\u0438 \u0432\u0441\u0451 \u043f\u0440\u043e\u0448\u043b\u043e \u0443\u0441\u043f\u0435\u0448\u043d\u043e, \u0442\u043e \u0434\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u0441\u044f \u0432 \u043e\u0431\u044a\u0435\u043a\u0442 bin, \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0439 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 \u0432 \u043c\u0435\u0442\u043e\u0434 Load.<\/p>\n<h5>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 Bin<\/h5>\n<p>  <\/p>\n<pre><code>func (storage *RedisStorage) LookupBins(names []string) ([]*Bin, error) { \tbins := []*Bin{} \tif len(names) == 0 { \t\treturn bins, nil \t} \targs := redis.Args{} \tfor _, name := range names { \t\targs = args.Add(storage.getKey(BIN_KEY, name)) \t} \tconn := storage.pool.Get() \tdefer conn.Close() \tif values, err := redis.Values(conn.Do(&quot;MGET&quot;, args...)); err == nil { \t\tbytes := [][]byte{} \t\tif err = redis.ScanSlice(values, &bytes); err != nil { \t\t\treturn nil, err \t\t} \t\tfor _, rawbin := range bytes { \t\t\tif len(rawbin) &gt; 0 { \t\t\t\tbin := &Bin{} \t\t\t\tif err := storage.Load(rawbin, bin); err == nil { \t\t\t\t\tbins = append(bins, bin) \t\t\t\t} \t\t\t} \t\t} \t\treturn bins, nil \t} else { \t\treturn nil, err \t} } <\/code><\/pre>\n<p>  \u0417\u0434\u0435\u0441\u044c \u043f\u043e\u0447\u0442\u0438 \u0432\u0441\u0451 \u0442\u043e\u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u0447\u0442\u043e \u0438 \u0432 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u043c \u043c\u0435\u0442\u043e\u0434\u0435, \u0437\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u0430 MGET \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u0440\u0435\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 redis.ScanSlice \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043e\u0442\u0432\u0435\u0442\u0430 \u0432 \u0441\u043b\u0430\u0439\u0441 \u043d\u0443\u0436\u043d\u043e\u0433\u043e \u0442\u0438\u043f\u0430.<\/p>\n<h5>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 Request<\/h5>\n<p>  <\/p>\n<pre><code>func (storage *RedisStorage) CreateRequest(bin *Bin, req *Request) (err error) { \tdata, err := storage.Dump(req) \tif err != nil { \t\treturn \t} \tconn := storage.pool.Get() \tdefer conn.Close() \tkey := storage.getKey(REQUESTS_KEY, bin.Name) \tconn.Send(&quot;LPUSH&quot;, key, req.Id) \tconn.Send(&quot;EXPIRE&quot;, key, storage.binLifetime) \tkey = storage.getKey(REQUEST_HASH_KEY, bin.Name) \tconn.Send(&quot;HSET&quot;, key, req.Id, data) \tconn.Send(&quot;EXPIRE&quot;, key, storage.binLifetime) \tconn.Flush() \trequestCount, err := redis.Int(conn.Receive()) \tif err != nil { \t\treturn \t} \tif requestCount &lt; storage.maxRequests { \t\tbin.RequestCount = requestCount \t} else { \t\tbin.RequestCount = storage.maxRequests \t} \tbin.Updated = time.Now().Unix() \tif requestCount &gt; storage.maxRequests * CLEANING_FACTOR { \t\tconn.Do(&quot;SADD&quot;, storage.getKey(CLEANING_SET), bin.Name) \t} \tif err = storage.UpdateBin(bin); err != nil { \t\treturn \t} \treturn } <\/code><\/pre>\n<p>  \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043c\u044b \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0432 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0434\u043b\u044f bin.Name, \u043f\u043e\u0442\u043e\u043c \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0432 \u0445\u0435\u0448 \u0442\u0430\u0431\u043b\u0438\u0446\u0443. \u041d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0435\u043c \u0432 \u043e\u0431\u043e\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432\u0440\u0435\u043c\u044f \u0436\u0438\u0437\u043d\u0438. \u041a\u043e\u043c\u0430\u043d\u0434\u0430 LPUSH \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 requestCount, \u0435\u0441\u043b\u0438 \u044d\u0442\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0440\u0435\u0432\u044b\u0441\u0438\u043b\u043e \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435, \u043f\u043e\u043c\u043d\u043e\u0436\u0435\u043d\u043d\u043e\u0435 \u043d\u0430 \u0444\u0430\u043a\u0442\u043e\u0440, \u0442\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u044d\u0442\u043e\u0442 Bin \u0432 \u043a\u0430\u043d\u0434\u0438\u0434\u0430\u0442\u044b \u043d\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u043e\u0447\u0438\u0441\u0442\u043a\u0443.<\/p>\n<p>  \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438 \u0441\u043f\u0438\u0441\u043a\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0441\u0434\u0435\u043b\u0430\u043d\u043e \u043f\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0438 \u0441 Bin \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u043c\u0438.<\/p>\n<h5>\u041e\u0447\u0438\u0441\u0442\u043a\u0430<\/h5>\n<p>  <\/p>\n<pre><code>func (storage *RedisStorage) clean() { \tfor { \t\tconn := storage.pool.Get() \t\tdefer conn.Close() \t\tbinName, err := redis.String(conn.Do(&quot;SPOP&quot;, storage.getKey(CLEANING_SET))) \t\tif err != nil { \t\t\tbreak \t\t} \t\tconn.Send(&quot;LRANGE&quot;, storage.getKey(REQUESTS_KEY, binName), storage.maxRequests, -1) \t\tconn.Send(&quot;LTRIM&quot;, storage.getKey(REQUESTS_KEY, binName), 0, storage.maxRequests-1) \t\tconn.Flush() \t\tif values, error := redis.Values(conn.Receive()); error == nil { \t\t\tids := []string{} \t\t\tif err := redis.ScanSlice(values, &ids); err != nil { \t\t\t\tcontinue \t\t\t} \t\t\tif len(ids) &gt; 0 { \t\t\t\targs := redis.Args{}.Add(storage.getKey(REQUEST_HASH_KEY, binName)).AddFlat(ids) \t\t\t\tconn.Do(&quot;HDEL&quot;, args...) \t\t\t} \t\t} \t} } <\/code><\/pre>\n<p>  \u0412 \u043e\u0442\u043b\u0438\u0447\u0438\u0438 \u043e\u0442 MemoryStorage, \u0437\u0434\u0435\u0441\u044c \u043c\u044b \u043e\u0447\u0438\u0449\u0430\u0435\u043c \u0438\u0437\u0431\u044b\u0442\u043e\u0447\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u0442\u0430\u043a \u043a\u0430\u043a \u0432\u0440\u0435\u043c\u044f \u0436\u0438\u0437\u043d\u0438 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439 \u0440\u0435\u0434\u0438\u0441\u0430 EXPIRE. \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043c\u044b \u0431\u0435\u0440\u0451\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430 \u043d\u0430 \u043e\u0447\u0438\u0449\u0435\u043d\u0438\u0435, \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u044b \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0434\u043b\u044f \u043d\u0435\u0433\u043e, \u043d\u0435 \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0445 \u0432 \u043b\u0438\u043c\u0438\u0442, \u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439 LTRIM \u0441\u0436\u0438\u043c\u0430\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0434\u043e \u043d\u0443\u0436\u043d\u043e\u0433\u043e \u043d\u0430\u043c \u0440\u0430\u0437\u043c\u0435\u0440\u0430. \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0440\u0430\u043d\u0435\u0435 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u044b \u043c\u044b \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0438\u0437 \u0445\u044d\u0448 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u044b HDEL, \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0449\u0435\u0439 \u0441\u0440\u0430\u0437\u0443 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u043b\u044e\u0447\u0435\u0439.<\/p>\n<p>  \u041c\u044b \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u043b\u0438 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c RedisStorage, \u0440\u044f\u0434\u043e\u043c \u0441 \u043d\u0438\u043c, \u0432 \u0444\u0430\u0439\u043b\u0435 redis_test.go \u0432\u044b \u043d\u0430\u0439\u0434\u0451\u0442\u0435 \u0442\u0430\u043a \u0436\u0435 \u0438 \u0442\u0435\u0441\u0442\u044b.<\/p>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0432\u044b\u0431\u0438\u0440\u0430\u0442\u044c \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0432 \u0444\u0430\u0439\u043b\u0435 api.go:<\/p>\n<pre><code>type RedisConfig struct { \tRedisAddr\t\t\tstring \tRedisPassword\t\tstring \tRedisPrefix\t\t\tstring }  type Config struct { ... \tStorage\t\t\t\tstring \tRedisConfig }  func GetApi(config *Config) *martini.ClassicMartini { \tvar storage Storage \tswitch config.Storage{ \tcase &quot;redis&quot;: \t\tredisStorage := NewRedisStorage(config.RedisAddr, config.RedisPassword, config.RedisPassword, MAX_REQUEST_COUNT, BIN_LIFETIME) \t\tredisStorage.StartCleaning(60) \t\tstorage = redisStorage \tdefault: \t\tmemoryStorage := NewMemoryStorage(MAX_REQUEST_COUNT, BIN_LIFETIME) \t\tmemoryStorage.StartCleaning(60) \t\tstorage = memoryStorage \t} ... <\/code><\/pre>\n<p>  \u041c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u043d\u043e\u0432\u043e\u0435 \u043f\u043e\u043b\u0435 Storage \u0432 \u043d\u0430\u0448\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0438 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043d\u0435\u0451 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0443\u0440\u0435\u043c \u043b\u0438\u0431\u043e RedisStorage \u043b\u0438\u0431\u043e MemoryStorage. \u0422\u0430\u043a \u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e RedisConfig, \u0434\u043b\u044f \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u043f\u0446\u0438\u0439 \u0440\u0435\u0434\u0438\u0441\u0430.<\/p>\n<p>  \u0422\u0430\u043a \u0436\u0435 \u0432\u043d\u0435\u0441\u0451\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c\u043e\u043c \u0444\u0430\u0439\u043b\u0435 main.go:  <\/p>\n<pre><code>import ( \t&quot;skimmer&quot; \t&quot;flag&quot; )  var ( \tconfig = skimmer.Config{ \t\tSessionSecret: &quot;secret123&quot;, \t\tRedisConfig: skimmer.RedisConfig{ \t\t\tRedisAddr: &quot;127.0.0.1:6379&quot;, \t\t\tRedisPassword: &quot;&quot;, \t\t\tRedisPrefix: &quot;skimmer&quot;, \t\t}, \t} )  func init() { \tflag.StringVar(&config.Storage, &quot;storage&quot;, &quot;memory&quot;, &quot;available storages: redis, memory&quot;) \tflag.StringVar(&config.SessionSecret, &quot;sessionSecret&quot;, config.SessionSecret, &quot;&quot;) \tflag.StringVar(&config.RedisAddr, &quot;redisAddr&quot;, config.RedisAddr, &quot;redis storage only&quot;) \tflag.StringVar(&config.RedisPassword, &quot;redisPassword&quot;, config.RedisPassword, &quot;redis storage only&quot;) \tflag.StringVar(&config.RedisPrefix, &quot;redisPrefix&quot;, config.RedisPrefix, &quot;redis storage only&quot;) }  func main() { \tflag.Parse() \tapi := skimmer.GetApi(&config) \tapi.Run() } <\/code><\/pre>\n<p>  \u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u043a\u0435\u0442 <a href=\"http:\/\/golang.org\/pkg\/flag\/\">flag<\/a>, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u0439 \u043b\u0435\u0433\u043a\u043e \u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0434\u043b\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u044e init \u0444\u043b\u0430\u0433 \u00abstorage\u00bb, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u044f\u043c\u043e \u0432 \u043d\u0430\u0448 config \u0432 \u043f\u043e\u043b\u0435 Storage. \u0422\u0430\u043a \u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u043f\u0446\u0438\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0440\u0435\u0434\u0438\u0441\u0430.  <\/p>\n<blockquote><p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f init \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u0430\u044f \u0434\u043b\u044f Go, \u043e\u043d\u0430 \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u043f\u0430\u043a\u0435\u0442\u0430. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043f\u0440\u043e <a href=\"http:\/\/golang.org\/ref\/spec#Program_execution\">\u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c<\/a> \u0432 Go. <\/p><\/blockquote>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c, \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0432 \u043d\u0430\u0448\u0443 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c &#8212;help, \u043c\u044b \u0443\u0432\u0438\u0434\u0438\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432:<\/p>\n<pre><code>&gt; go run .\/src\/main.go --help Usage of ...\/main:   -redisAddr=&quot;127.0.0.1:6379&quot;: redis storage only   -redisPassword=&quot;&quot;: redis storage only   -redisPrefix=&quot;skimmer&quot;: redis storage only   -sessionSecret=&quot;secret123&quot;:   -storage=&quot;memory&quot;: available storages: redis, memory <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043f\u043e\u043a\u0430 \u0435\u0449\u0451 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0441\u044b\u0440\u043e\u0435, \u0438 \u043d\u0435 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435, \u043d\u043e \u0443\u0436\u0435 \u0433\u043e\u0442\u043e\u0432\u043e\u0435 \u043a \u0440\u0430\u0431\u043e\u0442\u0435 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0443 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430\u0445.<\/p>\n<p>  \u0412 \u0442\u0440\u0435\u0442\u044c\u0435\u0439 \u0447\u0430\u0441\u0442\u0438 \u043c\u044b \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043e \u0432\u044b\u043a\u043b\u0430\u0434\u043a\u0435 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 GAE, Cocaine \u0438 Heroku, \u0430 \u0442\u0430\u043a \u0436\u0435 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u044f\u0442\u044c \u0435\u0433\u043e \u0432 \u0432\u0438\u0434\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u0435\u043c\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0435\u0433\u043e \u0432\u0441\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b. \u0411\u0443\u0434\u0435\u043c \u043f\u0438\u0441\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u044b \u043d\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c, \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u0437\u0430\u043d\u0438\u043c\u0430\u044f\u0441\u044c \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0435\u0439. \u041d\u0430\u0443\u0447\u0438\u043c\u0441\u044f \u043f\u0440\u043e\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0438 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c. \u0418 \u043d\u0430\u043f\u043e\u0441\u043b\u0435\u0434\u043e\u043a \u0432\u0441\u0442\u0440\u043e\u0438\u043c \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 groupcache \u043f\u0440\u044f\u043c\u043e \u0432\u043d\u0443\u0442\u0440\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>  \u0411\u0443\u0434\u0443 \u0440\u0430\u0434 \u043b\u044e\u0431\u044b\u043c \u043f\u0440\u0430\u0432\u043a\u0430\u043c \u0438 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c \u043f\u043e \u0441\u0442\u0430\u0442\u044c\u0435.    \t<\/p>\n<div class=\"clear\"><\/div>\n<\/p><\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"http:\/\/habrahabr.ru\/post\/214425\/\"> http:\/\/habrahabr.ru\/post\/214425\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div class=\"content html_format\">   \t\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u044c\u0438 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043f\u043e\u043b\u043d\u043e\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 Go.<\/p>\n<p>  \u0412 <a href=\"http:\/\/habrahabr.ru\/post\/208680\/\">\u043f\u0435\u0440\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438<\/a> \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 REST API \u0438 \u043d\u0430\u0443\u0447\u0438\u043b\u0438\u0441\u044c \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0449\u0438\u0435 HTTP \u0437\u0430\u043f\u0440\u043e\u0441\u044b. \u0412 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u0438, \u043c\u044b \u043f\u043e\u043a\u0440\u043e\u0435\u043c \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0442\u0435\u0441\u0442\u0430\u043c\u0438, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u0440\u0430\u0441\u0438\u0432\u044b\u0439 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 AngularJS \u0438 Bootstrap, \u0438 \u0432\u043d\u0435\u0434\u0440\u0438\u043c \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0434\u043b\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.  <\/p>\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-214425","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/214425","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=214425"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/214425\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=214425"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=214425"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=214425"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}