-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdb.json
1 lines (1 loc) · 486 KB
/
db.json
1
{"meta":{"version":1,"warehouse":"2.2.0"},"models":{"Asset":[{"_id":"themes/next/source/css/main.styl","path":"css/main.styl","modified":1,"renderable":1},{"_id":"themes/next/source/images/algolia_logo.svg","path":"images/algolia_logo.svg","modified":1,"renderable":1},{"_id":"themes/next/source/images/apple-touch-icon.png","path":"images/apple-touch-icon.png","modified":1,"renderable":1},{"_id":"themes/next/source/images/avatar.gif","path":"images/avatar.gif","modified":1,"renderable":1},{"_id":"themes/next/source/images/cc-by-nc-nd.svg","path":"images/cc-by-nc-nd.svg","modified":1,"renderable":1},{"_id":"themes/next/source/images/cc-by-nc-sa.svg","path":"images/cc-by-nc-sa.svg","modified":1,"renderable":1},{"_id":"themes/next/source/images/cc-by-nc.svg","path":"images/cc-by-nc.svg","modified":1,"renderable":1},{"_id":"themes/next/source/images/cc-by-nd.svg","path":"images/cc-by-nd.svg","modified":1,"renderable":1},{"_id":"themes/next/source/images/cc-by-sa.svg","path":"images/cc-by-sa.svg","modified":1,"renderable":1},{"_id":"themes/next/source/images/cc-by.svg","path":"images/cc-by.svg","modified":1,"renderable":1},{"_id":"themes/next/source/images/cc-zero.svg","path":"images/cc-zero.svg","modified":1,"renderable":1},{"_id":"themes/next/source/images/favicon-16x16.png","path":"images/favicon-16x16.png","modified":1,"renderable":1},{"_id":"themes/next/source/images/favicon-32x32.png","path":"images/favicon-32x32.png","modified":1,"renderable":1},{"_id":"themes/next/source/images/loading.gif","path":"images/loading.gif","modified":1,"renderable":1},{"_id":"themes/next/source/images/logo.svg","path":"images/logo.svg","modified":1,"renderable":1},{"_id":"themes/next/source/images/placeholder.gif","path":"images/placeholder.gif","modified":1,"renderable":1},{"_id":"themes/next/source/images/quote-l.svg","path":"images/quote-l.svg","modified":1,"renderable":1},{"_id":"themes/next/source/images/quote-r.svg","path":"images/quote-r.svg","modified":1,"renderable":1},{"_id":"themes/next/source/images/searchicon.png","path":"images/searchicon.png","modified":1,"renderable":1},{"_id":"themes/next/source/js/src/affix.js","path":"js/src/affix.js","modified":1,"renderable":1},{"_id":"themes/next/source/js/src/algolia-search.js","path":"js/src/algolia-search.js","modified":1,"renderable":1},{"_id":"themes/next/source/js/src/bootstrap.js","path":"js/src/bootstrap.js","modified":1,"renderable":1},{"_id":"themes/next/source/js/src/exturl.js","path":"js/src/exturl.js","modified":1,"renderable":1},{"_id":"themes/next/source/js/src/hook-duoshuo.js","path":"js/src/hook-duoshuo.js","modified":1,"renderable":1},{"_id":"themes/next/source/js/src/js.cookie.js","path":"js/src/js.cookie.js","modified":1,"renderable":1},{"_id":"themes/next/source/js/src/motion.js","path":"js/src/motion.js","modified":1,"renderable":1},{"_id":"themes/next/source/js/src/post-details.js","path":"js/src/post-details.js","modified":1,"renderable":1},{"_id":"themes/next/source/js/src/scroll-cookie.js","path":"js/src/scroll-cookie.js","modified":1,"renderable":1},{"_id":"themes/next/source/js/src/scrollspy.js","path":"js/src/scrollspy.js","modified":1,"renderable":1},{"_id":"themes/next/source/js/src/utils.js","path":"js/src/utils.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/algolia-instant-search/instantsearch.min.css","path":"lib/algolia-instant-search/instantsearch.min.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/canvas-nest/canvas-nest.min.js","path":"lib/canvas-nest/canvas-nest.min.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/canvas-ribbon/canvas-ribbon.js","path":"lib/canvas-ribbon/canvas-ribbon.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fastclick/LICENSE","path":"lib/fastclick/LICENSE","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fastclick/README.md","path":"lib/fastclick/README.md","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fastclick/bower.json","path":"lib/fastclick/bower.json","modified":1,"renderable":1},{"_id":"themes/next/source/lib/font-awesome/HELP-US-OUT.txt","path":"lib/font-awesome/HELP-US-OUT.txt","modified":1,"renderable":1},{"_id":"themes/next/source/lib/font-awesome/bower.json","path":"lib/font-awesome/bower.json","modified":1,"renderable":1},{"_id":"themes/next/source/lib/jquery_lazyload/CONTRIBUTING.md","path":"lib/jquery_lazyload/CONTRIBUTING.md","modified":1,"renderable":1},{"_id":"themes/next/source/lib/jquery_lazyload/README.md","path":"lib/jquery_lazyload/README.md","modified":1,"renderable":1},{"_id":"themes/next/source/lib/jquery_lazyload/bower.json","path":"lib/jquery_lazyload/bower.json","modified":1,"renderable":1},{"_id":"themes/next/source/lib/jquery_lazyload/jquery.lazyload.js","path":"lib/jquery_lazyload/jquery.lazyload.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/jquery_lazyload/jquery.scrollstop.js","path":"lib/jquery_lazyload/jquery.scrollstop.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/pace/pace-theme-barber-shop.min.css","path":"lib/pace/pace-theme-barber-shop.min.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/pace/pace-theme-big-counter.min.css","path":"lib/pace/pace-theme-big-counter.min.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/pace/pace-theme-bounce.min.css","path":"lib/pace/pace-theme-bounce.min.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/pace/pace-theme-center-atom.min.css","path":"lib/pace/pace-theme-center-atom.min.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/pace/pace-theme-center-circle.min.css","path":"lib/pace/pace-theme-center-circle.min.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/pace/pace-theme-center-radar.min.css","path":"lib/pace/pace-theme-center-radar.min.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/pace/pace-theme-center-simple.min.css","path":"lib/pace/pace-theme-center-simple.min.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/pace/pace-theme-corner-indicator.min.css","path":"lib/pace/pace-theme-corner-indicator.min.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/pace/pace-theme-fill-left.min.css","path":"lib/pace/pace-theme-fill-left.min.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/pace/pace-theme-flash.min.css","path":"lib/pace/pace-theme-flash.min.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/pace/pace-theme-loading-bar.min.css","path":"lib/pace/pace-theme-loading-bar.min.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/pace/pace-theme-mac-osx.min.css","path":"lib/pace/pace-theme-mac-osx.min.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/pace/pace-theme-minimal.min.css","path":"lib/pace/pace-theme-minimal.min.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/pace/pace.min.js","path":"lib/pace/pace.min.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/three/canvas_lines.min.js","path":"lib/three/canvas_lines.min.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/three/canvas_sphere.min.js","path":"lib/three/canvas_sphere.min.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/three/three-waves.min.js","path":"lib/three/three-waves.min.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/velocity/bower.json","path":"lib/velocity/bower.json","modified":1,"renderable":1},{"_id":"themes/next/source/lib/velocity/velocity.min.js","path":"lib/velocity/velocity.min.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/velocity/velocity.ui.js","path":"lib/velocity/velocity.ui.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/velocity/velocity.ui.min.js","path":"lib/velocity/velocity.ui.min.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/jquery/index.js","path":"lib/jquery/index.js","modified":1,"renderable":1},{"_id":"themes/next/source/js/src/schemes/pisces.js","path":"js/src/schemes/pisces.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/Han/dist/han.min.css","path":"lib/Han/dist/han.min.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/Han/dist/han.css","path":"lib/Han/dist/han.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/Han/dist/han.min.js","path":"lib/Han/dist/han.min.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fancybox/source/blank.gif","path":"lib/fancybox/source/blank.gif","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fancybox/source/[email protected]","path":"lib/fancybox/source/[email protected]","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fancybox/source/fancybox_loading.gif","path":"lib/fancybox/source/fancybox_loading.gif","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fancybox/source/fancybox_overlay.png","path":"lib/fancybox/source/fancybox_overlay.png","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fancybox/source/fancybox_sprite.png","path":"lib/fancybox/source/fancybox_sprite.png","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fancybox/source/[email protected]","path":"lib/fancybox/source/[email protected]","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fancybox/source/jquery.fancybox.css","path":"lib/fancybox/source/jquery.fancybox.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fancybox/source/jquery.fancybox.js","path":"lib/fancybox/source/jquery.fancybox.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fancybox/source/jquery.fancybox.pack.js","path":"lib/fancybox/source/jquery.fancybox.pack.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fastclick/lib/fastclick.js","path":"lib/fastclick/lib/fastclick.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fastclick/lib/fastclick.min.js","path":"lib/fastclick/lib/fastclick.min.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/font-awesome/css/font-awesome.css","path":"lib/font-awesome/css/font-awesome.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/font-awesome/css/font-awesome.css.map","path":"lib/font-awesome/css/font-awesome.css.map","modified":1,"renderable":1},{"_id":"themes/next/source/lib/font-awesome/css/font-awesome.min.css","path":"lib/font-awesome/css/font-awesome.min.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/ua-parser-js/dist/ua-parser.min.js","path":"lib/ua-parser-js/dist/ua-parser.min.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/ua-parser-js/dist/ua-parser.pack.js","path":"lib/ua-parser-js/dist/ua-parser.pack.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/font-awesome/fonts/fontawesome-webfont.woff","path":"lib/font-awesome/fonts/fontawesome-webfont.woff","modified":1,"renderable":1},{"_id":"themes/next/source/lib/velocity/velocity.js","path":"lib/velocity/velocity.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/Han/dist/font/han-space.otf","path":"lib/Han/dist/font/han-space.otf","modified":1,"renderable":1},{"_id":"themes/next/source/lib/Han/dist/font/han-space.woff","path":"lib/Han/dist/font/han-space.woff","modified":1,"renderable":1},{"_id":"themes/next/source/lib/Han/dist/font/han.woff","path":"lib/Han/dist/font/han.woff","modified":1,"renderable":1},{"_id":"themes/next/source/lib/Han/dist/font/han.otf","path":"lib/Han/dist/font/han.otf","modified":1,"renderable":1},{"_id":"themes/next/source/lib/Han/dist/font/han.woff2","path":"lib/Han/dist/font/han.woff2","modified":1,"renderable":1},{"_id":"themes/next/source/lib/Han/dist/han.js","path":"lib/Han/dist/han.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fancybox/source/helpers/fancybox_buttons.png","path":"lib/fancybox/source/helpers/fancybox_buttons.png","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fancybox/source/helpers/jquery.fancybox-buttons.css","path":"lib/fancybox/source/helpers/jquery.fancybox-buttons.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fancybox/source/helpers/jquery.fancybox-buttons.js","path":"lib/fancybox/source/helpers/jquery.fancybox-buttons.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fancybox/source/helpers/jquery.fancybox-media.js","path":"lib/fancybox/source/helpers/jquery.fancybox-media.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fancybox/source/helpers/jquery.fancybox-thumbs.css","path":"lib/fancybox/source/helpers/jquery.fancybox-thumbs.css","modified":1,"renderable":1},{"_id":"themes/next/source/lib/fancybox/source/helpers/jquery.fancybox-thumbs.js","path":"lib/fancybox/source/helpers/jquery.fancybox-thumbs.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/font-awesome/fonts/FontAwesome.otf","path":"lib/font-awesome/fonts/FontAwesome.otf","modified":1,"renderable":1},{"_id":"themes/next/source/lib/font-awesome/fonts/fontawesome-webfont.woff2","path":"lib/font-awesome/fonts/fontawesome-webfont.woff2","modified":1,"renderable":1},{"_id":"themes/next/source/lib/font-awesome/fonts/fontawesome-webfont.eot","path":"lib/font-awesome/fonts/fontawesome-webfont.eot","modified":1,"renderable":1},{"_id":"themes/next/source/lib/font-awesome/fonts/fontawesome-webfont.ttf","path":"lib/font-awesome/fonts/fontawesome-webfont.ttf","modified":1,"renderable":1},{"_id":"themes/next/source/lib/algolia-instant-search/instantsearch.min.js","path":"lib/algolia-instant-search/instantsearch.min.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/three/three.min.js","path":"lib/three/three.min.js","modified":1,"renderable":1},{"_id":"themes/next/source/lib/font-awesome/fonts/fontawesome-webfont.svg","path":"lib/font-awesome/fonts/fontawesome-webfont.svg","modified":1,"renderable":1}],"Cache":[{"_id":"themes/next/.bowerrc","hash":"3228a58ed0ece9f85e1e3136352094080b8dece1","modified":1525096196158},{"_id":"themes/next/.editorconfig","hash":"792fd2bd8174ece1a75d5fd24ab16594886f3a7f","modified":1525096196158},{"_id":"themes/next/.gitattributes","hash":"44bd4729c74ccb88110804f41746fec07bf487d4","modified":1525096196158},{"_id":"themes/next/.gitignore","hash":"32ea93f21d8693d5d8fa4eef1c51a21ad0670047","modified":1525096196159},{"_id":"themes/next/.hound.yml","hash":"b76daa84c9ca3ad292c78412603370a367cc2bc3","modified":1525096196159},{"_id":"themes/next/.javascript_ignore","hash":"8a224b381155f10e6eb132a4d815c5b52962a9d1","modified":1525096196159},{"_id":"themes/next/.jshintrc","hash":"9928f81bd822f6a8d67fdbc909b517178533bca9","modified":1525096196159},{"_id":"themes/next/.stylintrc","hash":"b28e24704a5d8de08346c45286574c8e76cc109f","modified":1525096196159},{"_id":"themes/next/.travis.yml","hash":"d60d4a5375fea23d53b2156b764a99b2e56fa660","modified":1525096196159},{"_id":"themes/next/LICENSE","hash":"f293bcfcdc06c0b77ba13570bb8af55eb5c059fd","modified":1525096196160},{"_id":"themes/next/README.md","hash":"aba736f1b934f2b169035ccf94d2771a270ec21d","modified":1525096196160},{"_id":"themes/next/README.cn.md","hash":"6d9177e7dad87e6129760e4b559bd3f7a15429d7","modified":1525096196160},{"_id":"themes/next/_config.yml","hash":"6d5cfc45e0da4722c8986dbca07db297f4487aee","modified":1525096196160},{"_id":"themes/next/bower.json","hash":"6d6ae7531cf3fedc97c58cdad664f5793eb3cc88","modified":1525096196160},{"_id":"themes/next/gulpfile.coffee","hash":"031bffc483e417b20e90eceb6cf358e7596d2e69","modified":1525096196160},{"_id":"themes/next/package.json","hash":"2174d61cbc9fa276a82b02649426842f088825ab","modified":1525096196178},{"_id":"source/categories/index.md","hash":"399bca60cc7620a134714cb7fe7c019598ae4ef8","modified":1525096196138},{"_id":"source/_posts/LeetCode-Week-1.md","hash":"22a8e928408d41aecaef46d33cd86d9da3451e8d","modified":1525096196132},{"_id":"source/_posts/MapReduce.md","hash":"bdcdf17585d662f39a8921b154eceb182bb8fed1","modified":1525096196132},{"_id":"source/_posts/Raft-PartA.md","hash":"c6e1d6f8e0aa782757789a1149ffbe82e1985439","modified":1525096196135},{"_id":"source/_posts/ZooKeeper.md","hash":"66bba40c1f60fb209d4c2317bad697f22f085210","modified":1525096196136},{"_id":"source/_posts/kubernetes.md","hash":"e720c2ab40d10ee236bda9967e6f728fe3c1fad0","modified":1564409991831},{"_id":"source/_posts/the-concurrency-on-go.md","hash":"a0dcc7f2ebc002de8899a097c2f46b30e761e0a7","modified":1525096196137},{"_id":"source/tags/index.md","hash":"d6bf90d150b50a164a5de6d19997ebeffb7bde96","modified":1525096196138},{"_id":"themes/next/.github/CONTRIBUTING.md","hash":"3b5eafd32abb718e56ccf8d1cee0607ad8ce611d","modified":1525096196158},{"_id":"themes/next/.github/ISSUE_TEMPLATE.md","hash":"352093a1b210c72136687fd2eee649244cee402c","modified":1525096196158},{"_id":"themes/next/.github/PULL_REQUEST_TEMPLATE.md","hash":"902f627155a65099e0a37842ff396a58d0dc306f","modified":1525096196159},{"_id":"themes/next/.github/browserstack_logo.png","hash":"a6c43887f64a7f48a2814e3714eaa1215e542037","modified":1525096196159},{"_id":"themes/next/languages/de.yml","hash":"057e7df11ddeb1c8c15a5d7c5ff29430d725ec6b","modified":1525096196161},{"_id":"themes/next/languages/default.yml","hash":"44ef3f26917f467459326c2c8be2f73e4d947f35","modified":1525096196161},{"_id":"themes/next/languages/en.yml","hash":"44ef3f26917f467459326c2c8be2f73e4d947f35","modified":1525096196161},{"_id":"themes/next/languages/fr-FR.yml","hash":"7e4eb7011b8feee641cfb11c6e73180b0ded1c0f","modified":1525096196161},{"_id":"themes/next/languages/id.yml","hash":"b5de1ea66dd9ef54cac9a1440eaa4e3f5fc011f5","modified":1525096196161},{"_id":"themes/next/languages/it.yml","hash":"aa595f2bda029f73ef7bfa104b4c55c3f4e9fb4c","modified":1525096196161},{"_id":"themes/next/languages/ja.yml","hash":"3c76e16fd19b262864475faa6854b718bc08c4d8","modified":1525096196161},{"_id":"themes/next/languages/ko.yml","hash":"ea5b46056e73ebcee121d5551627af35cbffc900","modified":1525096196162},{"_id":"themes/next/languages/pt-BR.yml","hash":"b1694ae766ed90277bcc4daca4b1cfa19cdcb72b","modified":1525096196162},{"_id":"themes/next/languages/pt.yml","hash":"44b61f2d085b827b507909a0b8f8ce31c6ef5d04","modified":1525096196162},{"_id":"themes/next/languages/ru.yml","hash":"98ec6f0b7183282e11cffc7ff586ceb82400dd75","modified":1525096196162},{"_id":"themes/next/languages/zh-Hans.yml","hash":"b342544b715da19d982349017bd56c5aaca11f71","modified":1525096196162},{"_id":"themes/next/languages/zh-hk.yml","hash":"2ef272bcb1f325480f59f1e2ab95584de3c6b8da","modified":1525096196162},{"_id":"themes/next/languages/zh-tw.yml","hash":"c53941a2eaac8e3a2f8dacc73ed555d3c6c5bd59","modified":1525096196163},{"_id":"themes/next/layout/_layout.swig","hash":"72a1a2612f7c14cc9af51c55c8dfac39d6c0a2bf","modified":1525096196163},{"_id":"themes/next/layout/archive.swig","hash":"f0a8225feafd971419837cdb4bcfec98a4a59b2f","modified":1525096196177},{"_id":"themes/next/layout/category.swig","hash":"4472255f4a3e3dd6d79201523a9526dcabdfbf18","modified":1525096196177},{"_id":"themes/next/layout/index.swig","hash":"783611349c941848a0e26ee2f1dc44dd14879bd1","modified":1525096196177},{"_id":"themes/next/layout/page.swig","hash":"969caaee05bdea725e99016eb63d810893a73e99","modified":1525096196178},{"_id":"themes/next/layout/post.swig","hash":"b3589a8e46288a10d20e41c7a5985d2493725aec","modified":1525096196178},{"_id":"themes/next/layout/schedule.swig","hash":"d86f8de4e118f8c4d778b285c140474084a271db","modified":1525096196178},{"_id":"themes/next/layout/tag.swig","hash":"7e0a7d7d832883eddb1297483ad22c184e4368de","modified":1525096196178},{"_id":"themes/next/scripts/merge-configs.js","hash":"cb617ddf692f56e6b6129564d52e302f50b28243","modified":1525096196178},{"_id":"themes/next/scripts/merge.js","hash":"9130dabe6a674c54b535f322b17d75fe6081472f","modified":1525096196179},{"_id":"themes/next/test/.jshintrc","hash":"19f93d13d1689fe033c82eb2d5f3ce30b6543cc0","modified":1525096196225},{"_id":"themes/next/test/helpers.js","hash":"a1f5de25154c3724ffc24a91ddc576cdbd60864f","modified":1525096196225},{"_id":"themes/next/test/intern.js","hash":"11fa8a4f5c3b4119a179ae0a2584c8187f907a73","modified":1525096196226},{"_id":"themes/next/source/fonts/.gitkeep","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1525096196196},{"_id":"source/_posts/MapReduce/transfer.png","hash":"916d4f254a43c9c2ad774895db9fa88eb8261738","modified":1525096196134},{"_id":"source/_posts/Raft-PartA/raft.png","hash":"a8d000abdb9a75caec7585ad6853b68aa779a89a","modified":1525096196136},{"_id":"source/_posts/Raft-PartA/election.png","hash":"68402d39dd3eca68e3463400a32e51e081ffb8a8","modified":1525096196135},{"_id":"source/_posts/ZooKeeper/service.png","hash":"3a478d1cba6b2ed2acd22799804c483a9d9bf0c9","modified":1525096196136},{"_id":"source/_posts/ZooKeeper/zknamespace.jpg","hash":"d0207adf88ed531749a86d9b100f503b121e3c67","modified":1525096196137},{"_id":"source/_posts/the-concurrency-on-go/all_1.png","hash":"8d5f7fb886dd9d0186e50416d3e7077f1299ae58","modified":1525096196137},{"_id":"source/_posts/the-concurrency-on-go/no_sleep_1.png","hash":"107bec997191699c00694efa1e6056ee90b2ca4e","modified":1525096196137},{"_id":"source/_posts/the-concurrency-on-go/test4.png","hash":"7345fe9a1dd1dad0951ab3240b8965afd71ec754","modified":1525096196138},{"_id":"source/_posts/the-concurrency-on-go/test5.png","hash":"4caa30ac40507796014769480df6524577a6590d","modified":1525096196138},{"_id":"themes/next/layout/_custom/header.swig","hash":"adc83b19e793491b1c6ea0fd8b46cd9f32e592fc","modified":1525096196163},{"_id":"themes/next/layout/_custom/sidebar.swig","hash":"adc83b19e793491b1c6ea0fd8b46cd9f32e592fc","modified":1525096196163},{"_id":"themes/next/layout/_macro/post-collapse.swig","hash":"31322a7f57936cf2dc62e824af5490da5354cf02","modified":1525096196163},{"_id":"themes/next/layout/_macro/post-copyright.swig","hash":"665a928604f99d2ba7dc4a4a9150178229568cc6","modified":1525096196164},{"_id":"themes/next/layout/_macro/reward.swig","hash":"56e8d8556cf474c56ae1bef9cb7bbd26554adb07","modified":1525096196164},{"_id":"themes/next/layout/_macro/post.swig","hash":"767e1d5503ecce85f577c8fb673a3503b65484ce","modified":1525096196164},{"_id":"themes/next/layout/_macro/sidebar.swig","hash":"9efc455894921a66bbc074055d3b39c8a34a48a4","modified":1525096196166},{"_id":"themes/next/layout/_macro/wechat-subscriber.swig","hash":"39852700e4084ecccffa6d4669168e5cc0514c9e","modified":1525096196167},{"_id":"themes/next/layout/_partials/comments.swig","hash":"0fbeb56e9c4d5193c6a181d45c4b1b7a44a0e027","modified":1525096196167},{"_id":"themes/next/layout/_partials/footer.swig","hash":"c4d6181f5d3db5365e622f78714af8cc58d7a45e","modified":1525096196168},{"_id":"themes/next/layout/_partials/head.swig","hash":"6b94fe8f3279daea5623c49ef4bb35917ba57510","modified":1525096196168},{"_id":"themes/next/layout/_partials/header.swig","hash":"ed042be6252848058c90109236ec988e392d91d4","modified":1525096196168},{"_id":"themes/next/layout/_partials/page-header.swig","hash":"1efd925d34a5d4ba2dc0838d9c86ba911e705fc9","modified":1525096196169},{"_id":"themes/next/layout/_partials/pagination.swig","hash":"9e8e21d194ef44d271b1cca0bc1448c14d7edf4f","modified":1525096196169},{"_id":"themes/next/layout/_partials/search.swig","hash":"9dbd378e94abfcb3f864a5b8dbbf18d212ca2ee0","modified":1525096196169},{"_id":"themes/next/layout/_scripts/boostrap.swig","hash":"03aaebe9d50f6acb007ec38cc04acd1cfceb404d","modified":1525096196171},{"_id":"themes/next/layout/_scripts/commons.swig","hash":"766b2bdda29523ed6cd8d7aa197f996022f8fd94","modified":1525096196171},{"_id":"themes/next/layout/_scripts/vendors.swig","hash":"9baf90f7c40b3b10f288e9268c3191e895890cea","modified":1525096196172},{"_id":"themes/next/layout/_third-party/duoshuo-hot-articles.swig","hash":"5d4638c46aef65bf32a01681495b62416ccc98db","modified":1525096196175},{"_id":"themes/next/layout/_third-party/exturl.swig","hash":"7c04a42319d728be356746363aff8ea247791d24","modified":1525096196175},{"_id":"themes/next/layout/_third-party/mathjax.swig","hash":"6d25596d6a7c57700d37b607f8d9a62d89708683","modified":1525096196176},{"_id":"themes/next/layout/_third-party/rating.swig","hash":"fc93b1a7e6aed0dddb1f3910142b48d8ab61174e","modified":1525096196176},{"_id":"themes/next/layout/_third-party/schedule.swig","hash":"22369026c87fc23893c35a7f250b42f3bb1b60f1","modified":1525096196176},{"_id":"themes/next/layout/_third-party/scroll-cookie.swig","hash":"1ddb2336a1a19b47af3017047012c01ec5f54529","modified":1525096196176},{"_id":"themes/next/scripts/tags/button.js","hash":"62e6dbeb53d07627a048132c79630b45d9a8f2cc","modified":1525096196179},{"_id":"themes/next/scripts/tags/center-quote.js","hash":"535fc542781021c4326dec24d8495cbb1387634a","modified":1525096196179},{"_id":"themes/next/scripts/tags/exturl.js","hash":"8d7e60f60779bde050d20fd76f6fdc36fc85e06d","modified":1525096196179},{"_id":"themes/next/scripts/tags/full-image.js","hash":"8eeb3fb89540299bdbb799edfdfdac3743b50596","modified":1525096196179},{"_id":"themes/next/scripts/tags/group-pictures.js","hash":"49252824cd53184dc9b97b2f2d87ff28e1b3ef27","modified":1525096196179},{"_id":"themes/next/scripts/tags/label.js","hash":"2f8f41a7316372f0d1ed6b51190dc4acd3e16fff","modified":1525096196179},{"_id":"themes/next/scripts/tags/lazy-image.js","hash":"eeeabede68cf263de9e6593ecf682f620da16f0a","modified":1525096196180},{"_id":"themes/next/scripts/tags/note.js","hash":"64de4e9d01cf3b491ffc7d53afdf148ee5ad9779","modified":1525096196180},{"_id":"themes/next/scripts/tags/tabs.js","hash":"5786545d51c38e8ca38d1bfc7dd9e946fc70a316","modified":1525096196180},{"_id":"themes/next/source/css/main.styl","hash":"20702c48d6053c92c5bcdbc68e8d0ef1369848a0","modified":1525096196196},{"_id":"themes/next/source/images/algolia_logo.svg","hash":"ec119560b382b2624e00144ae01c137186e91621","modified":1525096196196},{"_id":"themes/next/source/images/apple-touch-icon.png","hash":"2959dbc97f31c80283e67104fe0854e2369e40aa","modified":1525096196197},{"_id":"themes/next/source/images/avatar.gif","hash":"264082bb3a1af70d5499c7d22b0902cb454b6d12","modified":1525096196197},{"_id":"themes/next/source/images/cc-by-nc-nd.svg","hash":"c6524ece3f8039a5f612feaf865d21ec8a794564","modified":1525096196197},{"_id":"themes/next/source/images/cc-by-nc-sa.svg","hash":"3031be41e8753c70508aa88e84ed8f4f653f157e","modified":1525096196197},{"_id":"themes/next/source/images/cc-by-nc.svg","hash":"8d39b39d88f8501c0d27f8df9aae47136ebc59b7","modified":1525096196198},{"_id":"themes/next/source/images/cc-by-nd.svg","hash":"c563508ce9ced1e66948024ba1153400ac0e0621","modified":1525096196198},{"_id":"themes/next/source/images/cc-by-sa.svg","hash":"aa4742d733c8af8d38d4c183b8adbdcab045872e","modified":1525096196198},{"_id":"themes/next/source/images/cc-by.svg","hash":"28a0a4fe355a974a5e42f68031652b76798d4f7e","modified":1525096196198},{"_id":"themes/next/source/images/cc-zero.svg","hash":"87669bf8ac268a91d027a0a4802c92a1473e9030","modified":1525096196199},{"_id":"themes/next/source/images/favicon-16x16.png","hash":"943a0d67a9cdf8c198109b28f9dbd42f761d11c3","modified":1525096196199},{"_id":"themes/next/source/images/favicon-32x32.png","hash":"0749d7b24b0d2fae1c8eb7f671ad4646ee1894b1","modified":1525096196199},{"_id":"themes/next/source/images/loading.gif","hash":"5fbd472222feb8a22cf5b8aa5dc5b8e13af88e2b","modified":1525096196199},{"_id":"themes/next/source/images/logo.svg","hash":"d29cacbae1bdc4bbccb542107ee0524fe55ad6de","modified":1525096196199},{"_id":"themes/next/source/images/placeholder.gif","hash":"5fbd472222feb8a22cf5b8aa5dc5b8e13af88e2b","modified":1525096196199},{"_id":"themes/next/source/images/quote-l.svg","hash":"94e870b4c8c48da61d09522196d4dd40e277a98f","modified":1525096196199},{"_id":"themes/next/source/images/quote-r.svg","hash":"e60ae504f9d99b712c793c3740c6b100d057d4ec","modified":1525096196200},{"_id":"themes/next/source/images/searchicon.png","hash":"67727a6a969be0b2659b908518fa6706eed307b8","modified":1525096196200},{"_id":"source/_posts/MapReduce/allcondition.png","hash":"ea444b550b955959a85f873d5d39952f48926f55","modified":1525096196133},{"_id":"source/_posts/MapReduce/excution.png","hash":"e3b5cea1b5f926232a650c0c993c2a29def30f42","modified":1525096196134},{"_id":"themes/next/layout/_scripts/schemes/mist.swig","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1525096196172},{"_id":"themes/next/layout/_scripts/schemes/muse.swig","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1525096196172},{"_id":"themes/next/source/css/_mixins/Mist.styl","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1525096196191},{"_id":"themes/next/source/css/_mixins/Muse.styl","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1525096196191},{"_id":"themes/next/source/css/_mixins/custom.styl","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1525096196192},{"_id":"themes/next/source/css/_variables/Muse.styl","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1525096196196},{"_id":"themes/next/source/css/_variables/custom.styl","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1525096196196},{"_id":"themes/next/layout/_partials/head/external-fonts.swig","hash":"7ce76358411184482bb0934e70037949dd0da8ca","modified":1525096196168},{"_id":"themes/next/layout/_partials/head/custom-head.swig","hash":"9e1b9666efa77f4cf8d8261bcfa445a9ac608e53","modified":1525096196168},{"_id":"themes/next/layout/_partials/search/localsearch.swig","hash":"957701729b85fb0c5bfcf2fb99c19d54582f91ed","modified":1525096196169},{"_id":"themes/next/layout/_partials/search/swiftype.swig","hash":"959b7e04a96a5596056e4009b73b6489c117597e","modified":1525096196170},{"_id":"themes/next/layout/_partials/search/tinysou.swig","hash":"eefe2388ff3d424694045eda21346989b123977c","modified":1525096196170},{"_id":"themes/next/layout/_partials/share/add-this.swig","hash":"23e23dc0f76ef3c631f24c65277adf7ea517b383","modified":1525096196170},{"_id":"themes/next/layout/_partials/share/baidushare.swig","hash":"1f1107468aaf03f7d0dcd7eb2b653e2813a675b4","modified":1525096196170},{"_id":"themes/next/layout/_partials/share/duoshuo_share.swig","hash":"89c5a5240ecb223acfe1d12377df5562a943fd5d","modified":1525096196170},{"_id":"themes/next/layout/_partials/share/jiathis.swig","hash":"048fd5e98149469f8c28c21ba3561a7a67952c9b","modified":1525096196171},{"_id":"themes/next/layout/_scripts/pages/post-details.swig","hash":"069d1357c717572256e5cdee09574ebce529cbae","modified":1525096196171},{"_id":"themes/next/layout/_scripts/schemes/gemini.swig","hash":"a44acf9b0d0f44ef3dfc767376a95c984cc127de","modified":1525096196171},{"_id":"themes/next/layout/_scripts/schemes/pisces.swig","hash":"a44acf9b0d0f44ef3dfc767376a95c984cc127de","modified":1525096196172},{"_id":"themes/next/layout/_third-party/analytics/analytics-with-widget.swig","hash":"98df9d72e37dd071e882f2d5623c9d817815b139","modified":1525096196172},{"_id":"themes/next/layout/_third-party/analytics/application-insights.swig","hash":"60426bf73f8a89ba61fb1be2df3ad5398e32c4ef","modified":1525096196172},{"_id":"themes/next/layout/_third-party/analytics/baidu-analytics.swig","hash":"deda6a814ed48debc694c4e0c466f06c127163d0","modified":1525096196172},{"_id":"themes/next/layout/_third-party/analytics/cnzz-analytics.swig","hash":"8160b27bee0aa372c7dc7c8476c05bae57f58d0f","modified":1525096196173},{"_id":"themes/next/layout/_third-party/analytics/busuanzi-counter.swig","hash":"18e7bef8923d83ea42df6c97405e515a876cede4","modified":1525096196172},{"_id":"themes/next/layout/_third-party/analytics/google-analytics.swig","hash":"5d9943d74cc2e0a91badcf4f755c6de77eab193a","modified":1525096196173},{"_id":"themes/next/layout/_third-party/analytics/facebook-sdk.swig","hash":"a234c5cd1f75ca5731e814d0dbb92fdcf9240d1b","modified":1525096196173},{"_id":"themes/next/layout/_third-party/analytics/index.swig","hash":"5e9bb24c750b49513d9a65799e832f07410002ac","modified":1525096196173},{"_id":"themes/next/layout/_third-party/analytics/lean-analytics.swig","hash":"fc65b9c98a0a8ab43a5e7aabff6c5f03838e09c8","modified":1525096196173},{"_id":"themes/next/layout/_third-party/analytics/tencent-analytics.swig","hash":"3658414379e0e8a34c45c40feadc3edc8dc55f88","modified":1525096196173},{"_id":"themes/next/layout/_third-party/analytics/tencent-mta.swig","hash":"0ddc94ed4ba0c19627765fdf1abc4d8efbe53d5a","modified":1525096196174},{"_id":"themes/next/layout/_third-party/analytics/vkontakte-api.swig","hash":"c3971fd154d781088e1cc665035f8561a4098f4c","modified":1525096196174},{"_id":"themes/next/layout/_third-party/comments/changyan.swig","hash":"0e3378f7c39b2b0f69638290873ede6b6b6825c0","modified":1525096196174},{"_id":"themes/next/layout/_third-party/comments/disqus.swig","hash":"c316758546dc9ba6c60cb4d852c17ca6bb6d6724","modified":1525096196174},{"_id":"themes/next/layout/_third-party/comments/duoshuo.swig","hash":"a356b2185d40914447fde817eb3d358ab6b3e4c3","modified":1525096196174},{"_id":"themes/next/layout/_third-party/comments/hypercomments.swig","hash":"3e8dc5c6c912628a37e3b5f886bec7b2e5ed14ea","modified":1525096196175},{"_id":"themes/next/layout/_third-party/comments/index.swig","hash":"576e716893153a855eaf6d136fad7cb6d4065e09","modified":1525096196175},{"_id":"themes/next/layout/_third-party/comments/livere.swig","hash":"8a2e393d2e49f7bf560766d8a07cd461bf3fce4f","modified":1525096196175},{"_id":"themes/next/layout/_third-party/comments/valine.swig","hash":"9f4ed36c73e890909b8ebbe601fb60e13d048288","modified":1525096196175},{"_id":"themes/next/layout/_third-party/comments/youyan.swig","hash":"8b6650f77fe0a824c8075b2659e0403e0c78a705","modified":1525096196175},{"_id":"themes/next/layout/_third-party/search/index.swig","hash":"c747fb5c6b1f500e8f0c583e44195878b66e4e29","modified":1525096196176},{"_id":"themes/next/layout/_third-party/search/localsearch.swig","hash":"385c066af96bee30be2459dbec8aae1f15d382f5","modified":1525096196177},{"_id":"themes/next/layout/_third-party/search/tinysou.swig","hash":"cb3a5d36dbe1630bab84e03a52733a46df7c219b","modified":1525096196177},{"_id":"themes/next/layout/_third-party/seo/baidu-push.swig","hash":"c057b17f79e8261680fbae8dc4e81317a127c799","modified":1525096196177},{"_id":"themes/next/source/css/_custom/custom.styl","hash":"328d9a9696cc2ccf59c67d3c26000d569f46344c","modified":1525096196191},{"_id":"themes/next/source/css/_mixins/Gemini.styl","hash":"2aa5b7166a85a8aa34b17792ae4f58a5a96df6cc","modified":1525096196191},{"_id":"themes/next/source/css/_mixins/Pisces.styl","hash":"9ab65361ba0a12a986edd103e56492644c2db0b8","modified":1525096196191},{"_id":"themes/next/source/css/_mixins/base.styl","hash":"82f9055955920ed88a2ab6a20ab02169abb2c634","modified":1525096196192},{"_id":"themes/next/source/css/_variables/Gemini.styl","hash":"99fbb4686ea9a3e03a4726ed7cf4d8f529034452","modified":1525096196195},{"_id":"themes/next/source/css/_variables/Mist.styl","hash":"c8d35a6b9e3bff6d8fdb66de853065af9d37562d","modified":1525096196195},{"_id":"themes/next/source/css/_variables/Pisces.styl","hash":"ec79c23f1956bade7bcaa7189d97b7463b8f9f75","modified":1525096196196},{"_id":"themes/next/source/css/_variables/base.styl","hash":"4df88b33f4bd31b872b5c842405267256024f75a","modified":1525096196196},{"_id":"themes/next/source/js/src/affix.js","hash":"978e0422b5bf1b560236d8d10ebc1adcf66392e3","modified":1525096196200},{"_id":"themes/next/source/js/src/algolia-search.js","hash":"b172f697ed339a24b1e80261075232978d164c35","modified":1525096196200},{"_id":"themes/next/source/js/src/bootstrap.js","hash":"034bc8113e0966fe2096ba5b56061bbf10ef0512","modified":1525096196200},{"_id":"themes/next/source/js/src/exturl.js","hash":"e42e2aaab7bf4c19a0c8e779140e079c6aa5c0b1","modified":1525096196200},{"_id":"themes/next/source/js/src/hook-duoshuo.js","hash":"a6119070c0119f33e08b29da7d2cce2635eb40a0","modified":1525096196200},{"_id":"themes/next/source/js/src/js.cookie.js","hash":"9b37973a90fd50e71ea91682265715e45ae82c75","modified":1525096196201},{"_id":"themes/next/source/js/src/motion.js","hash":"754b294394f102c8fd9423a1789ddb1201677898","modified":1525096196201},{"_id":"themes/next/source/js/src/post-details.js","hash":"a13f45f7aa8291cf7244ec5ba93907d119c5dbdd","modified":1525096196201},{"_id":"themes/next/source/js/src/scroll-cookie.js","hash":"09dc828cbf5f31158ff6250d2bf7c3cde6365c67","modified":1525096196201},{"_id":"themes/next/source/js/src/scrollspy.js","hash":"fe4da1b9fe73518226446f5f27d2831e4426fc35","modified":1525096196201},{"_id":"themes/next/source/js/src/utils.js","hash":"6b0eeeb9dda4a7c94c1c4f6fafd2c801da6e8f96","modified":1525096196201},{"_id":"themes/next/source/lib/algolia-instant-search/instantsearch.min.css","hash":"90ef19edc982645b118b095615838d9c5eaba0de","modified":1525096196204},{"_id":"themes/next/source/lib/canvas-nest/canvas-nest.min.js","hash":"0387e75e23b1db108a755073fe52a0d03eb391a7","modified":1525096196207},{"_id":"themes/next/source/lib/canvas-ribbon/canvas-ribbon.js","hash":"7fd2f3e2773555392ef40df40cae3bedb884f17a","modified":1525096196207},{"_id":"themes/next/source/lib/fastclick/.bower.json","hash":"93ebd5b35e632f714dcf1753e1f6db77ec74449b","modified":1525096196210},{"_id":"themes/next/source/lib/fastclick/LICENSE","hash":"dcd5b6b43095d9e90353a28b09cb269de8d4838e","modified":1525096196210},{"_id":"themes/next/source/lib/fastclick/README.md","hash":"1decd8e1adad2cd6db0ab50cf56de6035156f4ea","modified":1525096196211},{"_id":"themes/next/source/lib/fastclick/bower.json","hash":"13379463c7463b4b96d13556b46faa4cc38d81e6","modified":1525096196211},{"_id":"themes/next/source/lib/font-awesome/.bower.json","hash":"a2aaaf12378db56bd10596ba3daae30950eac051","modified":1525096196211},{"_id":"themes/next/source/lib/font-awesome/.gitignore","hash":"69d152fa46b517141ec3b1114dd6134724494d83","modified":1525096196211},{"_id":"themes/next/source/lib/font-awesome/.npmignore","hash":"dcf470ab3a358103bb896a539cc03caeda10fa8b","modified":1525096196212},{"_id":"themes/next/source/lib/font-awesome/HELP-US-OUT.txt","hash":"4f7bf961f1bed448f6ba99aeb9219fabf930ba96","modified":1525096196212},{"_id":"themes/next/source/lib/font-awesome/bower.json","hash":"279a8a718ab6c930a67c41237f0aac166c1b9440","modified":1525096196212},{"_id":"themes/next/source/lib/jquery/.bower.json","hash":"91745c2cc6c946c7275f952b2b0760b880cea69e","modified":1525096196217},{"_id":"themes/next/source/lib/jquery_lazyload/.bower.json","hash":"b7638afc93e9cd350d0783565ee9a7da6805ad8e","modified":1525096196217},{"_id":"themes/next/source/lib/jquery_lazyload/CONTRIBUTING.md","hash":"4891864c24c28efecd81a6a8d3f261145190f901","modified":1525096196217},{"_id":"themes/next/source/lib/jquery_lazyload/README.md","hash":"895d50fa29759af7835256522e9dd7dac597765c","modified":1525096196218},{"_id":"themes/next/source/lib/jquery_lazyload/bower.json","hash":"65bc85d12197e71c40a55c0cd7f6823995a05222","modified":1525096196218},{"_id":"themes/next/source/lib/jquery_lazyload/jquery.lazyload.js","hash":"481fd478650e12b67c201a0ea41e92743f8b45a3","modified":1525096196218},{"_id":"themes/next/source/lib/jquery_lazyload/jquery.scrollstop.js","hash":"0e9a81785a011c98be5ea821a8ed7d411818cfd1","modified":1525096196218},{"_id":"themes/next/source/lib/pace/pace-theme-barber-shop.min.css","hash":"ee0d51446cb4ffe1bb96bd7bc8c8e046dddfcf46","modified":1525096196218},{"_id":"themes/next/source/lib/pace/pace-theme-big-counter.min.css","hash":"5b561dc328af4c4d512e20a76fe964d113a32ba8","modified":1525096196218},{"_id":"themes/next/source/lib/pace/pace-theme-bounce.min.css","hash":"f6bdb9a785b7979dd8ec5c60e278af955ef1e585","modified":1525096196219},{"_id":"themes/next/source/lib/pace/pace-theme-center-atom.min.css","hash":"dcf79c24fe5350fb73d8038573a104e73639e9d3","modified":1525096196219},{"_id":"themes/next/source/lib/pace/pace-theme-center-circle.min.css","hash":"a4066769c78affbfbc5e30a600e2c7862cd532e0","modified":1525096196219},{"_id":"themes/next/source/lib/pace/pace-theme-center-radar.min.css","hash":"ab7cba998bf4c03b13df342bf43647fa4f419783","modified":1525096196219},{"_id":"themes/next/source/lib/pace/pace-theme-center-simple.min.css","hash":"67f44c947548bd4d77e7590d3f59e236cbf9e98a","modified":1525096196219},{"_id":"themes/next/source/lib/pace/pace-theme-corner-indicator.min.css","hash":"b3c64c973f31884e3d8145989476707333406b9a","modified":1525096196219},{"_id":"themes/next/source/lib/pace/pace-theme-fill-left.min.css","hash":"0bec1e235a4a2cccda3f993b205424e1441a44ae","modified":1525096196219},{"_id":"themes/next/source/lib/pace/pace-theme-flash.min.css","hash":"13ace22c40312d7bbd8d9c1e50eff897a7a497d8","modified":1525096196219},{"_id":"themes/next/source/lib/pace/pace-theme-loading-bar.min.css","hash":"7ee28875dfc1230d76c537f6605766e8d4011e9f","modified":1525096196219},{"_id":"themes/next/source/lib/pace/pace-theme-mac-osx.min.css","hash":"9f2e7b51b084da407863826b25265b31150b3821","modified":1525096196220},{"_id":"themes/next/source/lib/pace/pace-theme-minimal.min.css","hash":"9cd783cceb8a191f3c8b5d81f7a430ecc3e489d3","modified":1525096196220},{"_id":"themes/next/source/lib/pace/pace.min.js","hash":"9944dfb7814b911090e96446cea4d36e2b487234","modified":1525096196220},{"_id":"themes/next/source/lib/three/canvas_lines.min.js","hash":"dce4a3b65f8bf958f973690caa7ec4952f353b0c","modified":1525096196220},{"_id":"themes/next/source/lib/three/canvas_sphere.min.js","hash":"d8ea241a53c135a650f7335d2b6982b899fd58a9","modified":1525096196220},{"_id":"themes/next/source/lib/three/three-waves.min.js","hash":"d968cba6b3a50b3626a02d67b544f349d83b147c","modified":1525096196221},{"_id":"themes/next/source/lib/velocity/.bower.json","hash":"05f960846f1c7a93dab1d3f9a1121e86812e8c88","modified":1525096196223},{"_id":"themes/next/source/lib/velocity/bower.json","hash":"2ec99573e84c7117368beccb9e94b6bf35d2db03","modified":1525096196223},{"_id":"themes/next/source/lib/velocity/velocity.min.js","hash":"2f1afadc12e4cf59ef3b405308d21baa97e739c6","modified":1525096196225},{"_id":"themes/next/source/lib/velocity/velocity.ui.js","hash":"6a1d101eab3de87527bb54fcc8c7b36b79d8f0df","modified":1525096196225},{"_id":"themes/next/source/lib/velocity/velocity.ui.min.js","hash":"ed5e534cd680a25d8d14429af824f38a2c7d9908","modified":1525096196225},{"_id":"themes/next/source/lib/jquery/index.js","hash":"41b4bfbaa96be6d1440db6e78004ade1c134e276","modified":1525096196217},{"_id":"themes/next/layout/_third-party/search/algolia-search/assets.swig","hash":"28ff4ed6714c59124569ffcbd10f1173d53ca923","modified":1525096196176},{"_id":"themes/next/layout/_third-party/search/algolia-search/dom.swig","hash":"ba698f49dd3a868c95b240d802f5b1b24ff287e4","modified":1525096196176},{"_id":"themes/next/source/css/_common/components/back-to-top-sidebar.styl","hash":"4719ce717962663c5c33ef97b1119a0b3a4ecdc3","modified":1525096196180},{"_id":"themes/next/source/css/_common/components/back-to-top.styl","hash":"31050fc7a25784805b4843550151c93bfa55c9c8","modified":1525096196180},{"_id":"themes/next/source/css/_common/components/buttons.styl","hash":"0dfb4b3ba3180d7285e66f270e1d3fa0f132c3d2","modified":1525096196181},{"_id":"themes/next/source/css/_common/components/comments.styl","hash":"471f1627891aca5c0e1973e09fbcb01e1510d193","modified":1525096196181},{"_id":"themes/next/source/css/_common/components/components.styl","hash":"a6bb5256be6195e76addbda12f4ed7c662d65e7a","modified":1525096196181},{"_id":"themes/next/source/css/_common/components/pagination.styl","hash":"c5d48863f332ff8ce7c88dec2c893f709d7331d3","modified":1525096196184},{"_id":"themes/next/source/css/_common/components/tag-cloud.styl","hash":"dd8a3b22fc2f222ac6e6c05bd8a773fb039169c0","modified":1525096196187},{"_id":"themes/next/source/css/_common/outline/outline.styl","hash":"2186be20e317505cd31886f1291429cc21f76703","modified":1525096196190},{"_id":"themes/next/source/css/_common/scaffolding/base.styl","hash":"f7c44b0ee46cf2cf82a4c9455ba8d8b55299976f","modified":1525096196190},{"_id":"themes/next/source/css/_common/scaffolding/helpers.styl","hash":"9c25c75311e1bd4d68df031d3f2ae6d141a90766","modified":1525096196190},{"_id":"themes/next/source/css/_common/scaffolding/mobile.styl","hash":"47a46583a1f3731157a3f53f80ed1ed5e2753e8e","modified":1525096196190},{"_id":"themes/next/source/css/_common/scaffolding/scaffolding.styl","hash":"a280a583b7615e939aaddbf778f5c108ef8a2a6c","modified":1525096196191},{"_id":"themes/next/source/css/_common/scaffolding/tables.styl","hash":"64f5d56c08d74a338813df1265580ca0cbf0190b","modified":1525096196191},{"_id":"themes/next/source/css/_schemes/Mist/_base.styl","hash":"c2d079788d6fc2e9a191ccdae94e50d55bf849dc","modified":1525096196192},{"_id":"themes/next/source/css/_schemes/Mist/_header.styl","hash":"5ae7906dc7c1d9468c7f4b4a6feddddc555797a1","modified":1525096196192},{"_id":"themes/next/source/css/_schemes/Mist/_logo.styl","hash":"38e5df90c8689a71c978fd83ba74af3d4e4e5386","modified":1525096196192},{"_id":"themes/next/source/css/_schemes/Mist/_menu.styl","hash":"b0dcca862cd0cc6e732e33d975b476d744911742","modified":1525096196193},{"_id":"themes/next/source/css/_schemes/Mist/_posts-expanded.styl","hash":"5e6c9f8a730b78c7ce5572d327c2a7311c3609b9","modified":1525096196193},{"_id":"themes/next/source/css/_schemes/Mist/_search.styl","hash":"1452cbe674cc1d008e1e9640eb4283841058fc64","modified":1525096196193},{"_id":"themes/next/source/css/_schemes/Mist/index.styl","hash":"9a5581a770af8964064fef7afd3e16963e45547f","modified":1525096196193},{"_id":"themes/next/source/css/_schemes/Gemini/index.styl","hash":"18c3336ee3d09bd2da6a876e1336539f03d5a973","modified":1525096196192},{"_id":"themes/next/source/css/_schemes/Muse/_layout.styl","hash":"0efa036a15c18f5abb058b7c0fad1dd9ac5eed4c","modified":1525096196194},{"_id":"themes/next/source/css/_schemes/Muse/_logo.styl","hash":"8829bc556ca38bfec4add4f15a2f028092ac6d46","modified":1525096196194},{"_id":"themes/next/source/css/_schemes/Muse/_menu.styl","hash":"82bbaa6322764779a1ac2e2c8390ce901c7972e2","modified":1525096196194},{"_id":"themes/next/source/css/_schemes/Muse/_search.styl","hash":"1452cbe674cc1d008e1e9640eb4283841058fc64","modified":1525096196194},{"_id":"themes/next/source/css/_schemes/Muse/index.styl","hash":"a0e2030a606c934fb2c5c7373aaae04a1caac4c5","modified":1525096196194},{"_id":"themes/next/source/css/_schemes/Pisces/_brand.styl","hash":"c4ed249798296f60bda02351fe6404fb3ef2126f","modified":1525096196194},{"_id":"themes/next/source/css/_schemes/Pisces/_layout.styl","hash":"5b93958239d3d2bf9aeaede44eced2434d784462","modified":1525096196195},{"_id":"themes/next/source/css/_schemes/Pisces/_menu.styl","hash":"215de948be49bcf14f06d500cef9f7035e406a43","modified":1525096196195},{"_id":"themes/next/source/css/_schemes/Pisces/_posts.styl","hash":"2f878213cb24c5ddc18877f6d15ec5c5f57745ac","modified":1525096196195},{"_id":"themes/next/source/css/_schemes/Pisces/_sidebar.styl","hash":"f3991aeca25d0814f5cea800b58f25d0222cd246","modified":1525096196195},{"_id":"themes/next/source/css/_schemes/Pisces/index.styl","hash":"69ecd6c97e7cdfd822ac8102b45ad0ede85050db","modified":1525096196195},{"_id":"themes/next/source/js/src/schemes/pisces.js","hash":"8050a5b2683d1d77238c5762b6bd89c543daed6e","modified":1525096196201},{"_id":"themes/next/source/lib/Han/dist/han.min.css","hash":"a0c9e32549a8b8cf327ab9227b037f323cdb60ee","modified":1525096196203},{"_id":"themes/next/source/lib/Han/dist/han.css","hash":"bd40da3fba8735df5850956814e312bd7b3193d7","modified":1525096196203},{"_id":"themes/next/source/lib/Han/dist/han.min.js","hash":"f559c68a25065a14f47da954a7617d87263e409d","modified":1525096196203},{"_id":"themes/next/source/lib/fancybox/source/blank.gif","hash":"2daeaa8b5f19f0bc209d976c02bd6acb51b00b0a","modified":1525096196207},{"_id":"themes/next/source/lib/fancybox/source/[email protected]","hash":"273b123496a42ba45c3416adb027cd99745058b0","modified":1525096196207},{"_id":"themes/next/source/lib/fancybox/source/fancybox_loading.gif","hash":"1a755fb2599f3a313cc6cfdb14df043f8c14a99c","modified":1525096196207},{"_id":"themes/next/source/lib/fancybox/source/fancybox_overlay.png","hash":"b3a4ee645ba494f52840ef8412015ba0f465dbe0","modified":1525096196208},{"_id":"themes/next/source/lib/fancybox/source/fancybox_sprite.png","hash":"17df19f97628e77be09c352bf27425faea248251","modified":1525096196208},{"_id":"themes/next/source/lib/fancybox/source/[email protected]","hash":"30c58913f327e28f466a00f4c1ac8001b560aed8","modified":1525096196208},{"_id":"themes/next/source/lib/fancybox/source/jquery.fancybox.css","hash":"5f163444617b6cf267342f06ac166a237bb62df9","modified":1525096196210},{"_id":"themes/next/source/lib/fancybox/source/jquery.fancybox.js","hash":"1cf3d47b5ccb7cb6e9019c64f2a88d03a64853e4","modified":1525096196210},{"_id":"themes/next/source/lib/fancybox/source/jquery.fancybox.pack.js","hash":"53360764b429c212f424399384417ccc233bb3be","modified":1525096196210},{"_id":"themes/next/source/lib/fastclick/lib/fastclick.js","hash":"06cef196733a710e77ad7e386ced6963f092dc55","modified":1525096196211},{"_id":"themes/next/source/lib/fastclick/lib/fastclick.min.js","hash":"2cae0f5a6c5d6f3cb993015e6863f9483fc4de18","modified":1525096196211},{"_id":"themes/next/source/lib/font-awesome/css/font-awesome.css","hash":"0140952c64e3f2b74ef64e050f2fe86eab6624c8","modified":1525096196212},{"_id":"themes/next/source/lib/font-awesome/css/font-awesome.css.map","hash":"0189d278706509412bac4745f96c83984e1d59f4","modified":1525096196212},{"_id":"themes/next/source/lib/font-awesome/css/font-awesome.min.css","hash":"512c7d79033e3028a9be61b540cf1a6870c896f8","modified":1525096196213},{"_id":"themes/next/source/lib/ua-parser-js/dist/ua-parser.min.js","hash":"38628e75e4412cc6f11074e03e1c6d257aae495b","modified":1525096196223},{"_id":"themes/next/source/lib/ua-parser-js/dist/ua-parser.pack.js","hash":"214dad442a92d36af77ed0ca1d9092b16687f02f","modified":1525096196223},{"_id":"themes/next/source/css/_common/scaffolding/normalize.styl","hash":"ece571f38180febaf02ace8187ead8318a300ea7","modified":1525096196191},{"_id":"themes/next/source/lib/font-awesome/fonts/fontawesome-webfont.woff","hash":"28b782240b3e76db824e12c02754a9731a167527","modified":1525096196216},{"_id":"themes/next/source/lib/velocity/velocity.js","hash":"9f08181baea0cc0e906703b7e5df9111b9ef3373","modified":1525096196224},{"_id":"themes/next/source/css/_common/components/footer/footer.styl","hash":"7905a7f625702b45645d8be1268cb8af3f698c70","modified":1525096196181},{"_id":"themes/next/source/css/_common/components/header/headerband.styl","hash":"d27448f199fc2f9980b601bc22b87f08b5d64dd1","modified":1525096196182},{"_id":"themes/next/source/css/_common/components/header/header.styl","hash":"ae1ca14e51de67b07dba8f61ec79ee0e2e344574","modified":1525096196182},{"_id":"themes/next/source/css/_common/components/header/menu.styl","hash":"8a2421cb9005352905fae9d41a847ae56957247e","modified":1525096196182},{"_id":"themes/next/source/css/_common/components/header/site-meta.styl","hash":"6c00f6e0978f4d8f9a846a15579963728aaa6a17","modified":1525096196182},{"_id":"themes/next/source/css/_common/components/header/site-nav.styl","hash":"49c2b2c14a1e7fcc810c6be4b632975d0204c281","modified":1525096196183},{"_id":"themes/next/source/css/_common/components/highlight/diff.styl","hash":"96f32ea6c3265a3889e6abe57587f6e2a2a40dfb","modified":1525096196183},{"_id":"themes/next/source/css/_common/components/highlight/highlight.styl","hash":"25dc25f61a232f03ca72472b7852f882448ec185","modified":1525096196183},{"_id":"themes/next/source/css/_common/components/highlight/theme.styl","hash":"b76387934fb6bb75212b23c1a194486892cc495e","modified":1525096196183},{"_id":"themes/next/source/css/_common/components/pages/archive.styl","hash":"f5aa2ba3bfffc15475e7e72a55b5c9d18609fdf5","modified":1525096196183},{"_id":"themes/next/source/css/_common/components/pages/categories.styl","hash":"4eff5b252d7b614e500fc7d52c97ce325e57d3ab","modified":1525096196183},{"_id":"themes/next/source/css/_common/components/pages/pages.styl","hash":"2039590632bba3943c39319d80ef630af7928185","modified":1525096196184},{"_id":"themes/next/source/css/_common/components/pages/post-detail.styl","hash":"9bf4362a4d0ae151ada84b219d39fbe5bb8c790e","modified":1525096196184},{"_id":"themes/next/source/css/_common/components/pages/schedule.styl","hash":"a82afbb72d83ee394aedc7b37ac0008a9823b4f4","modified":1525096196184},{"_id":"themes/next/source/css/_common/components/post/post-button.styl","hash":"f958da71d211f592ca64d0cf1328d801fffd3179","modified":1525096196184},{"_id":"themes/next/source/css/_common/components/post/post-collapse.styl","hash":"0f7f522cc6bfb3401d5afd62b0fcdf48bb2d604b","modified":1525096196184},{"_id":"themes/next/source/css/_common/components/post/post-copyright.styl","hash":"f54367c0feda6986c030cc4d15a0ca6ceea14bcb","modified":1525096196184},{"_id":"themes/next/source/css/_common/components/post/post-eof.styl","hash":"2cdc094ecf907a02fce25ad4a607cd5c40da0f2b","modified":1525096196185},{"_id":"themes/next/source/css/_common/components/post/post-expand.styl","hash":"535b3b4f8cb1eec2558e094320e7dfb01f94c0e7","modified":1525096196185},{"_id":"themes/next/source/css/_common/components/post/post-gallery.styl","hash":"387ce23bba52b22a586b2dfb4ec618fe1ffd3926","modified":1525096196185},{"_id":"themes/next/source/css/_common/components/post/post-meta.styl","hash":"aea21141015ca8c409d8b33e3e34ec505f464e93","modified":1525096196185},{"_id":"themes/next/source/css/_common/components/post/post-nav.styl","hash":"a5d8617a24d7cb6c5ad91ea621183ca2c0917331","modified":1525096196185},{"_id":"themes/next/source/css/_common/components/post/post-reward.styl","hash":"36332c8a91f089f545f3c3e8ea90d08aa4d6e60c","modified":1525096196185},{"_id":"themes/next/source/css/_common/components/post/post-rtl.styl","hash":"017074ef58166e2d69c53bb7590a0e7a8947a1ed","modified":1525096196185},{"_id":"themes/next/source/css/_common/components/post/post-title.styl","hash":"d5a4e4fc17f1f7e7c3a61b52d8e2e9677e139de7","modified":1525096196186},{"_id":"themes/next/source/css/_common/components/post/post-tags.styl","hash":"a352ae5b1f8857393bf770d2e638bf15f0c9585d","modified":1525096196185},{"_id":"themes/next/source/css/_common/components/post/post-type.styl","hash":"10251257aceecb117233c9554dcf8ecfef8e2104","modified":1525096196186},{"_id":"themes/next/source/css/_common/components/post/post-widgets.styl","hash":"08a500b2984f109b751f3697ca33172d1340591a","modified":1525096196186},{"_id":"themes/next/source/css/_common/components/post/post.styl","hash":"fb0d3ae0f0c26393199de8f81fb3658d86fbbfaf","modified":1525096196186},{"_id":"themes/next/source/css/_common/components/sidebar/sidebar-author-links.styl","hash":"0a6c0efffdf18bddbc1d1238feaed282b09cd0fe","modified":1525096196186},{"_id":"themes/next/source/css/_common/components/sidebar/sidebar-author.styl","hash":"920343e41c124221a17f050bbb989494d44f7a24","modified":1525096196186},{"_id":"themes/next/source/css/_common/components/sidebar/sidebar-blogroll.styl","hash":"89dd4f8b1f1cce3ad46cf2256038472712387d02","modified":1525096196186},{"_id":"themes/next/source/css/_common/components/sidebar/sidebar-dimmer.styl","hash":"efa5e5022e205b52786ce495d4879f5e7b8f84b2","modified":1525096196186},{"_id":"themes/next/source/css/_common/components/sidebar/sidebar-feed-link.styl","hash":"9486ddd2cb255227db102d09a7df4cae0fabad72","modified":1525096196187},{"_id":"themes/next/source/css/_common/components/sidebar/sidebar-nav.styl","hash":"45fa7193435a8eae9960267438750b4c9fa9587f","modified":1525096196187},{"_id":"themes/next/source/css/_common/components/sidebar/sidebar-toc.styl","hash":"77c92a449ce84d558d26d052681f2e0dd77c70c9","modified":1525096196187},{"_id":"themes/next/source/css/_common/components/sidebar/sidebar-toggle.styl","hash":"f7784aba0c1cd20d824c918c120012d57a5eaa2a","modified":1525096196187},{"_id":"themes/next/source/css/_common/components/sidebar/site-state.styl","hash":"3623e7fa4324ec1307370f33d8f287a9e20a5578","modified":1525096196187},{"_id":"themes/next/source/css/_common/components/sidebar/sidebar.styl","hash":"50305b6ad7d09d2ffa4854e39f41ec1f4fe984fd","modified":1525096196187},{"_id":"themes/next/source/css/_common/components/tags/blockquote-center.styl","hash":"c2abe4d87148e23e15d49ee225bc650de60baf46","modified":1525096196187},{"_id":"themes/next/source/css/_common/components/tags/exturl.styl","hash":"1b3cc9f4e5a7f6e05b4100e9990b37b20d4a2005","modified":1525096196188},{"_id":"themes/next/source/css/_common/components/tags/full-image.styl","hash":"b8969e1654eec89a0fd10d88b337fee9cb03cd44","modified":1525096196188},{"_id":"themes/next/source/css/_common/components/tags/group-pictures.styl","hash":"4851b981020c5cbc354a1af9b831a2dcb3cf9d39","modified":1525096196188},{"_id":"themes/next/source/css/_common/components/tags/label.styl","hash":"4a457d265d62f287c63d48764ce45d9bcfc9ec5a","modified":1525096196188},{"_id":"themes/next/source/css/_common/components/tags/note-modern.styl","hash":"45df0cf4c97b47e05573bcd41028ee50f3fdf432","modified":1525096196188},{"_id":"themes/next/source/css/_common/components/tags/tabs.styl","hash":"4ab5deed8c3b0c338212380f678f8382672e1bcb","modified":1525096196188},{"_id":"themes/next/source/css/_common/components/tags/note.styl","hash":"32c9156bea5bac9e9ad0b4c08ffbca8b3d9aac4b","modified":1525096196188},{"_id":"themes/next/source/css/_common/components/tags/tags.styl","hash":"ead0d0f2321dc71505788c7f689f92257cf14947","modified":1525096196188},{"_id":"themes/next/source/css/_common/components/third-party/algolia-search.styl","hash":"fd42777b9125fd8969dc39d4f15473e2b91b4142","modified":1525096196189},{"_id":"themes/next/source/css/_common/components/third-party/baidushare.styl","hash":"93b08815c4d17e2b96fef8530ec1f1064dede6ef","modified":1525096196189},{"_id":"themes/next/source/css/_common/components/third-party/busuanzi-counter.styl","hash":"d4e6d8d7b34dc69994593c208f875ae8f7e8a3ae","modified":1525096196189},{"_id":"themes/next/source/css/_common/components/third-party/duoshuo.styl","hash":"2340dd9b3202c61d73cc708b790fac5adddbfc7f","modified":1525096196189},{"_id":"themes/next/source/css/_common/components/third-party/han.styl","hash":"cce6772e2cdb4db85d35486ae4c6c59367fbdd40","modified":1525096196189},{"_id":"themes/next/source/css/_common/components/third-party/jiathis.styl","hash":"327b5f63d55ec26f7663185c1a778440588d9803","modified":1525096196189},{"_id":"themes/next/source/css/_common/components/third-party/localsearch.styl","hash":"d89c4b562b528e4746696b2ad8935764d133bdae","modified":1525096196190},{"_id":"themes/next/source/css/_common/components/third-party/third-party.styl","hash":"aeff0e6e23725e8baea27c890ccbbf466024f767","modified":1525096196190},{"_id":"themes/next/source/css/_schemes/Mist/outline/outline.styl","hash":"5dc4859c66305f871e56cba78f64bfe3bf1b5f01","modified":1525096196193},{"_id":"themes/next/source/css/_schemes/Mist/sidebar/sidebar-blogroll.styl","hash":"817587e46df49e819858c8ecbafa08b53d5ff040","modified":1525096196193},{"_id":"themes/next/source/css/_schemes/Muse/sidebar/sidebar-blogroll.styl","hash":"817587e46df49e819858c8ecbafa08b53d5ff040","modified":1525096196194},{"_id":"themes/next/source/lib/Han/dist/font/han-space.otf","hash":"07436f011b44051f61b8329c99de4bec64e86f4b","modified":1525096196202},{"_id":"themes/next/source/lib/Han/dist/font/han-space.woff","hash":"7a635062b10bf5662ae1d218ba0980171005d060","modified":1525096196202},{"_id":"themes/next/source/lib/Han/dist/font/han.woff","hash":"f38ff9b2eecaa17b50b66aa2dae87e9e7436d195","modified":1525096196202},{"_id":"themes/next/source/lib/Han/dist/font/han.otf","hash":"f1f6bb8f461f5672e000380195d3d2358a28494c","modified":1525096196202},{"_id":"themes/next/source/lib/Han/dist/font/han.woff2","hash":"623af3ed5423371ac136a4fe0e8cc7bb7396037a","modified":1525096196202},{"_id":"themes/next/source/lib/Han/dist/han.js","hash":"e345397e0585c9fed1449e614ec13e0224acf2ab","modified":1525096196203},{"_id":"themes/next/source/lib/fancybox/source/helpers/fancybox_buttons.png","hash":"e385b139516c6813dcd64b8fc431c364ceafe5f3","modified":1525096196208},{"_id":"themes/next/source/lib/fancybox/source/helpers/jquery.fancybox-buttons.css","hash":"1a9d8e5c22b371fcc69d4dbbb823d9c39f04c0c8","modified":1525096196209},{"_id":"themes/next/source/lib/fancybox/source/helpers/jquery.fancybox-buttons.js","hash":"91e41741c2e93f732c82aaacec4cfc6e3f3ec876","modified":1525096196209},{"_id":"themes/next/source/lib/fancybox/source/helpers/jquery.fancybox-media.js","hash":"3bdf69ed2469e4fb57f5a95f17300eef891ff90d","modified":1525096196209},{"_id":"themes/next/source/lib/fancybox/source/helpers/jquery.fancybox-thumbs.css","hash":"4ac329c16a5277592fc12a37cca3d72ca4ec292f","modified":1525096196209},{"_id":"themes/next/source/lib/fancybox/source/helpers/jquery.fancybox-thumbs.js","hash":"53e194f4a72e649c04fb586dd57762b8c022800b","modified":1525096196209},{"_id":"themes/next/source/lib/font-awesome/fonts/FontAwesome.otf","hash":"048707bc52ac4b6563aaa383bfe8660a0ddc908c","modified":1525096196213},{"_id":"themes/next/source/lib/font-awesome/fonts/fontawesome-webfont.woff2","hash":"d6f48cba7d076fb6f2fd6ba993a75b9dc1ecbf0c","modified":1525096196216},{"_id":"themes/next/source/lib/font-awesome/fonts/fontawesome-webfont.eot","hash":"d980c2ce873dc43af460d4d572d441304499f400","modified":1525096196214},{"_id":"themes/next/source/lib/font-awesome/fonts/fontawesome-webfont.ttf","hash":"13b1eab65a983c7a73bc7997c479d66943f7c6cb","modified":1525096196216},{"_id":"themes/next/source/lib/algolia-instant-search/instantsearch.min.js","hash":"9ccc6f8144f54e86df9a3fd33a18368d81cf3a4f","modified":1525096196206},{"_id":"themes/next/source/lib/three/three.min.js","hash":"73f4cdc17e51a72b9bf5b9291f65386d615c483b","modified":1525096196223},{"_id":"themes/next/source/lib/font-awesome/fonts/fontawesome-webfont.svg","hash":"98a8aa5cf7d62c2eff5f07ede8d844b874ef06ed","modified":1525096196215}],"Category":[{"name":"分布式系统","_id":"cjyohfvd30004sy0yntd1yvpc"},{"name":"MIT6.824","parent":"cjyohfvd30004sy0yntd1yvpc","_id":"cjyohfvdb000dsy0ye1hbyfmr"},{"name":"kubernetes","parent":"cjyohfvd30004sy0yntd1yvpc","_id":"cjyohfvdc000gsy0y2kr2pv78"},{"name":"LeetCode","_id":"cjyohfve0000psy0ys3e6rq9n"},{"name":"MIT 6.824","parent":"cjyohfvd30004sy0yntd1yvpc","_id":"cjyohfve1000ssy0yjqd52juo"},{"name":"golang","_id":"cjyohfve2000wsy0ygx6is5uk"}],"Data":[],"Page":[{"title":"categories","date":"2017-10-19T06:16:14.000Z","type":"categories","_content":"","source":"categories/index.md","raw":"---\ntitle: categories\ndate: 2017-10-19 14:16:14\ntype: \"categories\"\n---\n","updated":"2018-04-30T13:49:56.138Z","path":"categories/index.html","comments":1,"layout":"page","_id":"cjyohfvcv0000sy0ypqwrk9u5","content":"","site":{"data":{}},"excerpt":"","more":""},{"title":"tags","type":"tags","date":"2017-10-19T06:15:50.000Z","_content":"","source":"tags/index.md","raw":"---\ntitle: tags\ntype: \"tags\"\ndate: 2017-10-19 14:15:50\n---\n","updated":"2018-04-30T13:49:56.138Z","path":"tags/index.html","comments":1,"layout":"page","_id":"cjyohfvd00002sy0ygtgvg1rf","content":"","site":{"data":{}},"excerpt":"","more":""}],"Post":[{"title":"MapReduce","date":"2017-10-30T13:32:06.000Z","_content":"最近读了一下经典的MapReduce这篇论文,大概的了解了一下整个模型的处理流程和用户的编程的方法,以及处理过程的优化和差错处理,下面我来谈谈我对论文的理解。网上有的更多的是使用MapReduce,而我着重对原理的理解。\n并且,我也跟着MIT6.824的实验实验了一个简单的MapReduce模型,采用多线程来模拟分布式,最终实现了词频统计和倒排序索引\n\n\n## 编程模型\n\n整个`mapreduce`分成两个部分,包括`map`和`reduce`部分,其中map的输入为`key/value pairs`,输出为`intermediate key/value pairs`,而`reduce`接受 `key`和所对应的`values`将其合并。简单的过程如下所示:\n\n`map\t\t\t(k1,v1)\t\t\t->\t\tlist(k2,v2)`\n`reduce\t\t(k2,list(v2))\t->\t\tlist(v2)`\n\n\n## 执行过程\n整个模型的执行过程比较明了,当处理到`mapreduce`部分时,`master`开始统领整个过程,将输入内容分割并分配给M台机器进行Map的工作,结束后每个Map都将中间值分成R个分区`hash(key)%R`,R个机器读取完每个中间文件的相应的分区后进行Reduce的工作最后输出结果并合并。整个过程如下图所示\n{% asset_image excution.png %}\n\n\n\n### map\n1. MapReduce Library 将用户的输入文件分成M块,每一块的大小为16-64MB可以由用户指定,然后将用户的代码复制到集群的各个机器上\n2. master指定 M map 任务,R reduce 任务,并挑选空闲的机器执行\n3. map 将输入的内容转化成 intermediate key-value pairs ,并存储在自己的内存当中\n4. 周期性的将内存中的键值对写入本机的磁盘中并分割成R个区,可以使用`hash(key)%R`处理,并将在磁盘中的存储位置返回给master\n\n\n\n### reduce\n5. reduce 利用**网络**从master所给的地址中读取完所有的内容后,按照key将结果进行**排序**,如果内容过大可以使用外部排序\n6. 当reduce 遍历完成后,将 key 和对应的 value序列传递给用户指定的reduce程序\n7. 当所有的任务都执行完成后,mapreduce 返回用户程序代码\n\n\n\n\n## 容错处理\nMapreduce 编程模型是建立在一个计算机的集群上的,需要考虑主机失效的情况,类似与GFS。这一点也是十分重要的,考虑了实际情况中,集群往往采用了一些低性能,被淘汰的电脑,计算机失效的情况十分的常见。其中失效分成了两种情况:1.Worker Failure;2.Master Failure;\n\n### Worker Failure\n\n- master周期性的`ping`worker,如果长时间没有的到回应认为当前worker失效\n- 当work完成了master所指派的任务时,会被标记成`idle`\n- 因为map结束后,结果存储在本地的磁盘中,如果任务结束而主机失效了,需要重新分配在一台主机上,而reduce则不用,因为结果存储在全局的文件系统中\n- 总之其中的worker失效了,master会将任务重新指派给被标记为`idle`的机器继续完成任务,不会有其他的影响\n\n\n### Master Failure\n\n- master可以周期性的建立备份,当master宕机后,可以从这些checkpoint中恢复过来\n- 然而,必须终止当前的mapreduce活动,用户需要重新开始尝试\n\n\n## 模型优化\n\n\n\n### Locality\nmapreduce是面向大规模数据的处理模型,因此大量的数据在网络中传输占用了大量的带宽,产生了大量的时延,因此我们可以有限指派数据所在的电脑进行map的任务,避免了这部分数据的传输。而mapreduce是配合GFS使用,而GFS将文件分成大小相同的chunck并有多个replicas分布在不同的chunck-machine,所以master将map任务分配给了那些有这些replica的机器上,减小了网络数据量的传输\n\n\n\n### Task Granularity\n我们将map任务切割成M块,reduce任务分割成R块,理论上M和R应该远远大于机器的数量,让一个worker运行多个不同的任务能够动态的均衡负载并且当worker失效的时候能够快速的恢复(???这里一直不太懂)\n在R和M的设置上,我们选择一个M使得每一份输入的大小控制在16-64MB,而R的大小是worker数量的一个倍数,例如当workers=2000,R=5000,M=20000\n\n### Backup Tasks\nBackup Tasks 是模型优化的精髓。由于众所周知的“木桶效应”,执行的时间往往是由最慢的那个worker决定的,即“straggler”。正是由于这些“stragger”的存在,使得整个mapreduce的过程耗费大量的时间。因此,在当mapreduce过程接近完成的时候,master又将这些正在进行的任务分配给backup,无论是primary还是backup完成了都标记成任务完成,经过计算大约减少了44%的时间\n\n\n## 性能分析\n\n\n\n### Grep\n{% asset_image transfer.png %}\n上图是数据传送过程中的速率图,其中在50s左右的时候1764台机器开始进行工作,数据传输速率到达30GB/s,当map结束后,速率开始下降我都知道80s是停止。这里所谓的数据传输速率可以代表progress computation。\n可以看出在前60s的overhead,是因为master需要将处理的程序传送到每一个worker上,能达到这样的速度也是由于上面所说的**Local Optimization**\n使得大量的传输不依靠网络。\n\n\n\n### Sort\n1. 在这次sort中,我们设置M=15000,R=4000,在(a)的最上面的图中我们可以看出在200秒左右map任务完成,速率最高到达13GB/s,低于上面的**Grep**是因为map结束后需要产生**Intermediate Key**并写入磁盘中,耗费大量的I/O时间\n2. shuffling在第一个map完成之后开始,由于本次有1700个worker,因此大致上有两个峰分别代表两个batch\n3. 最后一张图是reduce worker工作,输入为Intermediate key-value paris,最终在891秒完成了任务\n\n\n有一些东西需要我们稍微注意一下:\n- 输入的速率远大于shuffle也是因为**Local Opimization**,大部分的数据都在本地,而shuffle是依靠网络,reduce远距离读取worker存在本地硬盘中的结果\n- 输出的速率比起shuffle更小是因为我们做了备份,每个结果都有一个备份因此速率更小\n\n{% asset_image allcondition.png %}\n\n\n### Effect of Backup Tasks\n在图(b)中可以看出最后的完成的时间为1200多秒,因为有”stragger”一直拖着,导致最后完成需要更多的时间。\n可以看出backup对整个工作还是十分有益的\n\n\n### Machine Failures\n图(c)表示了在运行的过程中出现了部分机器失效的情况,可以看出立刻安排了新的workers,整个mapreduce的工作还是继续进行\n可以看到Input rate 出现了负增长,是因为部分完成的map的工作丢失了(intermediate key-values存储在本地)随后马上执行re-execution,只比正常情况多了5%的时间\n\n## 总结\n以上就是我对mapreduce的总结,我会尽快写出MIT6.824 实验一(实现一个简易的mapreduce)\n\n\n\n\n\n\n\n\n\n","source":"_posts/MapReduce.md","raw":"---\ntitle: MapReduce\ndate: 2017-10-30 21:32:06\ncategories:\n- 分布式系统\n- MIT6.824\ntags:\n- mapreduce\n---\n最近读了一下经典的MapReduce这篇论文,大概的了解了一下整个模型的处理流程和用户的编程的方法,以及处理过程的优化和差错处理,下面我来谈谈我对论文的理解。网上有的更多的是使用MapReduce,而我着重对原理的理解。\n并且,我也跟着MIT6.824的实验实验了一个简单的MapReduce模型,采用多线程来模拟分布式,最终实现了词频统计和倒排序索引\n\n\n## 编程模型\n\n整个`mapreduce`分成两个部分,包括`map`和`reduce`部分,其中map的输入为`key/value pairs`,输出为`intermediate key/value pairs`,而`reduce`接受 `key`和所对应的`values`将其合并。简单的过程如下所示:\n\n`map\t\t\t(k1,v1)\t\t\t->\t\tlist(k2,v2)`\n`reduce\t\t(k2,list(v2))\t->\t\tlist(v2)`\n\n\n## 执行过程\n整个模型的执行过程比较明了,当处理到`mapreduce`部分时,`master`开始统领整个过程,将输入内容分割并分配给M台机器进行Map的工作,结束后每个Map都将中间值分成R个分区`hash(key)%R`,R个机器读取完每个中间文件的相应的分区后进行Reduce的工作最后输出结果并合并。整个过程如下图所示\n{% asset_image excution.png %}\n\n\n\n### map\n1. MapReduce Library 将用户的输入文件分成M块,每一块的大小为16-64MB可以由用户指定,然后将用户的代码复制到集群的各个机器上\n2. master指定 M map 任务,R reduce 任务,并挑选空闲的机器执行\n3. map 将输入的内容转化成 intermediate key-value pairs ,并存储在自己的内存当中\n4. 周期性的将内存中的键值对写入本机的磁盘中并分割成R个区,可以使用`hash(key)%R`处理,并将在磁盘中的存储位置返回给master\n\n\n\n### reduce\n5. reduce 利用**网络**从master所给的地址中读取完所有的内容后,按照key将结果进行**排序**,如果内容过大可以使用外部排序\n6. 当reduce 遍历完成后,将 key 和对应的 value序列传递给用户指定的reduce程序\n7. 当所有的任务都执行完成后,mapreduce 返回用户程序代码\n\n\n\n\n## 容错处理\nMapreduce 编程模型是建立在一个计算机的集群上的,需要考虑主机失效的情况,类似与GFS。这一点也是十分重要的,考虑了实际情况中,集群往往采用了一些低性能,被淘汰的电脑,计算机失效的情况十分的常见。其中失效分成了两种情况:1.Worker Failure;2.Master Failure;\n\n### Worker Failure\n\n- master周期性的`ping`worker,如果长时间没有的到回应认为当前worker失效\n- 当work完成了master所指派的任务时,会被标记成`idle`\n- 因为map结束后,结果存储在本地的磁盘中,如果任务结束而主机失效了,需要重新分配在一台主机上,而reduce则不用,因为结果存储在全局的文件系统中\n- 总之其中的worker失效了,master会将任务重新指派给被标记为`idle`的机器继续完成任务,不会有其他的影响\n\n\n### Master Failure\n\n- master可以周期性的建立备份,当master宕机后,可以从这些checkpoint中恢复过来\n- 然而,必须终止当前的mapreduce活动,用户需要重新开始尝试\n\n\n## 模型优化\n\n\n\n### Locality\nmapreduce是面向大规模数据的处理模型,因此大量的数据在网络中传输占用了大量的带宽,产生了大量的时延,因此我们可以有限指派数据所在的电脑进行map的任务,避免了这部分数据的传输。而mapreduce是配合GFS使用,而GFS将文件分成大小相同的chunck并有多个replicas分布在不同的chunck-machine,所以master将map任务分配给了那些有这些replica的机器上,减小了网络数据量的传输\n\n\n\n### Task Granularity\n我们将map任务切割成M块,reduce任务分割成R块,理论上M和R应该远远大于机器的数量,让一个worker运行多个不同的任务能够动态的均衡负载并且当worker失效的时候能够快速的恢复(???这里一直不太懂)\n在R和M的设置上,我们选择一个M使得每一份输入的大小控制在16-64MB,而R的大小是worker数量的一个倍数,例如当workers=2000,R=5000,M=20000\n\n### Backup Tasks\nBackup Tasks 是模型优化的精髓。由于众所周知的“木桶效应”,执行的时间往往是由最慢的那个worker决定的,即“straggler”。正是由于这些“stragger”的存在,使得整个mapreduce的过程耗费大量的时间。因此,在当mapreduce过程接近完成的时候,master又将这些正在进行的任务分配给backup,无论是primary还是backup完成了都标记成任务完成,经过计算大约减少了44%的时间\n\n\n## 性能分析\n\n\n\n### Grep\n{% asset_image transfer.png %}\n上图是数据传送过程中的速率图,其中在50s左右的时候1764台机器开始进行工作,数据传输速率到达30GB/s,当map结束后,速率开始下降我都知道80s是停止。这里所谓的数据传输速率可以代表progress computation。\n可以看出在前60s的overhead,是因为master需要将处理的程序传送到每一个worker上,能达到这样的速度也是由于上面所说的**Local Optimization**\n使得大量的传输不依靠网络。\n\n\n\n### Sort\n1. 在这次sort中,我们设置M=15000,R=4000,在(a)的最上面的图中我们可以看出在200秒左右map任务完成,速率最高到达13GB/s,低于上面的**Grep**是因为map结束后需要产生**Intermediate Key**并写入磁盘中,耗费大量的I/O时间\n2. shuffling在第一个map完成之后开始,由于本次有1700个worker,因此大致上有两个峰分别代表两个batch\n3. 最后一张图是reduce worker工作,输入为Intermediate key-value paris,最终在891秒完成了任务\n\n\n有一些东西需要我们稍微注意一下:\n- 输入的速率远大于shuffle也是因为**Local Opimization**,大部分的数据都在本地,而shuffle是依靠网络,reduce远距离读取worker存在本地硬盘中的结果\n- 输出的速率比起shuffle更小是因为我们做了备份,每个结果都有一个备份因此速率更小\n\n{% asset_image allcondition.png %}\n\n\n### Effect of Backup Tasks\n在图(b)中可以看出最后的完成的时间为1200多秒,因为有”stragger”一直拖着,导致最后完成需要更多的时间。\n可以看出backup对整个工作还是十分有益的\n\n\n### Machine Failures\n图(c)表示了在运行的过程中出现了部分机器失效的情况,可以看出立刻安排了新的workers,整个mapreduce的工作还是继续进行\n可以看到Input rate 出现了负增长,是因为部分完成的map的工作丢失了(intermediate key-values存储在本地)随后马上执行re-execution,只比正常情况多了5%的时间\n\n## 总结\n以上就是我对mapreduce的总结,我会尽快写出MIT6.824 实验一(实现一个简易的mapreduce)\n\n\n\n\n\n\n\n\n\n","slug":"MapReduce","published":1,"updated":"2018-04-30T13:49:56.132Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cjyohfvcw0001sy0yqj958sne","content":"<p>最近读了一下经典的MapReduce这篇论文,大概的了解了一下整个模型的处理流程和用户的编程的方法,以及处理过程的优化和差错处理,下面我来谈谈我对论文的理解。网上有的更多的是使用MapReduce,而我着重对原理的理解。<br>并且,我也跟着MIT6.824的实验实验了一个简单的MapReduce模型,采用多线程来模拟分布式,最终实现了词频统计和倒排序索引</p>\n<h2 id=\"编程模型\"><a href=\"#编程模型\" class=\"headerlink\" title=\"编程模型\"></a>编程模型</h2><p>整个<code>mapreduce</code>分成两个部分,包括<code>map</code>和<code>reduce</code>部分,其中map的输入为<code>key/value pairs</code>,输出为<code>intermediate key/value pairs</code>,而<code>reduce</code>接受 <code>key</code>和所对应的<code>values</code>将其合并。简单的过程如下所示:</p>\n<p><code>map (k1,v1) -> list(k2,v2)</code><br><code>reduce (k2,list(v2)) -> list(v2)</code></p>\n<h2 id=\"执行过程\"><a href=\"#执行过程\" class=\"headerlink\" title=\"执行过程\"></a>执行过程</h2><p>整个模型的执行过程比较明了,当处理到<code>mapreduce</code>部分时,<code>master</code>开始统领整个过程,将输入内容分割并分配给M台机器进行Map的工作,结束后每个Map都将中间值分成R个分区<code>hash(key)%R</code>,R个机器读取完每个中间文件的相应的分区后进行Reduce的工作最后输出结果并合并。整个过程如下图所示<br><img src=\"/2017/10/30/MapReduce/excution.png\" alt=\"excution.png\" title=\"\"></p>\n<h3 id=\"map\"><a href=\"#map\" class=\"headerlink\" title=\"map\"></a>map</h3><ol>\n<li>MapReduce Library 将用户的输入文件分成M块,每一块的大小为16-64MB可以由用户指定,然后将用户的代码复制到集群的各个机器上</li>\n<li>master指定 M map 任务,R reduce 任务,并挑选空闲的机器执行</li>\n<li>map 将输入的内容转化成 intermediate key-value pairs ,并存储在自己的内存当中</li>\n<li>周期性的将内存中的键值对写入本机的磁盘中并分割成R个区,可以使用<code>hash(key)%R</code>处理,并将在磁盘中的存储位置返回给master</li>\n</ol>\n<h3 id=\"reduce\"><a href=\"#reduce\" class=\"headerlink\" title=\"reduce\"></a>reduce</h3><ol>\n<li>reduce 利用<strong>网络</strong>从master所给的地址中读取完所有的内容后,按照key将结果进行<strong>排序</strong>,如果内容过大可以使用外部排序</li>\n<li>当reduce 遍历完成后,将 key 和对应的 value序列传递给用户指定的reduce程序</li>\n<li>当所有的任务都执行完成后,mapreduce 返回用户程序代码</li>\n</ol>\n<h2 id=\"容错处理\"><a href=\"#容错处理\" class=\"headerlink\" title=\"容错处理\"></a>容错处理</h2><p>Mapreduce 编程模型是建立在一个计算机的集群上的,需要考虑主机失效的情况,类似与GFS。这一点也是十分重要的,考虑了实际情况中,集群往往采用了一些低性能,被淘汰的电脑,计算机失效的情况十分的常见。其中失效分成了两种情况:1.Worker Failure;2.Master Failure;</p>\n<h3 id=\"Worker-Failure\"><a href=\"#Worker-Failure\" class=\"headerlink\" title=\"Worker Failure\"></a>Worker Failure</h3><ul>\n<li>master周期性的<code>ping</code>worker,如果长时间没有的到回应认为当前worker失效</li>\n<li>当work完成了master所指派的任务时,会被标记成<code>idle</code></li>\n<li>因为map结束后,结果存储在本地的磁盘中,如果任务结束而主机失效了,需要重新分配在一台主机上,而reduce则不用,因为结果存储在全局的文件系统中</li>\n<li>总之其中的worker失效了,master会将任务重新指派给被标记为<code>idle</code>的机器继续完成任务,不会有其他的影响</li>\n</ul>\n<h3 id=\"Master-Failure\"><a href=\"#Master-Failure\" class=\"headerlink\" title=\"Master Failure\"></a>Master Failure</h3><ul>\n<li>master可以周期性的建立备份,当master宕机后,可以从这些checkpoint中恢复过来</li>\n<li>然而,必须终止当前的mapreduce活动,用户需要重新开始尝试</li>\n</ul>\n<h2 id=\"模型优化\"><a href=\"#模型优化\" class=\"headerlink\" title=\"模型优化\"></a>模型优化</h2><h3 id=\"Locality\"><a href=\"#Locality\" class=\"headerlink\" title=\"Locality\"></a>Locality</h3><p>mapreduce是面向大规模数据的处理模型,因此大量的数据在网络中传输占用了大量的带宽,产生了大量的时延,因此我们可以有限指派数据所在的电脑进行map的任务,避免了这部分数据的传输。而mapreduce是配合GFS使用,而GFS将文件分成大小相同的chunck并有多个replicas分布在不同的chunck-machine,所以master将map任务分配给了那些有这些replica的机器上,减小了网络数据量的传输</p>\n<h3 id=\"Task-Granularity\"><a href=\"#Task-Granularity\" class=\"headerlink\" title=\"Task Granularity\"></a>Task Granularity</h3><p>我们将map任务切割成M块,reduce任务分割成R块,理论上M和R应该远远大于机器的数量,让一个worker运行多个不同的任务能够动态的均衡负载并且当worker失效的时候能够快速的恢复(???这里一直不太懂)<br>在R和M的设置上,我们选择一个M使得每一份输入的大小控制在16-64MB,而R的大小是worker数量的一个倍数,例如当workers=2000,R=5000,M=20000</p>\n<h3 id=\"Backup-Tasks\"><a href=\"#Backup-Tasks\" class=\"headerlink\" title=\"Backup Tasks\"></a>Backup Tasks</h3><p>Backup Tasks 是模型优化的精髓。由于众所周知的“木桶效应”,执行的时间往往是由最慢的那个worker决定的,即“straggler”。正是由于这些“stragger”的存在,使得整个mapreduce的过程耗费大量的时间。因此,在当mapreduce过程接近完成的时候,master又将这些正在进行的任务分配给backup,无论是primary还是backup完成了都标记成任务完成,经过计算大约减少了44%的时间</p>\n<h2 id=\"性能分析\"><a href=\"#性能分析\" class=\"headerlink\" title=\"性能分析\"></a>性能分析</h2><h3 id=\"Grep\"><a href=\"#Grep\" class=\"headerlink\" title=\"Grep\"></a>Grep</h3><img src=\"/2017/10/30/MapReduce/transfer.png\" alt=\"transfer.png\" title=\"\">\n<p>上图是数据传送过程中的速率图,其中在50s左右的时候1764台机器开始进行工作,数据传输速率到达30GB/s,当map结束后,速率开始下降我都知道80s是停止。这里所谓的数据传输速率可以代表progress computation。<br>可以看出在前60s的overhead,是因为master需要将处理的程序传送到每一个worker上,能达到这样的速度也是由于上面所说的<strong>Local Optimization</strong><br>使得大量的传输不依靠网络。</p>\n<h3 id=\"Sort\"><a href=\"#Sort\" class=\"headerlink\" title=\"Sort\"></a>Sort</h3><ol>\n<li>在这次sort中,我们设置M=15000,R=4000,在(a)的最上面的图中我们可以看出在200秒左右map任务完成,速率最高到达13GB/s,低于上面的<strong>Grep</strong>是因为map结束后需要产生<strong>Intermediate Key</strong>并写入磁盘中,耗费大量的I/O时间</li>\n<li>shuffling在第一个map完成之后开始,由于本次有1700个worker,因此大致上有两个峰分别代表两个batch</li>\n<li>最后一张图是reduce worker工作,输入为Intermediate key-value paris,最终在891秒完成了任务</li>\n</ol>\n<p>有一些东西需要我们稍微注意一下:</p>\n<ul>\n<li>输入的速率远大于shuffle也是因为<strong>Local Opimization</strong>,大部分的数据都在本地,而shuffle是依靠网络,reduce远距离读取worker存在本地硬盘中的结果</li>\n<li>输出的速率比起shuffle更小是因为我们做了备份,每个结果都有一个备份因此速率更小</li>\n</ul>\n<img src=\"/2017/10/30/MapReduce/allcondition.png\" alt=\"allcondition.png\" title=\"\">\n<h3 id=\"Effect-of-Backup-Tasks\"><a href=\"#Effect-of-Backup-Tasks\" class=\"headerlink\" title=\"Effect of Backup Tasks\"></a>Effect of Backup Tasks</h3><p>在图(b)中可以看出最后的完成的时间为1200多秒,因为有”stragger”一直拖着,导致最后完成需要更多的时间。<br>可以看出backup对整个工作还是十分有益的</p>\n<h3 id=\"Machine-Failures\"><a href=\"#Machine-Failures\" class=\"headerlink\" title=\"Machine Failures\"></a>Machine Failures</h3><p>图(c)表示了在运行的过程中出现了部分机器失效的情况,可以看出立刻安排了新的workers,整个mapreduce的工作还是继续进行<br>可以看到Input rate 出现了负增长,是因为部分完成的map的工作丢失了(intermediate key-values存储在本地)随后马上执行re-execution,只比正常情况多了5%的时间</p>\n<h2 id=\"总结\"><a href=\"#总结\" class=\"headerlink\" title=\"总结\"></a>总结</h2><p>以上就是我对mapreduce的总结,我会尽快写出MIT6.824 实验一(实现一个简易的mapreduce)</p>\n","site":{"data":{}},"excerpt":"","more":"<p>最近读了一下经典的MapReduce这篇论文,大概的了解了一下整个模型的处理流程和用户的编程的方法,以及处理过程的优化和差错处理,下面我来谈谈我对论文的理解。网上有的更多的是使用MapReduce,而我着重对原理的理解。<br>并且,我也跟着MIT6.824的实验实验了一个简单的MapReduce模型,采用多线程来模拟分布式,最终实现了词频统计和倒排序索引</p>\n<h2 id=\"编程模型\"><a href=\"#编程模型\" class=\"headerlink\" title=\"编程模型\"></a>编程模型</h2><p>整个<code>mapreduce</code>分成两个部分,包括<code>map</code>和<code>reduce</code>部分,其中map的输入为<code>key/value pairs</code>,输出为<code>intermediate key/value pairs</code>,而<code>reduce</code>接受 <code>key</code>和所对应的<code>values</code>将其合并。简单的过程如下所示:</p>\n<p><code>map (k1,v1) -> list(k2,v2)</code><br><code>reduce (k2,list(v2)) -> list(v2)</code></p>\n<h2 id=\"执行过程\"><a href=\"#执行过程\" class=\"headerlink\" title=\"执行过程\"></a>执行过程</h2><p>整个模型的执行过程比较明了,当处理到<code>mapreduce</code>部分时,<code>master</code>开始统领整个过程,将输入内容分割并分配给M台机器进行Map的工作,结束后每个Map都将中间值分成R个分区<code>hash(key)%R</code>,R个机器读取完每个中间文件的相应的分区后进行Reduce的工作最后输出结果并合并。整个过程如下图所示<br><img src=\"/2017/10/30/MapReduce/excution.png\" alt=\"excution.png\" title=\"\"></p>\n<h3 id=\"map\"><a href=\"#map\" class=\"headerlink\" title=\"map\"></a>map</h3><ol>\n<li>MapReduce Library 将用户的输入文件分成M块,每一块的大小为16-64MB可以由用户指定,然后将用户的代码复制到集群的各个机器上</li>\n<li>master指定 M map 任务,R reduce 任务,并挑选空闲的机器执行</li>\n<li>map 将输入的内容转化成 intermediate key-value pairs ,并存储在自己的内存当中</li>\n<li>周期性的将内存中的键值对写入本机的磁盘中并分割成R个区,可以使用<code>hash(key)%R</code>处理,并将在磁盘中的存储位置返回给master</li>\n</ol>\n<h3 id=\"reduce\"><a href=\"#reduce\" class=\"headerlink\" title=\"reduce\"></a>reduce</h3><ol>\n<li>reduce 利用<strong>网络</strong>从master所给的地址中读取完所有的内容后,按照key将结果进行<strong>排序</strong>,如果内容过大可以使用外部排序</li>\n<li>当reduce 遍历完成后,将 key 和对应的 value序列传递给用户指定的reduce程序</li>\n<li>当所有的任务都执行完成后,mapreduce 返回用户程序代码</li>\n</ol>\n<h2 id=\"容错处理\"><a href=\"#容错处理\" class=\"headerlink\" title=\"容错处理\"></a>容错处理</h2><p>Mapreduce 编程模型是建立在一个计算机的集群上的,需要考虑主机失效的情况,类似与GFS。这一点也是十分重要的,考虑了实际情况中,集群往往采用了一些低性能,被淘汰的电脑,计算机失效的情况十分的常见。其中失效分成了两种情况:1.Worker Failure;2.Master Failure;</p>\n<h3 id=\"Worker-Failure\"><a href=\"#Worker-Failure\" class=\"headerlink\" title=\"Worker Failure\"></a>Worker Failure</h3><ul>\n<li>master周期性的<code>ping</code>worker,如果长时间没有的到回应认为当前worker失效</li>\n<li>当work完成了master所指派的任务时,会被标记成<code>idle</code></li>\n<li>因为map结束后,结果存储在本地的磁盘中,如果任务结束而主机失效了,需要重新分配在一台主机上,而reduce则不用,因为结果存储在全局的文件系统中</li>\n<li>总之其中的worker失效了,master会将任务重新指派给被标记为<code>idle</code>的机器继续完成任务,不会有其他的影响</li>\n</ul>\n<h3 id=\"Master-Failure\"><a href=\"#Master-Failure\" class=\"headerlink\" title=\"Master Failure\"></a>Master Failure</h3><ul>\n<li>master可以周期性的建立备份,当master宕机后,可以从这些checkpoint中恢复过来</li>\n<li>然而,必须终止当前的mapreduce活动,用户需要重新开始尝试</li>\n</ul>\n<h2 id=\"模型优化\"><a href=\"#模型优化\" class=\"headerlink\" title=\"模型优化\"></a>模型优化</h2><h3 id=\"Locality\"><a href=\"#Locality\" class=\"headerlink\" title=\"Locality\"></a>Locality</h3><p>mapreduce是面向大规模数据的处理模型,因此大量的数据在网络中传输占用了大量的带宽,产生了大量的时延,因此我们可以有限指派数据所在的电脑进行map的任务,避免了这部分数据的传输。而mapreduce是配合GFS使用,而GFS将文件分成大小相同的chunck并有多个replicas分布在不同的chunck-machine,所以master将map任务分配给了那些有这些replica的机器上,减小了网络数据量的传输</p>\n<h3 id=\"Task-Granularity\"><a href=\"#Task-Granularity\" class=\"headerlink\" title=\"Task Granularity\"></a>Task Granularity</h3><p>我们将map任务切割成M块,reduce任务分割成R块,理论上M和R应该远远大于机器的数量,让一个worker运行多个不同的任务能够动态的均衡负载并且当worker失效的时候能够快速的恢复(???这里一直不太懂)<br>在R和M的设置上,我们选择一个M使得每一份输入的大小控制在16-64MB,而R的大小是worker数量的一个倍数,例如当workers=2000,R=5000,M=20000</p>\n<h3 id=\"Backup-Tasks\"><a href=\"#Backup-Tasks\" class=\"headerlink\" title=\"Backup Tasks\"></a>Backup Tasks</h3><p>Backup Tasks 是模型优化的精髓。由于众所周知的“木桶效应”,执行的时间往往是由最慢的那个worker决定的,即“straggler”。正是由于这些“stragger”的存在,使得整个mapreduce的过程耗费大量的时间。因此,在当mapreduce过程接近完成的时候,master又将这些正在进行的任务分配给backup,无论是primary还是backup完成了都标记成任务完成,经过计算大约减少了44%的时间</p>\n<h2 id=\"性能分析\"><a href=\"#性能分析\" class=\"headerlink\" title=\"性能分析\"></a>性能分析</h2><h3 id=\"Grep\"><a href=\"#Grep\" class=\"headerlink\" title=\"Grep\"></a>Grep</h3><img src=\"/2017/10/30/MapReduce/transfer.png\" alt=\"transfer.png\" title=\"\">\n<p>上图是数据传送过程中的速率图,其中在50s左右的时候1764台机器开始进行工作,数据传输速率到达30GB/s,当map结束后,速率开始下降我都知道80s是停止。这里所谓的数据传输速率可以代表progress computation。<br>可以看出在前60s的overhead,是因为master需要将处理的程序传送到每一个worker上,能达到这样的速度也是由于上面所说的<strong>Local Optimization</strong><br>使得大量的传输不依靠网络。</p>\n<h3 id=\"Sort\"><a href=\"#Sort\" class=\"headerlink\" title=\"Sort\"></a>Sort</h3><ol>\n<li>在这次sort中,我们设置M=15000,R=4000,在(a)的最上面的图中我们可以看出在200秒左右map任务完成,速率最高到达13GB/s,低于上面的<strong>Grep</strong>是因为map结束后需要产生<strong>Intermediate Key</strong>并写入磁盘中,耗费大量的I/O时间</li>\n<li>shuffling在第一个map完成之后开始,由于本次有1700个worker,因此大致上有两个峰分别代表两个batch</li>\n<li>最后一张图是reduce worker工作,输入为Intermediate key-value paris,最终在891秒完成了任务</li>\n</ol>\n<p>有一些东西需要我们稍微注意一下:</p>\n<ul>\n<li>输入的速率远大于shuffle也是因为<strong>Local Opimization</strong>,大部分的数据都在本地,而shuffle是依靠网络,reduce远距离读取worker存在本地硬盘中的结果</li>\n<li>输出的速率比起shuffle更小是因为我们做了备份,每个结果都有一个备份因此速率更小</li>\n</ul>\n<img src=\"/2017/10/30/MapReduce/allcondition.png\" alt=\"allcondition.png\" title=\"\">\n<h3 id=\"Effect-of-Backup-Tasks\"><a href=\"#Effect-of-Backup-Tasks\" class=\"headerlink\" title=\"Effect of Backup Tasks\"></a>Effect of Backup Tasks</h3><p>在图(b)中可以看出最后的完成的时间为1200多秒,因为有”stragger”一直拖着,导致最后完成需要更多的时间。<br>可以看出backup对整个工作还是十分有益的</p>\n<h3 id=\"Machine-Failures\"><a href=\"#Machine-Failures\" class=\"headerlink\" title=\"Machine Failures\"></a>Machine Failures</h3><p>图(c)表示了在运行的过程中出现了部分机器失效的情况,可以看出立刻安排了新的workers,整个mapreduce的工作还是继续进行<br>可以看到Input rate 出现了负增长,是因为部分完成的map的工作丢失了(intermediate key-values存储在本地)随后马上执行re-execution,只比正常情况多了5%的时间</p>\n<h2 id=\"总结\"><a href=\"#总结\" class=\"headerlink\" title=\"总结\"></a>总结</h2><p>以上就是我对mapreduce的总结,我会尽快写出MIT6.824 实验一(实现一个简易的mapreduce)</p>\n"},{"title":"ZooKeeper","date":"2017-11-27T11:24:21.000Z","_content":"zookeeper是一个分布式的协调系统,目标是封装好关键的服务,提供简易使用的借口,建立更加复杂的协调原语。\n并且,zookeeper是高效的,适用于以读为主的服务请求,任意一个server都可以服务于read请求,查找local replica并返回结果,而写操作则需要通过leader,并确保数据的一致性\n\n{% asset_image service.png %}\n一组server能够提高性能\n\n## Core feature\n在能大概读懂文章的内容之后,有以下的几个特点印象特别深刻:\n### Wait-free\nzookeeper最大的特点就是高效的,对比其他同步的算法,采用异步的算法能够有效的防止部分machine而阻塞了整个服务进程。\n但是采用异步的方式难以保证一致性,但采用**FIFO Client Order**和**Linearizable Writes**保证了系统的协调性\n**move away from blocking primitives,such as locks**\n\n### Complicated imformation\nZnode并不是被设计来存储数据的,但可以存储复杂的meta-data和configuration\n### Znode\nznode可以被设置 `sequential flag`,当`emphemral node`被删除后,同时也被删除。\n同时采用递增的命名方式,序号小的是先被设置的----可以用于锁的设置\n采用类似UNIX的命名方式进行管理,如图所示\n{% asset_image zknamespace.jpg %}\n### Client API\n将复杂的一致性过程用简单的API进行包装,方便使用\n\n## Service overview\n下面来介绍一下整体的服务:\n- znode作为数据对象,而客户端可以通过提供的API进行操纵\n- `wathces`作为一次性的触发器,在操作中绑定,用于数据更新的提醒\n- 提供API对`data object`进行操纵\n\n\n### Hierarchical data object\n`data object`具有层次化的组织结构,像UNIX文件的文件组织结构。若要访问数据对象,输入该对象的绝对地址\n当建立新的znode,可以设置`sequential flag`,使得znode命名中的数字是单调递增的\n数据对象可以分成 **Regular**和**Ephemeral**两种:\n#### Regular\n客户端可以简单的创建并删除的对象\n#### Ephemeral\n当客户端连接到ZK后可以创建临时的对象,并让ZK自动的删除(session 结束后)\n### Watches callback\n`watches`可以让客户端及时的接收某个数据的改变,可以在每次读取等操作时设置,仅仅只更新一次\n`getData(\"/foo\",true)`\n当`/foo`发生改变之前,会通过`watch`通知client\n### Session\n- client在连接到zk时确立一个session,当发出请求时获得一个session handle\n- session设置了一个定时器,一段时间没有接收到信息,ZK认为client出错了\n- watch 状态的变化\n\n\n### Client API\n1. `create(path,data,flags)`创建data object,flags ---> regular, ephemeral, set the sequential flag\n2. `delete(path,version)`当版本号相对应后,删除数据对象\n3. `exists(path,watch)` 允许设置`watch`,当数据对象不存在之前会notify客户端\n4. `getData(path,watch)` 返回相应的data、meta-data,在change之前notify客户端\n5. `setData(path,data,version)`将data[]写入相应的path中\n6. `getChildren(path,watch)`返回znode的全部的children名字\n7. `sync(path)`等待所有的更新操作的结束,使得read到的数据是最新的。\n\n\n上述API均有同步和异步两个版本,异步调用,zk client保证按照操作被调用的顺序执行回调;\n所有更新操作均有版本号,可决定是否做版本检查\n\n## System guarantees\n整个系统通过下面两个条件保证整个系统的协调性,下面有一个case以选举的方式具体说明整个过程:\n当选举出一个新的leader时,这个leader会改变系统的设置,并通知各个节点,需要保证一下两点:\n1. 当leader开始改变某些设置的时候,我们不希望任何process开始使用这些正在改变的设置\n2. 当新的leader在设置完全更新完之前宕机后,我们不希望任何的process去使用这不完整的设置\n\n总之,就是希望其他的process在configuration完全设置完后再使用\nleader可以指定一个地址作为ready zone,当configuration设置完成后,其他的process去读取,完整自己的更新。\n这样一个异步的方式,比起leader去更新每一个znode快的多\n### Linearizable writes\n所有更新操作都是序列化的。由于ZK强调高性能,因此我们这的linerizability是异步的方式,允许client拥有多个操作,并保证每一个client是以FIFO的方式执行的\n### FIFO client order\n所有的操作都是按照clent发送的顺序的顺序去执行的。\n## Example of primitives\n下面是利用ZK的API进行的一些case的设计,帮助更好的实现系统的一致性\n### Configuration Management\n如上面所说的选举完新的leader后需要对configuration有很多新的操作,因此会指定一个znode,所有的server异步的对这个地址进行读取,完成自身的configuration的更新操作。\n利用watch进行监视,以便及时的收取到更新configuration的notification\n### Rendezbous(集结地)\n当client创建master和client程序时,由于是决策器完成的,client并不知道worker和master的地址和端口,因此无法让worker连接master;\n因此,client可以创建一个Zr并将它的地址传递给指定的master和所有的worker。\n当worker启动时,read Zr,并设置watch=true,当master写入address和port之后,notify worker\n### Group Membership\n设置一个`eephemeral Node`Zg代表整个group,并设置`sequential flag`保持唯一性\n当创建一个process,则在Zg下创建一个znode,process fail,znode自动移除。\n### Simple Lock without Herd Effect\nZK可以创建简单的锁服务,原理是获得锁,就创建一个znode,释放锁就删除相对应的znode。\n而znode中的命名的大小是作为请求获得锁的process的顺序,从小到大\n为了避免**herd effect**,释放锁后仅仅唤醒一个等待的线程\n```\n//lock()\nn = create(l+\"/lock-\",EPHEMERAL|SEQUENTIAL)\nC = getChildren(l,false)\nif n is lowest znode in C,exit\np = znode in C ordered just before n\nif exist(p,true) wait for watch event\ngoto\n```\n```\n//unlock()\ndelete(n)\n```\n简单的来说就是,创建在parent l 下创建一个n,如果n是这些锁中最小的一个,则获得锁并退出\n如果不是,则在n前面一个锁p设置exsist的watch,当不存在时,通知当前线程,跳转到创建并获得锁的步骤\n删除锁则直接将该znode删除即可\n\n## 与其他的系统的对比\n\n### ZK vs Chubby\nchubby也有一个类似于文件系统的接口,它也使用一种特定的协议保证副本之间的一致性;however,zk不是一种锁服务,它可以用于实现锁,但是在API中没有锁操作。zk允许客户端连接任意一个zk server,不只是leader,zk client可以使用local replica来服务数据以及管理watch,因为它的一致性模型比chubby更放松;\n\n\n","source":"_posts/ZooKeeper.md","raw":"---\ntitle: ZooKeeper\ndate: 2017-11-27 19:24:21\ncategories:\n- 分布式系统\n- MIT6.824\ntags:\n- mapreduce\n---\nzookeeper是一个分布式的协调系统,目标是封装好关键的服务,提供简易使用的借口,建立更加复杂的协调原语。\n并且,zookeeper是高效的,适用于以读为主的服务请求,任意一个server都可以服务于read请求,查找local replica并返回结果,而写操作则需要通过leader,并确保数据的一致性\n\n{% asset_image service.png %}\n一组server能够提高性能\n\n## Core feature\n在能大概读懂文章的内容之后,有以下的几个特点印象特别深刻:\n### Wait-free\nzookeeper最大的特点就是高效的,对比其他同步的算法,采用异步的算法能够有效的防止部分machine而阻塞了整个服务进程。\n但是采用异步的方式难以保证一致性,但采用**FIFO Client Order**和**Linearizable Writes**保证了系统的协调性\n**move away from blocking primitives,such as locks**\n\n### Complicated imformation\nZnode并不是被设计来存储数据的,但可以存储复杂的meta-data和configuration\n### Znode\nznode可以被设置 `sequential flag`,当`emphemral node`被删除后,同时也被删除。\n同时采用递增的命名方式,序号小的是先被设置的----可以用于锁的设置\n采用类似UNIX的命名方式进行管理,如图所示\n{% asset_image zknamespace.jpg %}\n### Client API\n将复杂的一致性过程用简单的API进行包装,方便使用\n\n## Service overview\n下面来介绍一下整体的服务:\n- znode作为数据对象,而客户端可以通过提供的API进行操纵\n- `wathces`作为一次性的触发器,在操作中绑定,用于数据更新的提醒\n- 提供API对`data object`进行操纵\n\n\n### Hierarchical data object\n`data object`具有层次化的组织结构,像UNIX文件的文件组织结构。若要访问数据对象,输入该对象的绝对地址\n当建立新的znode,可以设置`sequential flag`,使得znode命名中的数字是单调递增的\n数据对象可以分成 **Regular**和**Ephemeral**两种:\n#### Regular\n客户端可以简单的创建并删除的对象\n#### Ephemeral\n当客户端连接到ZK后可以创建临时的对象,并让ZK自动的删除(session 结束后)\n### Watches callback\n`watches`可以让客户端及时的接收某个数据的改变,可以在每次读取等操作时设置,仅仅只更新一次\n`getData(\"/foo\",true)`\n当`/foo`发生改变之前,会通过`watch`通知client\n### Session\n- client在连接到zk时确立一个session,当发出请求时获得一个session handle\n- session设置了一个定时器,一段时间没有接收到信息,ZK认为client出错了\n- watch 状态的变化\n\n\n### Client API\n1. `create(path,data,flags)`创建data object,flags ---> regular, ephemeral, set the sequential flag\n2. `delete(path,version)`当版本号相对应后,删除数据对象\n3. `exists(path,watch)` 允许设置`watch`,当数据对象不存在之前会notify客户端\n4. `getData(path,watch)` 返回相应的data、meta-data,在change之前notify客户端\n5. `setData(path,data,version)`将data[]写入相应的path中\n6. `getChildren(path,watch)`返回znode的全部的children名字\n7. `sync(path)`等待所有的更新操作的结束,使得read到的数据是最新的。\n\n\n上述API均有同步和异步两个版本,异步调用,zk client保证按照操作被调用的顺序执行回调;\n所有更新操作均有版本号,可决定是否做版本检查\n\n## System guarantees\n整个系统通过下面两个条件保证整个系统的协调性,下面有一个case以选举的方式具体说明整个过程:\n当选举出一个新的leader时,这个leader会改变系统的设置,并通知各个节点,需要保证一下两点:\n1. 当leader开始改变某些设置的时候,我们不希望任何process开始使用这些正在改变的设置\n2. 当新的leader在设置完全更新完之前宕机后,我们不希望任何的process去使用这不完整的设置\n\n总之,就是希望其他的process在configuration完全设置完后再使用\nleader可以指定一个地址作为ready zone,当configuration设置完成后,其他的process去读取,完整自己的更新。\n这样一个异步的方式,比起leader去更新每一个znode快的多\n### Linearizable writes\n所有更新操作都是序列化的。由于ZK强调高性能,因此我们这的linerizability是异步的方式,允许client拥有多个操作,并保证每一个client是以FIFO的方式执行的\n### FIFO client order\n所有的操作都是按照clent发送的顺序的顺序去执行的。\n## Example of primitives\n下面是利用ZK的API进行的一些case的设计,帮助更好的实现系统的一致性\n### Configuration Management\n如上面所说的选举完新的leader后需要对configuration有很多新的操作,因此会指定一个znode,所有的server异步的对这个地址进行读取,完成自身的configuration的更新操作。\n利用watch进行监视,以便及时的收取到更新configuration的notification\n### Rendezbous(集结地)\n当client创建master和client程序时,由于是决策器完成的,client并不知道worker和master的地址和端口,因此无法让worker连接master;\n因此,client可以创建一个Zr并将它的地址传递给指定的master和所有的worker。\n当worker启动时,read Zr,并设置watch=true,当master写入address和port之后,notify worker\n### Group Membership\n设置一个`eephemeral Node`Zg代表整个group,并设置`sequential flag`保持唯一性\n当创建一个process,则在Zg下创建一个znode,process fail,znode自动移除。\n### Simple Lock without Herd Effect\nZK可以创建简单的锁服务,原理是获得锁,就创建一个znode,释放锁就删除相对应的znode。\n而znode中的命名的大小是作为请求获得锁的process的顺序,从小到大\n为了避免**herd effect**,释放锁后仅仅唤醒一个等待的线程\n```\n//lock()\nn = create(l+\"/lock-\",EPHEMERAL|SEQUENTIAL)\nC = getChildren(l,false)\nif n is lowest znode in C,exit\np = znode in C ordered just before n\nif exist(p,true) wait for watch event\ngoto\n```\n```\n//unlock()\ndelete(n)\n```\n简单的来说就是,创建在parent l 下创建一个n,如果n是这些锁中最小的一个,则获得锁并退出\n如果不是,则在n前面一个锁p设置exsist的watch,当不存在时,通知当前线程,跳转到创建并获得锁的步骤\n删除锁则直接将该znode删除即可\n\n## 与其他的系统的对比\n\n### ZK vs Chubby\nchubby也有一个类似于文件系统的接口,它也使用一种特定的协议保证副本之间的一致性;however,zk不是一种锁服务,它可以用于实现锁,但是在API中没有锁操作。zk允许客户端连接任意一个zk server,不只是leader,zk client可以使用local replica来服务数据以及管理watch,因为它的一致性模型比chubby更放松;\n\n\n","slug":"ZooKeeper","published":1,"updated":"2018-04-30T13:49:56.136Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cjyohfvd00003sy0yyfpxwp4f","content":"<p>zookeeper是一个分布式的协调系统,目标是封装好关键的服务,提供简易使用的借口,建立更加复杂的协调原语。<br>并且,zookeeper是高效的,适用于以读为主的服务请求,任意一个server都可以服务于read请求,查找local replica并返回结果,而写操作则需要通过leader,并确保数据的一致性</p>\n<img src=\"/2017/11/27/ZooKeeper/service.png\" alt=\"service.png\" title=\"\">\n<p>一组server能够提高性能</p>\n<h2 id=\"Core-feature\"><a href=\"#Core-feature\" class=\"headerlink\" title=\"Core feature\"></a>Core feature</h2><p>在能大概读懂文章的内容之后,有以下的几个特点印象特别深刻:</p>\n<h3 id=\"Wait-free\"><a href=\"#Wait-free\" class=\"headerlink\" title=\"Wait-free\"></a>Wait-free</h3><p>zookeeper最大的特点就是高效的,对比其他同步的算法,采用异步的算法能够有效的防止部分machine而阻塞了整个服务进程。<br>但是采用异步的方式难以保证一致性,但采用<strong>FIFO Client Order</strong>和<strong>Linearizable Writes</strong>保证了系统的协调性<br><strong>move away from blocking primitives,such as locks</strong></p>\n<h3 id=\"Complicated-imformation\"><a href=\"#Complicated-imformation\" class=\"headerlink\" title=\"Complicated imformation\"></a>Complicated imformation</h3><p>Znode并不是被设计来存储数据的,但可以存储复杂的meta-data和configuration</p>\n<h3 id=\"Znode\"><a href=\"#Znode\" class=\"headerlink\" title=\"Znode\"></a>Znode</h3><p>znode可以被设置 <code>sequential flag</code>,当<code>emphemral node</code>被删除后,同时也被删除。<br>同时采用递增的命名方式,序号小的是先被设置的—-可以用于锁的设置<br>采用类似UNIX的命名方式进行管理,如图所示<br><img src=\"/2017/11/27/ZooKeeper/zknamespace.jpg\" alt=\"zknamespace.jpg\" title=\"\"></p>\n<h3 id=\"Client-API\"><a href=\"#Client-API\" class=\"headerlink\" title=\"Client API\"></a>Client API</h3><p>将复杂的一致性过程用简单的API进行包装,方便使用</p>\n<h2 id=\"Service-overview\"><a href=\"#Service-overview\" class=\"headerlink\" title=\"Service overview\"></a>Service overview</h2><p>下面来介绍一下整体的服务:</p>\n<ul>\n<li>znode作为数据对象,而客户端可以通过提供的API进行操纵</li>\n<li><code>wathces</code>作为一次性的触发器,在操作中绑定,用于数据更新的提醒</li>\n<li>提供API对<code>data object</code>进行操纵</li>\n</ul>\n<h3 id=\"Hierarchical-data-object\"><a href=\"#Hierarchical-data-object\" class=\"headerlink\" title=\"Hierarchical data object\"></a>Hierarchical data object</h3><p><code>data object</code>具有层次化的组织结构,像UNIX文件的文件组织结构。若要访问数据对象,输入该对象的绝对地址<br>当建立新的znode,可以设置<code>sequential flag</code>,使得znode命名中的数字是单调递增的<br>数据对象可以分成 <strong>Regular</strong>和<strong>Ephemeral</strong>两种:</p>\n<h4 id=\"Regular\"><a href=\"#Regular\" class=\"headerlink\" title=\"Regular\"></a>Regular</h4><p>客户端可以简单的创建并删除的对象</p>\n<h4 id=\"Ephemeral\"><a href=\"#Ephemeral\" class=\"headerlink\" title=\"Ephemeral\"></a>Ephemeral</h4><p>当客户端连接到ZK后可以创建临时的对象,并让ZK自动的删除(session 结束后)</p>\n<h3 id=\"Watches-callback\"><a href=\"#Watches-callback\" class=\"headerlink\" title=\"Watches callback\"></a>Watches callback</h3><p><code>watches</code>可以让客户端及时的接收某个数据的改变,可以在每次读取等操作时设置,仅仅只更新一次<br><code>getData("/foo",true)</code><br>当<code>/foo</code>发生改变之前,会通过<code>watch</code>通知client</p>\n<h3 id=\"Session\"><a href=\"#Session\" class=\"headerlink\" title=\"Session\"></a>Session</h3><ul>\n<li>client在连接到zk时确立一个session,当发出请求时获得一个session handle</li>\n<li>session设置了一个定时器,一段时间没有接收到信息,ZK认为client出错了</li>\n<li>watch 状态的变化</li>\n</ul>\n<h3 id=\"Client-API-1\"><a href=\"#Client-API-1\" class=\"headerlink\" title=\"Client API\"></a>Client API</h3><ol>\n<li><code>create(path,data,flags)</code>创建data object,flags —> regular, ephemeral, set the sequential flag</li>\n<li><code>delete(path,version)</code>当版本号相对应后,删除数据对象</li>\n<li><code>exists(path,watch)</code> 允许设置<code>watch</code>,当数据对象不存在之前会notify客户端</li>\n<li><code>getData(path,watch)</code> 返回相应的data、meta-data,在change之前notify客户端</li>\n<li><code>setData(path,data,version)</code>将data[]写入相应的path中</li>\n<li><code>getChildren(path,watch)</code>返回znode的全部的children名字</li>\n<li><code>sync(path)</code>等待所有的更新操作的结束,使得read到的数据是最新的。</li>\n</ol>\n<p>上述API均有同步和异步两个版本,异步调用,zk client保证按照操作被调用的顺序执行回调;<br>所有更新操作均有版本号,可决定是否做版本检查</p>\n<h2 id=\"System-guarantees\"><a href=\"#System-guarantees\" class=\"headerlink\" title=\"System guarantees\"></a>System guarantees</h2><p>整个系统通过下面两个条件保证整个系统的协调性,下面有一个case以选举的方式具体说明整个过程:<br>当选举出一个新的leader时,这个leader会改变系统的设置,并通知各个节点,需要保证一下两点:</p>\n<ol>\n<li>当leader开始改变某些设置的时候,我们不希望任何process开始使用这些正在改变的设置</li>\n<li>当新的leader在设置完全更新完之前宕机后,我们不希望任何的process去使用这不完整的设置</li>\n</ol>\n<p>总之,就是希望其他的process在configuration完全设置完后再使用<br>leader可以指定一个地址作为ready zone,当configuration设置完成后,其他的process去读取,完整自己的更新。<br>这样一个异步的方式,比起leader去更新每一个znode快的多</p>\n<h3 id=\"Linearizable-writes\"><a href=\"#Linearizable-writes\" class=\"headerlink\" title=\"Linearizable writes\"></a>Linearizable writes</h3><p>所有更新操作都是序列化的。由于ZK强调高性能,因此我们这的linerizability是异步的方式,允许client拥有多个操作,并保证每一个client是以FIFO的方式执行的</p>\n<h3 id=\"FIFO-client-order\"><a href=\"#FIFO-client-order\" class=\"headerlink\" title=\"FIFO client order\"></a>FIFO client order</h3><p>所有的操作都是按照clent发送的顺序的顺序去执行的。</p>\n<h2 id=\"Example-of-primitives\"><a href=\"#Example-of-primitives\" class=\"headerlink\" title=\"Example of primitives\"></a>Example of primitives</h2><p>下面是利用ZK的API进行的一些case的设计,帮助更好的实现系统的一致性</p>\n<h3 id=\"Configuration-Management\"><a href=\"#Configuration-Management\" class=\"headerlink\" title=\"Configuration Management\"></a>Configuration Management</h3><p>如上面所说的选举完新的leader后需要对configuration有很多新的操作,因此会指定一个znode,所有的server异步的对这个地址进行读取,完成自身的configuration的更新操作。<br>利用watch进行监视,以便及时的收取到更新configuration的notification</p>\n<h3 id=\"Rendezbous(集结地)\"><a href=\"#Rendezbous(集结地)\" class=\"headerlink\" title=\"Rendezbous(集结地)\"></a>Rendezbous(集结地)</h3><p>当client创建master和client程序时,由于是决策器完成的,client并不知道worker和master的地址和端口,因此无法让worker连接master;<br>因此,client可以创建一个Zr并将它的地址传递给指定的master和所有的worker。<br>当worker启动时,read Zr,并设置watch=true,当master写入address和port之后,notify worker</p>\n<h3 id=\"Group-Membership\"><a href=\"#Group-Membership\" class=\"headerlink\" title=\"Group Membership\"></a>Group Membership</h3><p>设置一个<code>eephemeral Node</code>Zg代表整个group,并设置<code>sequential flag</code>保持唯一性<br>当创建一个process,则在Zg下创建一个znode,process fail,znode自动移除。</p>\n<h3 id=\"Simple-Lock-without-Herd-Effect\"><a href=\"#Simple-Lock-without-Herd-Effect\" class=\"headerlink\" title=\"Simple Lock without Herd Effect\"></a>Simple Lock without Herd Effect</h3><p>ZK可以创建简单的锁服务,原理是获得锁,就创建一个znode,释放锁就删除相对应的znode。<br>而znode中的命名的大小是作为请求获得锁的process的顺序,从小到大<br>为了避免<strong>herd effect</strong>,释放锁后仅仅唤醒一个等待的线程<br><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div></pre></td><td class=\"code\"><pre><div class=\"line\">//lock()</div><div class=\"line\">n = create(l+"/lock-",EPHEMERAL|SEQUENTIAL)</div><div class=\"line\">C = getChildren(l,false)</div><div class=\"line\">if n is lowest znode in C,exit</div><div class=\"line\">p = znode in C ordered just before n</div><div class=\"line\">if exist(p,true) wait for watch event</div><div class=\"line\">goto</div></pre></td></tr></table></figure></p>\n<figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div></pre></td><td class=\"code\"><pre><div class=\"line\">//unlock()</div><div class=\"line\">delete(n)</div></pre></td></tr></table></figure>\n<p>简单的来说就是,创建在parent l 下创建一个n,如果n是这些锁中最小的一个,则获得锁并退出<br>如果不是,则在n前面一个锁p设置exsist的watch,当不存在时,通知当前线程,跳转到创建并获得锁的步骤<br>删除锁则直接将该znode删除即可</p>\n<h2 id=\"与其他的系统的对比\"><a href=\"#与其他的系统的对比\" class=\"headerlink\" title=\"与其他的系统的对比\"></a>与其他的系统的对比</h2><h3 id=\"ZK-vs-Chubby\"><a href=\"#ZK-vs-Chubby\" class=\"headerlink\" title=\"ZK vs Chubby\"></a>ZK vs Chubby</h3><p>chubby也有一个类似于文件系统的接口,它也使用一种特定的协议保证副本之间的一致性;however,zk不是一种锁服务,它可以用于实现锁,但是在API中没有锁操作。zk允许客户端连接任意一个zk server,不只是leader,zk client可以使用local replica来服务数据以及管理watch,因为它的一致性模型比chubby更放松;</p>\n","site":{"data":{}},"excerpt":"","more":"<p>zookeeper是一个分布式的协调系统,目标是封装好关键的服务,提供简易使用的借口,建立更加复杂的协调原语。<br>并且,zookeeper是高效的,适用于以读为主的服务请求,任意一个server都可以服务于read请求,查找local replica并返回结果,而写操作则需要通过leader,并确保数据的一致性</p>\n<img src=\"/2017/11/27/ZooKeeper/service.png\" alt=\"service.png\" title=\"\">\n<p>一组server能够提高性能</p>\n<h2 id=\"Core-feature\"><a href=\"#Core-feature\" class=\"headerlink\" title=\"Core feature\"></a>Core feature</h2><p>在能大概读懂文章的内容之后,有以下的几个特点印象特别深刻:</p>\n<h3 id=\"Wait-free\"><a href=\"#Wait-free\" class=\"headerlink\" title=\"Wait-free\"></a>Wait-free</h3><p>zookeeper最大的特点就是高效的,对比其他同步的算法,采用异步的算法能够有效的防止部分machine而阻塞了整个服务进程。<br>但是采用异步的方式难以保证一致性,但采用<strong>FIFO Client Order</strong>和<strong>Linearizable Writes</strong>保证了系统的协调性<br><strong>move away from blocking primitives,such as locks</strong></p>\n<h3 id=\"Complicated-imformation\"><a href=\"#Complicated-imformation\" class=\"headerlink\" title=\"Complicated imformation\"></a>Complicated imformation</h3><p>Znode并不是被设计来存储数据的,但可以存储复杂的meta-data和configuration</p>\n<h3 id=\"Znode\"><a href=\"#Znode\" class=\"headerlink\" title=\"Znode\"></a>Znode</h3><p>znode可以被设置 <code>sequential flag</code>,当<code>emphemral node</code>被删除后,同时也被删除。<br>同时采用递增的命名方式,序号小的是先被设置的—-可以用于锁的设置<br>采用类似UNIX的命名方式进行管理,如图所示<br><img src=\"/2017/11/27/ZooKeeper/zknamespace.jpg\" alt=\"zknamespace.jpg\" title=\"\"></p>\n<h3 id=\"Client-API\"><a href=\"#Client-API\" class=\"headerlink\" title=\"Client API\"></a>Client API</h3><p>将复杂的一致性过程用简单的API进行包装,方便使用</p>\n<h2 id=\"Service-overview\"><a href=\"#Service-overview\" class=\"headerlink\" title=\"Service overview\"></a>Service overview</h2><p>下面来介绍一下整体的服务:</p>\n<ul>\n<li>znode作为数据对象,而客户端可以通过提供的API进行操纵</li>\n<li><code>wathces</code>作为一次性的触发器,在操作中绑定,用于数据更新的提醒</li>\n<li>提供API对<code>data object</code>进行操纵</li>\n</ul>\n<h3 id=\"Hierarchical-data-object\"><a href=\"#Hierarchical-data-object\" class=\"headerlink\" title=\"Hierarchical data object\"></a>Hierarchical data object</h3><p><code>data object</code>具有层次化的组织结构,像UNIX文件的文件组织结构。若要访问数据对象,输入该对象的绝对地址<br>当建立新的znode,可以设置<code>sequential flag</code>,使得znode命名中的数字是单调递增的<br>数据对象可以分成 <strong>Regular</strong>和<strong>Ephemeral</strong>两种:</p>\n<h4 id=\"Regular\"><a href=\"#Regular\" class=\"headerlink\" title=\"Regular\"></a>Regular</h4><p>客户端可以简单的创建并删除的对象</p>\n<h4 id=\"Ephemeral\"><a href=\"#Ephemeral\" class=\"headerlink\" title=\"Ephemeral\"></a>Ephemeral</h4><p>当客户端连接到ZK后可以创建临时的对象,并让ZK自动的删除(session 结束后)</p>\n<h3 id=\"Watches-callback\"><a href=\"#Watches-callback\" class=\"headerlink\" title=\"Watches callback\"></a>Watches callback</h3><p><code>watches</code>可以让客户端及时的接收某个数据的改变,可以在每次读取等操作时设置,仅仅只更新一次<br><code>getData("/foo",true)</code><br>当<code>/foo</code>发生改变之前,会通过<code>watch</code>通知client</p>\n<h3 id=\"Session\"><a href=\"#Session\" class=\"headerlink\" title=\"Session\"></a>Session</h3><ul>\n<li>client在连接到zk时确立一个session,当发出请求时获得一个session handle</li>\n<li>session设置了一个定时器,一段时间没有接收到信息,ZK认为client出错了</li>\n<li>watch 状态的变化</li>\n</ul>\n<h3 id=\"Client-API-1\"><a href=\"#Client-API-1\" class=\"headerlink\" title=\"Client API\"></a>Client API</h3><ol>\n<li><code>create(path,data,flags)</code>创建data object,flags —> regular, ephemeral, set the sequential flag</li>\n<li><code>delete(path,version)</code>当版本号相对应后,删除数据对象</li>\n<li><code>exists(path,watch)</code> 允许设置<code>watch</code>,当数据对象不存在之前会notify客户端</li>\n<li><code>getData(path,watch)</code> 返回相应的data、meta-data,在change之前notify客户端</li>\n<li><code>setData(path,data,version)</code>将data[]写入相应的path中</li>\n<li><code>getChildren(path,watch)</code>返回znode的全部的children名字</li>\n<li><code>sync(path)</code>等待所有的更新操作的结束,使得read到的数据是最新的。</li>\n</ol>\n<p>上述API均有同步和异步两个版本,异步调用,zk client保证按照操作被调用的顺序执行回调;<br>所有更新操作均有版本号,可决定是否做版本检查</p>\n<h2 id=\"System-guarantees\"><a href=\"#System-guarantees\" class=\"headerlink\" title=\"System guarantees\"></a>System guarantees</h2><p>整个系统通过下面两个条件保证整个系统的协调性,下面有一个case以选举的方式具体说明整个过程:<br>当选举出一个新的leader时,这个leader会改变系统的设置,并通知各个节点,需要保证一下两点:</p>\n<ol>\n<li>当leader开始改变某些设置的时候,我们不希望任何process开始使用这些正在改变的设置</li>\n<li>当新的leader在设置完全更新完之前宕机后,我们不希望任何的process去使用这不完整的设置</li>\n</ol>\n<p>总之,就是希望其他的process在configuration完全设置完后再使用<br>leader可以指定一个地址作为ready zone,当configuration设置完成后,其他的process去读取,完整自己的更新。<br>这样一个异步的方式,比起leader去更新每一个znode快的多</p>\n<h3 id=\"Linearizable-writes\"><a href=\"#Linearizable-writes\" class=\"headerlink\" title=\"Linearizable writes\"></a>Linearizable writes</h3><p>所有更新操作都是序列化的。由于ZK强调高性能,因此我们这的linerizability是异步的方式,允许client拥有多个操作,并保证每一个client是以FIFO的方式执行的</p>\n<h3 id=\"FIFO-client-order\"><a href=\"#FIFO-client-order\" class=\"headerlink\" title=\"FIFO client order\"></a>FIFO client order</h3><p>所有的操作都是按照clent发送的顺序的顺序去执行的。</p>\n<h2 id=\"Example-of-primitives\"><a href=\"#Example-of-primitives\" class=\"headerlink\" title=\"Example of primitives\"></a>Example of primitives</h2><p>下面是利用ZK的API进行的一些case的设计,帮助更好的实现系统的一致性</p>\n<h3 id=\"Configuration-Management\"><a href=\"#Configuration-Management\" class=\"headerlink\" title=\"Configuration Management\"></a>Configuration Management</h3><p>如上面所说的选举完新的leader后需要对configuration有很多新的操作,因此会指定一个znode,所有的server异步的对这个地址进行读取,完成自身的configuration的更新操作。<br>利用watch进行监视,以便及时的收取到更新configuration的notification</p>\n<h3 id=\"Rendezbous(集结地)\"><a href=\"#Rendezbous(集结地)\" class=\"headerlink\" title=\"Rendezbous(集结地)\"></a>Rendezbous(集结地)</h3><p>当client创建master和client程序时,由于是决策器完成的,client并不知道worker和master的地址和端口,因此无法让worker连接master;<br>因此,client可以创建一个Zr并将它的地址传递给指定的master和所有的worker。<br>当worker启动时,read Zr,并设置watch=true,当master写入address和port之后,notify worker</p>\n<h3 id=\"Group-Membership\"><a href=\"#Group-Membership\" class=\"headerlink\" title=\"Group Membership\"></a>Group Membership</h3><p>设置一个<code>eephemeral Node</code>Zg代表整个group,并设置<code>sequential flag</code>保持唯一性<br>当创建一个process,则在Zg下创建一个znode,process fail,znode自动移除。</p>\n<h3 id=\"Simple-Lock-without-Herd-Effect\"><a href=\"#Simple-Lock-without-Herd-Effect\" class=\"headerlink\" title=\"Simple Lock without Herd Effect\"></a>Simple Lock without Herd Effect</h3><p>ZK可以创建简单的锁服务,原理是获得锁,就创建一个znode,释放锁就删除相对应的znode。<br>而znode中的命名的大小是作为请求获得锁的process的顺序,从小到大<br>为了避免<strong>herd effect</strong>,释放锁后仅仅唤醒一个等待的线程<br><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div></pre></td><td class=\"code\"><pre><div class=\"line\">//lock()</div><div class=\"line\">n = create(l+"/lock-",EPHEMERAL|SEQUENTIAL)</div><div class=\"line\">C = getChildren(l,false)</div><div class=\"line\">if n is lowest znode in C,exit</div><div class=\"line\">p = znode in C ordered just before n</div><div class=\"line\">if exist(p,true) wait for watch event</div><div class=\"line\">goto</div></pre></td></tr></table></figure></p>\n<figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div></pre></td><td class=\"code\"><pre><div class=\"line\">//unlock()</div><div class=\"line\">delete(n)</div></pre></td></tr></table></figure>\n<p>简单的来说就是,创建在parent l 下创建一个n,如果n是这些锁中最小的一个,则获得锁并退出<br>如果不是,则在n前面一个锁p设置exsist的watch,当不存在时,通知当前线程,跳转到创建并获得锁的步骤<br>删除锁则直接将该znode删除即可</p>\n<h2 id=\"与其他的系统的对比\"><a href=\"#与其他的系统的对比\" class=\"headerlink\" title=\"与其他的系统的对比\"></a>与其他的系统的对比</h2><h3 id=\"ZK-vs-Chubby\"><a href=\"#ZK-vs-Chubby\" class=\"headerlink\" title=\"ZK vs Chubby\"></a>ZK vs Chubby</h3><p>chubby也有一个类似于文件系统的接口,它也使用一种特定的协议保证副本之间的一致性;however,zk不是一种锁服务,它可以用于实现锁,但是在API中没有锁操作。zk允许客户端连接任意一个zk server,不只是leader,zk client可以使用local replica来服务数据以及管理watch,因为它的一致性模型比chubby更放松;</p>\n"},{"title":"kubernetes","date":"2019-07-29T13:13:14.000Z","_content":"## Pod\npod 在启动的时候会创建两种类型的容器,一种用于初始化一些配置,另一种就是Running时的容器,卷是容器中持久化存储数据的空间,pod在启动的过程中只有当卷被挂载到目的地上之后,pod启动的流程才能继续下去,而pod在重新启动的时候,只需要把目录重新挂载就好,并不需要清除存储空间的数据\n\n### 网络\n同一个pod中的不同的容器是共享网络的,他们之间通过端口号进行标识,同一个pod上的所有的容器都会链接到同一个网络设备上,由Sandbox中的沙箱容器,就是`pause`容器\n\n#### Pause容器\n> In Kubernetes, the pause container serves as the \"parent container\" for all of the containers in your pod. The pause container has two core responsibilities. First, it serves as the basis of Linux namespace sharing in the pod. And second, with PID (process ID) namespace sharing enabled, it serves as PID 1 for each pod and reaps zombie processes.\n\n从概述上来看,他的意思是Pause容器作为所有容器的父容器,有以下两个作用:\n1. 共享Linux的命名空间\n2. 用于回收进程,释放进程空间,作为PID为1的进程\n\n功能实现上来说,它首先是创建了一个namespace,然后再把其他的docker加入到了已有的命名空间中,\n同时作为一个pid为1的进程,用于回收僵尸进程\n\n### pod的生命周期\n从 create 到 probe 到 running 到 shutdown 到 restart,整个流程的说明如下:\n\n1. pod被创建之后,需要检查健康状态,当前Pod 已经能够接受外部的请求时,才会将流量打到新的 Pod 上并继续对外提供服务 \n2. Pod 被删除之前都会触发一个`PreStop`的钩子,处理完之后才会删除这个pod,因此可以在这里进行优雅的删除\n\n#### 创建\nPod 的创建都是通过 SyncPod 来实现的,创建的过程大体上可以分为六个步骤:\n\n1. 计算 Pod 中沙盒和容器的变更;\n2. 强制停止 Pod 对应的沙盒;\n3. 强制停止所有不应该运行的容器;\n3. 为 Pod 创建新的沙盒;\n4. 创建 Pod 规格中指定的初始化容器;\n5. 依次创建 Pod 规格中指定的常规容器;\n\n每一个容器的创建的过程如下所示:\n\n1. 通过镜像拉取器获得当前容器中使用镜像的引用;\n2. 调用远程的 runtimeService 创建容器;\n3. 调用内部的生命周期方法 PreStartContainer 为当前的容器设置分配的 CPU 等资源;\n4. 调用远程的 runtimeService 开始运行镜像;\n5. 如果当前的容器包含 PostStart 钩子就会执行该回调;\n\n#### 健康性检查\n\n1. Kubelet使用liveness probe(存活探针)来确定何时重启容器\n2. Kubelet使用readiness probe(就绪探针)来确定容器是否已经就绪可以接受流量\n\n检查的方式可以如下所示:\n\n**liveness 命令**\n\n间隔一段时间就会去执行这个命令,判断系统是否还在运行中\n\n```yaml\n livenessProbe:\n exec:\n command:\n - cat\n - /tmp/healthy\n initialDelaySeconds: 5\n periodSeconds: 5\n```\n\n**liveness HTTP请求**\n\n定期会向8080端口发送一个请求,去校验这个服务是否还在运行中\n\n```yaml\nlivenessProbe:\n httpGet:\n path: /healthz\n port: 8080\n httpHeaders:\n - name: X-Custom-Header\n value: Awesome\n initialDelaySeconds: 3\n periodSeconds: 3\n```\n\n\n\n**liveness TCP请求**\n\nTCP检查的配置与HTTP检查非常相似。 此示例同时使用了readiness和liveness probe。 容器启动后5秒钟,kubelet将发送第一个readiness probe。 这将尝试连接到端口8080上的goproxy容器。如果探测成功,则该pod将被标记为就绪。Kubelet将每隔10秒钟执行一次该检查。\n\n```yaml\n livenessProbe:\n tcpSocket:\n port: 8080\n initialDelaySeconds: 15\n periodSeconds: 20\n```\n\n**Readiness 探针**\n\nReadiness probe的配置跟liveness probe很像","source":"_posts/kubernetes.md","raw":"---\ntitle: kubernetes\ndate: 2019-07-29 21:13:14\ncategories:\n- 分布式系统\n- kubernetes\ntags:\n- 秋招\n---\n## Pod\npod 在启动的时候会创建两种类型的容器,一种用于初始化一些配置,另一种就是Running时的容器,卷是容器中持久化存储数据的空间,pod在启动的过程中只有当卷被挂载到目的地上之后,pod启动的流程才能继续下去,而pod在重新启动的时候,只需要把目录重新挂载就好,并不需要清除存储空间的数据\n\n### 网络\n同一个pod中的不同的容器是共享网络的,他们之间通过端口号进行标识,同一个pod上的所有的容器都会链接到同一个网络设备上,由Sandbox中的沙箱容器,就是`pause`容器\n\n#### Pause容器\n> In Kubernetes, the pause container serves as the \"parent container\" for all of the containers in your pod. The pause container has two core responsibilities. First, it serves as the basis of Linux namespace sharing in the pod. And second, with PID (process ID) namespace sharing enabled, it serves as PID 1 for each pod and reaps zombie processes.\n\n从概述上来看,他的意思是Pause容器作为所有容器的父容器,有以下两个作用:\n1. 共享Linux的命名空间\n2. 用于回收进程,释放进程空间,作为PID为1的进程\n\n功能实现上来说,它首先是创建了一个namespace,然后再把其他的docker加入到了已有的命名空间中,\n同时作为一个pid为1的进程,用于回收僵尸进程\n\n### pod的生命周期\n从 create 到 probe 到 running 到 shutdown 到 restart,整个流程的说明如下:\n\n1. pod被创建之后,需要检查健康状态,当前Pod 已经能够接受外部的请求时,才会将流量打到新的 Pod 上并继续对外提供服务 \n2. Pod 被删除之前都会触发一个`PreStop`的钩子,处理完之后才会删除这个pod,因此可以在这里进行优雅的删除\n\n#### 创建\nPod 的创建都是通过 SyncPod 来实现的,创建的过程大体上可以分为六个步骤:\n\n1. 计算 Pod 中沙盒和容器的变更;\n2. 强制停止 Pod 对应的沙盒;\n3. 强制停止所有不应该运行的容器;\n3. 为 Pod 创建新的沙盒;\n4. 创建 Pod 规格中指定的初始化容器;\n5. 依次创建 Pod 规格中指定的常规容器;\n\n每一个容器的创建的过程如下所示:\n\n1. 通过镜像拉取器获得当前容器中使用镜像的引用;\n2. 调用远程的 runtimeService 创建容器;\n3. 调用内部的生命周期方法 PreStartContainer 为当前的容器设置分配的 CPU 等资源;\n4. 调用远程的 runtimeService 开始运行镜像;\n5. 如果当前的容器包含 PostStart 钩子就会执行该回调;\n\n#### 健康性检查\n\n1. Kubelet使用liveness probe(存活探针)来确定何时重启容器\n2. Kubelet使用readiness probe(就绪探针)来确定容器是否已经就绪可以接受流量\n\n检查的方式可以如下所示:\n\n**liveness 命令**\n\n间隔一段时间就会去执行这个命令,判断系统是否还在运行中\n\n```yaml\n livenessProbe:\n exec:\n command:\n - cat\n - /tmp/healthy\n initialDelaySeconds: 5\n periodSeconds: 5\n```\n\n**liveness HTTP请求**\n\n定期会向8080端口发送一个请求,去校验这个服务是否还在运行中\n\n```yaml\nlivenessProbe:\n httpGet:\n path: /healthz\n port: 8080\n httpHeaders:\n - name: X-Custom-Header\n value: Awesome\n initialDelaySeconds: 3\n periodSeconds: 3\n```\n\n\n\n**liveness TCP请求**\n\nTCP检查的配置与HTTP检查非常相似。 此示例同时使用了readiness和liveness probe。 容器启动后5秒钟,kubelet将发送第一个readiness probe。 这将尝试连接到端口8080上的goproxy容器。如果探测成功,则该pod将被标记为就绪。Kubelet将每隔10秒钟执行一次该检查。\n\n```yaml\n livenessProbe:\n tcpSocket:\n port: 8080\n initialDelaySeconds: 15\n periodSeconds: 20\n```\n\n**Readiness 探针**\n\nReadiness probe的配置跟liveness probe很像","slug":"kubernetes","published":1,"updated":"2019-07-29T14:19:51.831Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cjyohfvd40006sy0yyxsxrl58","content":"<h2 id=\"Pod\"><a href=\"#Pod\" class=\"headerlink\" title=\"Pod\"></a>Pod</h2><p>pod 在启动的时候会创建两种类型的容器,一种用于初始化一些配置,另一种就是Running时的容器,卷是容器中持久化存储数据的空间,pod在启动的过程中只有当卷被挂载到目的地上之后,pod启动的流程才能继续下去,而pod在重新启动的时候,只需要把目录重新挂载就好,并不需要清除存储空间的数据</p>\n<h3 id=\"网络\"><a href=\"#网络\" class=\"headerlink\" title=\"网络\"></a>网络</h3><p>同一个pod中的不同的容器是共享网络的,他们之间通过端口号进行标识,同一个pod上的所有的容器都会链接到同一个网络设备上,由Sandbox中的沙箱容器,就是<code>pause</code>容器</p>\n<h4 id=\"Pause容器\"><a href=\"#Pause容器\" class=\"headerlink\" title=\"Pause容器\"></a>Pause容器</h4><blockquote>\n<p>In Kubernetes, the pause container serves as the “parent container” for all of the containers in your pod. The pause container has two core responsibilities. First, it serves as the basis of Linux namespace sharing in the pod. And second, with PID (process ID) namespace sharing enabled, it serves as PID 1 for each pod and reaps zombie processes.</p>\n</blockquote>\n<p>从概述上来看,他的意思是Pause容器作为所有容器的父容器,有以下两个作用:</p>\n<ol>\n<li>共享Linux的命名空间</li>\n<li>用于回收进程,释放进程空间,作为PID为1的进程</li>\n</ol>\n<p>功能实现上来说,它首先是创建了一个namespace,然后再把其他的docker加入到了已有的命名空间中,<br>同时作为一个pid为1的进程,用于回收僵尸进程</p>\n<h3 id=\"pod的生命周期\"><a href=\"#pod的生命周期\" class=\"headerlink\" title=\"pod的生命周期\"></a>pod的生命周期</h3><p>从 create 到 probe 到 running 到 shutdown 到 restart,整个流程的说明如下:</p>\n<ol>\n<li>pod被创建之后,需要检查健康状态,当前Pod 已经能够接受外部的请求时,才会将流量打到新的 Pod 上并继续对外提供服务 </li>\n<li>Pod 被删除之前都会触发一个<code>PreStop</code>的钩子,处理完之后才会删除这个pod,因此可以在这里进行优雅的删除</li>\n</ol>\n<h4 id=\"创建\"><a href=\"#创建\" class=\"headerlink\" title=\"创建\"></a>创建</h4><p>Pod 的创建都是通过 SyncPod 来实现的,创建的过程大体上可以分为六个步骤:</p>\n<ol>\n<li>计算 Pod 中沙盒和容器的变更;</li>\n<li>强制停止 Pod 对应的沙盒;</li>\n<li>强制停止所有不应该运行的容器;</li>\n<li>为 Pod 创建新的沙盒;</li>\n<li>创建 Pod 规格中指定的初始化容器;</li>\n<li>依次创建 Pod 规格中指定的常规容器;</li>\n</ol>\n<p>每一个容器的创建的过程如下所示:</p>\n<ol>\n<li>通过镜像拉取器获得当前容器中使用镜像的引用;</li>\n<li>调用远程的 runtimeService 创建容器;</li>\n<li>调用内部的生命周期方法 PreStartContainer 为当前的容器设置分配的 CPU 等资源;</li>\n<li>调用远程的 runtimeService 开始运行镜像;</li>\n<li>如果当前的容器包含 PostStart 钩子就会执行该回调;</li>\n</ol>\n<h4 id=\"健康性检查\"><a href=\"#健康性检查\" class=\"headerlink\" title=\"健康性检查\"></a>健康性检查</h4><ol>\n<li>Kubelet使用liveness probe(存活探针)来确定何时重启容器</li>\n<li>Kubelet使用readiness probe(就绪探针)来确定容器是否已经就绪可以接受流量</li>\n</ol>\n<p>检查的方式可以如下所示:</p>\n<p><strong>liveness 命令</strong></p>\n<p>间隔一段时间就会去执行这个命令,判断系统是否还在运行中</p>\n<figure class=\"highlight yaml\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"attr\">livenessProbe:</span></div><div class=\"line\"><span class=\"attr\"> exec:</span></div><div class=\"line\"><span class=\"attr\"> command:</span></div><div class=\"line\"><span class=\"bullet\"> -</span> <span class=\"string\">cat</span></div><div class=\"line\"><span class=\"bullet\"> -</span> <span class=\"string\">/tmp/healthy</span></div><div class=\"line\"><span class=\"attr\"> initialDelaySeconds:</span> <span class=\"number\">5</span></div><div class=\"line\"><span class=\"attr\"> periodSeconds:</span> <span class=\"number\">5</span></div></pre></td></tr></table></figure>\n<p><strong>liveness HTTP请求</strong></p>\n<p>定期会向8080端口发送一个请求,去校验这个服务是否还在运行中</p>\n<figure class=\"highlight yaml\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"attr\">livenessProbe:</span></div><div class=\"line\"><span class=\"attr\"> httpGet:</span></div><div class=\"line\"><span class=\"attr\"> path:</span> <span class=\"string\">/healthz</span></div><div class=\"line\"><span class=\"attr\"> port:</span> <span class=\"number\">8080</span></div><div class=\"line\"><span class=\"attr\"> httpHeaders:</span></div><div class=\"line\"><span class=\"attr\"> - name:</span> <span class=\"string\">X-Custom-Header</span></div><div class=\"line\"><span class=\"attr\"> value:</span> <span class=\"string\">Awesome</span></div><div class=\"line\"><span class=\"attr\"> initialDelaySeconds:</span> <span class=\"number\">3</span></div><div class=\"line\"><span class=\"attr\"> periodSeconds:</span> <span class=\"number\">3</span></div></pre></td></tr></table></figure>\n<p><strong>liveness TCP请求</strong></p>\n<p>TCP检查的配置与HTTP检查非常相似。 此示例同时使用了readiness和liveness probe。 容器启动后5秒钟,kubelet将发送第一个readiness probe。 这将尝试连接到端口8080上的goproxy容器。如果探测成功,则该pod将被标记为就绪。Kubelet将每隔10秒钟执行一次该检查。</p>\n<figure class=\"highlight yaml\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"attr\">livenessProbe:</span></div><div class=\"line\"><span class=\"attr\"> tcpSocket:</span></div><div class=\"line\"><span class=\"attr\"> port:</span> <span class=\"number\">8080</span></div><div class=\"line\"><span class=\"attr\"> initialDelaySeconds:</span> <span class=\"number\">15</span></div><div class=\"line\"><span class=\"attr\"> periodSeconds:</span> <span class=\"number\">20</span></div></pre></td></tr></table></figure>\n<p><strong>Readiness 探针</strong></p>\n<p>Readiness probe的配置跟liveness probe很像</p>\n","site":{"data":{}},"excerpt":"","more":"<h2 id=\"Pod\"><a href=\"#Pod\" class=\"headerlink\" title=\"Pod\"></a>Pod</h2><p>pod 在启动的时候会创建两种类型的容器,一种用于初始化一些配置,另一种就是Running时的容器,卷是容器中持久化存储数据的空间,pod在启动的过程中只有当卷被挂载到目的地上之后,pod启动的流程才能继续下去,而pod在重新启动的时候,只需要把目录重新挂载就好,并不需要清除存储空间的数据</p>\n<h3 id=\"网络\"><a href=\"#网络\" class=\"headerlink\" title=\"网络\"></a>网络</h3><p>同一个pod中的不同的容器是共享网络的,他们之间通过端口号进行标识,同一个pod上的所有的容器都会链接到同一个网络设备上,由Sandbox中的沙箱容器,就是<code>pause</code>容器</p>\n<h4 id=\"Pause容器\"><a href=\"#Pause容器\" class=\"headerlink\" title=\"Pause容器\"></a>Pause容器</h4><blockquote>\n<p>In Kubernetes, the pause container serves as the “parent container” for all of the containers in your pod. The pause container has two core responsibilities. First, it serves as the basis of Linux namespace sharing in the pod. And second, with PID (process ID) namespace sharing enabled, it serves as PID 1 for each pod and reaps zombie processes.</p>\n</blockquote>\n<p>从概述上来看,他的意思是Pause容器作为所有容器的父容器,有以下两个作用:</p>\n<ol>\n<li>共享Linux的命名空间</li>\n<li>用于回收进程,释放进程空间,作为PID为1的进程</li>\n</ol>\n<p>功能实现上来说,它首先是创建了一个namespace,然后再把其他的docker加入到了已有的命名空间中,<br>同时作为一个pid为1的进程,用于回收僵尸进程</p>\n<h3 id=\"pod的生命周期\"><a href=\"#pod的生命周期\" class=\"headerlink\" title=\"pod的生命周期\"></a>pod的生命周期</h3><p>从 create 到 probe 到 running 到 shutdown 到 restart,整个流程的说明如下:</p>\n<ol>\n<li>pod被创建之后,需要检查健康状态,当前Pod 已经能够接受外部的请求时,才会将流量打到新的 Pod 上并继续对外提供服务 </li>\n<li>Pod 被删除之前都会触发一个<code>PreStop</code>的钩子,处理完之后才会删除这个pod,因此可以在这里进行优雅的删除</li>\n</ol>\n<h4 id=\"创建\"><a href=\"#创建\" class=\"headerlink\" title=\"创建\"></a>创建</h4><p>Pod 的创建都是通过 SyncPod 来实现的,创建的过程大体上可以分为六个步骤:</p>\n<ol>\n<li>计算 Pod 中沙盒和容器的变更;</li>\n<li>强制停止 Pod 对应的沙盒;</li>\n<li>强制停止所有不应该运行的容器;</li>\n<li>为 Pod 创建新的沙盒;</li>\n<li>创建 Pod 规格中指定的初始化容器;</li>\n<li>依次创建 Pod 规格中指定的常规容器;</li>\n</ol>\n<p>每一个容器的创建的过程如下所示:</p>\n<ol>\n<li>通过镜像拉取器获得当前容器中使用镜像的引用;</li>\n<li>调用远程的 runtimeService 创建容器;</li>\n<li>调用内部的生命周期方法 PreStartContainer 为当前的容器设置分配的 CPU 等资源;</li>\n<li>调用远程的 runtimeService 开始运行镜像;</li>\n<li>如果当前的容器包含 PostStart 钩子就会执行该回调;</li>\n</ol>\n<h4 id=\"健康性检查\"><a href=\"#健康性检查\" class=\"headerlink\" title=\"健康性检查\"></a>健康性检查</h4><ol>\n<li>Kubelet使用liveness probe(存活探针)来确定何时重启容器</li>\n<li>Kubelet使用readiness probe(就绪探针)来确定容器是否已经就绪可以接受流量</li>\n</ol>\n<p>检查的方式可以如下所示:</p>\n<p><strong>liveness 命令</strong></p>\n<p>间隔一段时间就会去执行这个命令,判断系统是否还在运行中</p>\n<figure class=\"highlight yaml\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"attr\">livenessProbe:</span></div><div class=\"line\"><span class=\"attr\"> exec:</span></div><div class=\"line\"><span class=\"attr\"> command:</span></div><div class=\"line\"><span class=\"bullet\"> -</span> <span class=\"string\">cat</span></div><div class=\"line\"><span class=\"bullet\"> -</span> <span class=\"string\">/tmp/healthy</span></div><div class=\"line\"><span class=\"attr\"> initialDelaySeconds:</span> <span class=\"number\">5</span></div><div class=\"line\"><span class=\"attr\"> periodSeconds:</span> <span class=\"number\">5</span></div></pre></td></tr></table></figure>\n<p><strong>liveness HTTP请求</strong></p>\n<p>定期会向8080端口发送一个请求,去校验这个服务是否还在运行中</p>\n<figure class=\"highlight yaml\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"attr\">livenessProbe:</span></div><div class=\"line\"><span class=\"attr\"> httpGet:</span></div><div class=\"line\"><span class=\"attr\"> path:</span> <span class=\"string\">/healthz</span></div><div class=\"line\"><span class=\"attr\"> port:</span> <span class=\"number\">8080</span></div><div class=\"line\"><span class=\"attr\"> httpHeaders:</span></div><div class=\"line\"><span class=\"attr\"> - name:</span> <span class=\"string\">X-Custom-Header</span></div><div class=\"line\"><span class=\"attr\"> value:</span> <span class=\"string\">Awesome</span></div><div class=\"line\"><span class=\"attr\"> initialDelaySeconds:</span> <span class=\"number\">3</span></div><div class=\"line\"><span class=\"attr\"> periodSeconds:</span> <span class=\"number\">3</span></div></pre></td></tr></table></figure>\n<p><strong>liveness TCP请求</strong></p>\n<p>TCP检查的配置与HTTP检查非常相似。 此示例同时使用了readiness和liveness probe。 容器启动后5秒钟,kubelet将发送第一个readiness probe。 这将尝试连接到端口8080上的goproxy容器。如果探测成功,则该pod将被标记为就绪。Kubelet将每隔10秒钟执行一次该检查。</p>\n<figure class=\"highlight yaml\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"attr\">livenessProbe:</span></div><div class=\"line\"><span class=\"attr\"> tcpSocket:</span></div><div class=\"line\"><span class=\"attr\"> port:</span> <span class=\"number\">8080</span></div><div class=\"line\"><span class=\"attr\"> initialDelaySeconds:</span> <span class=\"number\">15</span></div><div class=\"line\"><span class=\"attr\"> periodSeconds:</span> <span class=\"number\">20</span></div></pre></td></tr></table></figure>\n<p><strong>Readiness 探针</strong></p>\n<p>Readiness probe的配置跟liveness probe很像</p>\n"},{"title":"LeetCode-Week-1","date":"2017-10-28T07:13:19.000Z","_content":">写在前面\n\n实在是无法忍受自己写代码如此差劲,心里所想的东西完无法用代码表达出来,因此打算从头开始一点一点的刷 LeetCode \n我的计划是每天**至少**写两道题并进行总结,一周发一次Blog,虽然这个可能花费大量时间,但胜在能够打牢基础,我认为这还是值得的\n每道题都会由以下几个部分组成:\n- 题目\n- 代码\n- 如果没有代码,就写没有考虑到的地方\n- 优秀的代码和解析\n\n\n希望能坚持啊!兄弟!\n\n## Spiral Matrix\nGiven a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order.\n\nFor example,\nGiven the following matrix:\n```c++\n[\n [ 1, 2, 3 ],\n [ 4, 5, 6 ],\n [ 7, 8, 9 ]\n]\n```\nYou should return `[1,2,3,6,9,8,7,4,5]`\n### 解析\n这道题就是以螺旋的方式遍历整个二维数组,我的思路是设置1个变量k用于计数,还有4个变量用于指向四个遍历方向,还有一个二维数组记录每一个位置上的数字是否被访问过,若是,跳过,有点傻有点啰嗦\n### 代码\n```c\n vector<int> spiralOrder(vector<vector<int>>& matrix) {\n vector<int>res;\n if(matrix.empty())\n return res;\n\tint m = matrix.size();\n\tint n = matrix[0].size();\n\tint a[50][50];\n\tmemset(a, 0, sizeof(a));\n\tint k = 0, i, j;\n\tint q, w, e, r;\n\tq = r = 0;\n\tw = n-1;\n\te = m-1;\n\twhile (k < m*n)\n\t{\n\t\tfor (i = 0; i < n; i++)\n\t\t{\n\t\t\tif (a[q][i] == 0)\n\t\t\t{\n\t\t\t\tres.push_back(matrix[q][i]);\n\t\t\t\ta[q][i] = 1;\n\t\t\t\tk++;\n\t\t\t}\n\t\t}\n\t\tq++;\n\t\tfor (i = 0; i < m; i++)\n\t\t{\n\t\t\tif (a[i][w] == 0)\n\t\t\t{\n\t\t\t\tres.push_back(matrix[i][w]);\n\t\t\t\ta[i][w] = 1;\n\t\t\t\tk++;\n\t\t\t}\n\t\t}\n\t\tw--;\n\t\tfor (i = n - 1; i >= 0; i--)\n\t\t{\n\t\t\tif (a[e][i] == 0)\n\t\t\t{\n\t\t\t\tres.push_back(matrix[e][i]);\n\t\t\t\ta[e][i] = 1;\n\t\t\t\tk++;\n\t\t\t}\n\t\t}\n\t\te--;\n\t\tfor (i = m - 1; i >= 0; i--)\n\t\t{\n\t\t\tif (a[i][r] == 0)\n\t\t\t{\n\t\t\t\tres.push_back(matrix[i][r]);\n\t\t\t\ta[i][r] = 1;\n\t\t\t\tk++;\n\t\t\t}\n\t\t}\n\t\tr++;\n\t}\n\treturn res;\n}\n```\n### 简便的代码\n```c\npublic class Solution {\n public List<Integer> spiralOrder(int[][] matrix) {\n \n List<Integer> res = new ArrayList<Integer>();\n \n if (matrix.length == 0) {\n return res;\n }\n \n int rowBegin = 0;\n int rowEnd = matrix.length-1;\n int colBegin = 0;\n int colEnd = matrix[0].length - 1;\n \n while (rowBegin <= rowEnd && colBegin <= colEnd) {\n // Traverse Right\n for (int j = colBegin; j <= colEnd; j ++) {\n res.add(matrix[rowBegin][j]);\n }\n rowBegin++;\n \n // Traverse Down\n for (int j = rowBegin; j <= rowEnd; j ++) {\n res.add(matrix[j][colEnd]);\n }\n colEnd--;\n \n if (rowBegin <= rowEnd) {\n // Traverse Left\n for (int j = colEnd; j >= colBegin; j --) {\n res.add(matrix[rowEnd][j]);\n }\n }\n rowEnd--;\n \n if (colBegin <= colEnd) {\n // Traver Up\n for (int j = rowEnd; j >= rowBegin; j --) {\n res.add(matrix[j][colBegin]);\n }\n }\n colBegin ++;\n }\n \n return res;\n }\n```\n同样也是while中套着循环,同时也是利用了四个变量分别指向行、列的开始、结尾的位置,可以通过这四个变量设置循环的次数,而不用另外开辟一个数组来记录该数字是否被访问过。\n\n## Maximum Subarray\nFind the contiguous subarray within an array (containing at least one number) which has the largest sum.\n\nFor example, given the array`[-2,1,-3,4,-1,2,1,-5,4]`,\nthe contiguous subarray`[4,-1,2,1]`has the largest sum =`6`.\n### 愚蠢的代码\n这代码一看就是会超时的,想过使用DP算法,但是不知道怎么处理**连续**\n```\nint maxSubArray(vector<int>& nums) {\n\tint max_sum = INT_MIN;\n\tint i;\n\tfor (i = 0; i < nums.size(); i++)\n\t{\n\t\tint temp = func(i, nums);\n\t\tmax_sum = max(max_sum, temp);\n\t}\n\treturn max_sum;\n}\n\nint func(int begin, vector<int> &nums)\n{\n\tint sum = 0;\n\tint max_sum = nums[begin];\n\tint i;\n\tfor (i = begin ; i < nums.size(); i++)\n\t{\n\t\tsum += nums[i];\n\t\tmax_sum = max(sum, max_sum);\n\t}\n\treturn max_sum;\n}\n```\n### 优秀的代码\n``` c\npublic int maxSubArray(int[] A) {\n int n = A.length;\n int[] dp = new int[n];//dp[i]表示从A[0]-A[i]中连续的字串最大值\n dp[0] = A[0];\n int max = dp[0];\n \n for(int i = 1; i < n; i++){\n dp[i] = A[i] + (dp[i - 1] > 0 ? dp[i - 1] : 0);\n max = Math.max(max, dp[i]);\n }\n \n return max;\n}\n```\n当前的字串最大值,是当前的值 + 之前的的字串的值 是否大于0,若小于,相当于重新开始计算子串的最大值\n\n## 56. Merge Intervals\n\nGiven a collection of intervals, merge all overlapping intervals.\n\nFor example,\nGiven`[1,3],[2,6],[8,10],[15,18]`,\nreturn`[1,6],[8,10],[15,18]`.\n### 解析\n这道题看上去还是十分简单的,就是遍历如果前一个的end大于后一个start,则赋值并继续循环\n如果不符合,就加到结果的`vector`中,并更新start和end的值\n**注意点**\n\n1. 首先要对其进行排序,按照start升序\n2. 考虑多种情况,也有可能前一个包含后一个\n\n### 代码\n```\nstatic bool cmp(const Interval &a, const Interval &b)\n{\n\treturn a.start < b.start;\n}\n \nvector<Interval> merge(vector<Interval>& intervals) {\n\tint i, j, k;\n\tvector<Interval>res;\n if (intervals.empty())\n\t\treturn res;\n sort(intervals.begin(), intervals.end(), cmp);\n\tint start = intervals[i].start;\n\tint end = intervals[i].end;\n\tfor (i = 1; i < intervals.size(); i++)\n\t{\n if (intervals[i].start >= start && intervals[i].end <= end)\n continue;\n\t\tif (intervals[i].start <= end)\n\t\t{\n\t\t\tend = intervals[i].end;\n\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tInterval tmp = { start, end };\n\t\t\tres.push_back(tmp);\n\t\t\tstart = intervals[i].start;\n\t\t\tend = intervals[i].end;\n\t\t}\n\t}\n Interval tmp = { start, end };\n\tres.push_back(tmp);\n\treturn res;\n \n}\n```\n## 55. Jump Game\nGiven an array of non-negative integers, you are initially positioned at the first index of the array.\n\nEach element in the array represents your maximum jump length at that position.\n\nDetermine if you are able to reach the last index.\n\nFor example:\nA = `[2,3,1,1,4]`, return `true`.\n\nA = `[3,2,1,0,4]`, return `false`.\n\n### 解题思路\n用distance表示i之前最远的距离,如果 `i > diatance`,说明永远到达不了i,就退出循环\n当distance >= n-1时,至少能跳到最后\n\n夏令营时,我采用的是深搜,显然会超时\n\n### 代码\n```\nbool canJump(vector<int>& nums) {\n\tint distance = 0;\n\tint n = nums.size();\n\tfor (int i = 0; i < n && i <= distance; i++)\n\t{\n\t\tdistance = max(nums[i] + i, distance);\n\t}\n\tif (distance < n - 1)\n\t\treturn false;\n\treturn true;\n}\n```\n## 57.Insert Interval\n\nGiven a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary).\n\nYou may assume that the intervals were initially sorted according to their start times.\n\n**Example 1:**\nGiven intervals`[1,3],[6,9]`, insert and merge `[2,5]` in as `[1,5],[6,9]`.\n\n**Example 2:**\nGiven `[1,2],[3,5],[6,7],[8,10],[12,16]`, insert and merge `[4,9]` in as `[1,2],[3,10],[12,16]`.\n\nThis is because the new interval `[4,9]` overlaps with `[3,5],[6,7],[8,10]`.\n### 解题思路\n这个合并可以分为三个部分\n1. `end < newInterval.stat`,直接将其添加至新的`res`中\n2. `start > newInterval.end`,同上\n3. 有部分相交,`start <= newInterval.end`的部分,则`start`取最小值,`end`取最大值,并`push`进入结果中\n### 代码\n\n```c\nvector<Interval> insert(vector<Interval>& intervals, Interval newInterval) {\n\tint i = 0 ;\n\tint s = newInterval.start;\n\tint e = newInterval.end;\n\tvector<Interval>res;\n\tif (intervals.empty())\n\t{\n\t\tintervals.push_back(newInterval);\n\t\treturn intervals;\n\t}\n\t//前部\n\n\twhile (i < intervals.size() && newInterval.start > intervals[i].end)\n\t{\n\t\tres.push_back(intervals[i]);\n\t\ti++;\n\t}\n\t//中间\n\twhile (i < intervals.size() && intervals[i].start <= newInterval.end)\n\t{\n\t\ts = min(s, intervals[i].start);\n\t\te = max(e, intervals[i].end);\n\t\ti++;\n\t}\n\tInterval t{ s, e };\n\tres.push_back(t);\n\n\t//后部\n\twhile (i < intervals.size() && newInterval.end < intervals[i].start)\n\t{\n\t\tres.push_back(intervals[i]);\n\t\ti++;\n\t}\n\treturn res;\n}\n```\n\n## Spiral Matrix II\nGiven an integer n, generate a square matrix filled with elements from 1 to n2 in spiral order.\n\nFor example,\nGiven n = `3`,\n\nYou should return the following matrix:\n```\n[\n [ 1, 2, 3 ],\n [ 8, 9, 4 ],\n [ 7, 6, 5 ]\n]\n```\n\n### 解题思路\n和之前的遍历类似,不再赘述,重要的是如何用verctor定义一个二维数组!!!\n`vector<vector<int>> res(n,vector<int>(n))` !!!\n`vector<vector<int>> res(n,vector<int>(n))` !!!\n### 代码\n```c\nvector<vector<int>> generateMatrix(int n) {\n\tvector<vector<int>> res(n, vector<int>(n));//!!!!!!!!!!!!\n\tint i, j, k, l;\n\tint c = 1;\n\ti = j = 0;\n\tk = l = n - 1;\n\twhile (c <= n*n) \n\t{\n\t\tfor (int m = j; m <= l; m++)\n\t\t{\n\t\t\tres[i][m] = c;\n\t\t\tc++;\n\t\t}\n\t\ti++;\n\t\tfor (int m = i; m <= k; m++)\n\t\t{\n\t\t\tres[m][l] = c;\n\t\t\tc++;\n\t\t}\n\t\tl--;\n\t\tfor (int m = l; m >= j; m--)\n\t\t{\n\t\t\tres[k][m] = c;\n\t\t\tc++;\n\t\t}\n\t\tk--;\n\t\tfor (int m = k; m >= i; m--)\n\t\t{\n\t\t\tres[m][j] = c;\n\t\t\tc++;\n\t\t}\n\t\tj++;\n\t}\n\treturn res;\n}\n```\n## Unique Paths II\nFollow up for \"Unique Paths\":\n\nNow consider if some obstacles are added to the grids. How many unique paths would there be?\n\nAn obstacle and empty space is marked as `1` and `0` respectively in the grid.\n\nFor example,\nThere is one obstacle in the middle of a 3x3 grid as illustrated below.\n```\n[\n [0,0,0],\n [0,1,0],\n [0,0,0]\n]\n```\nThe total number of unique paths is `2`.\n\nNote: m and n will be at most 100.\n\n### 解决思路\n这道题采用DP算法,到达当前点的路径数量等于左边和上面点的路径之和,同时要考虑边界的问题以及碰到障碍物时的处理办法\n###代码\n```\nint uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {\n\tint row = obstacleGrid.size();\n\tint col = obstacleGrid[0].size();\n\tint i, j;\n\tvector<vector<int>> dp(row, vector<int>(col, 0));\n\tfor (i = 0; i < row; i++)\n\t{\n\t\tfor (j = 0; j < col; j++)\n\t\t{\n\t\t\tif (obstacleGrid[i][j] == 1)\n\t\t\t\tdp[i][j] = 0;\n\t\t\telse if (i == 0 && j == 0)\n\t\t\t\tdp[i][j] = 1;\n\t\t\telse if (i == 0 && j > 0)\n\t\t\t\tdp[i][j] = dp[i][j - 1];\n\t\t\telse if (j == 0 && i > 0)\n\t\t\t\tdp[i][j] = dp[i - 1][j];\n\t\t\telse if (i > 0 && j > 0)\n\t\t\t\tdp[i][j] = dp[i - 1][j] + dp[i][j - 1];\n\t\t}\n\t}\n\treturn dp[row - 1][col - 1];\n}\n```\n还有直接将边界扩大一圈,直接考虑边界的问题,代码如下\n```\n int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {\n int m = obstacleGrid.size(), n = obstacleGrid[0].size();\n vector<vector<int> > dp(m + 1, vector<int> (n + 1, 0));\n dp[0][1] = 1;\n for (int i = 1; i <= m; i++)\n for (int j = 1; j <= n; j++)\n if (!obstacleGrid[i - 1][j - 1])\n dp[i][j] = dp[i - 1][j] + dp[i][j - 1];\n return dp[m][n];\n } \n```\n\n## 93. Restore IP Addresses\nGiven a string containing only digits, restore it by returning all possible valid IP address combinations.\n\nFor example:\nGiven `\"25525511135\"`,\n\nreturn `[\"255.255.11.135\", \"255.255.111.35\"]`. (Order does not matter)\n### 解题思路\n采用dfs,每一次长度为1~3,如果这一小块的长度为3,且值>255不符合;如果起始值为0且长度>1则不符合\n最终当count==4并且指针指向结尾的时候符合要求并添加进结果中\n### 代码\n```\nvoid dfs(string ip,vector<string>& res, int idx, int count, string s)\n{\n\tif (count > 4)\n\t\treturn;\n\tif (count == 4 && idx == ip.length())\n\t{\n\t\tres.push_back(s);\n\t\treturn;\n\t}\n\tfor (int i = 1; i < 4; i++)\n\t{\n\t\tif (idx + i > ip.length())\n\t\t\tbreak;\n\t\tstring t = ip.substr(idx , i);\n\t\tif ((t[0] == '0' && t.length() > 1) || (i == 3 && atoi(t.c_str()) > 255))\n\t\t\tcontinue;\n\t\tdfs(ip, res, idx + i, count + 1, s + t + (count == 3 ? \"\" : \".\"));\n\t}\n}\n\n\nvector<string> restoreIpAddresses(string s) {\n\tvector<string>res;\n\tdfs(s, res, 0, 0, \"\");\n\treturn res;\n}\n```\n## 79. Word Search\nGiven a 2D board and a word, find if the word exists in the grid.\n\nThe word can be constructed from letters of sequentially adjacent cell, where \"adjacent\" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.\n\nFor example,\nGiven **board** =\n```\n[\n ['A','B','C','E'],\n ['S','F','C','S'],\n ['A','D','E','E']\n]\n```\nword = `\"ABCCED\"`, -> returns `true`,\nword = `\"SEE\"`, -> returns `true`,\nword = `\"ABCB\"`, -> returns `false`.\n### 解题思路\n这一次真的可以采用深搜的方法,从一个匹配的字母处开始寻找,规定查找的四个方向,如果当前字母与字符串中的字母相同,搜索下一个字母。\n同时,不能重复字母,因此可以创建一个与`board`相同大小的二维向量用于表示是否访问过\n记得回溯!\n## 代码\n```\nvoid find(int i, int j, int index, string word, vector<vector<char>>& board, int& res, vector<vector<int>>& visited)\n{\n int idx[] = { 1, -1, 0, 0 };\n int idy[] = { 0, 0, -1, 1 };\n\tif (res == 0)\n\t{\n\t\tif (i < 0 || i >= board.size() || j < 0 || j >= board[i].size()||visited[i][j])\n\t\t\treturn;\n\t\tif (board[i][j] != word[index])\n\t\t\treturn;\n\t\tif (index == word.length() - 1)\n\t\t{\n\t\t\tres = 1;\n\t\t\treturn;\n\t\t}\n\t\tfor (int k = 0; k < 4; k++)\n\t\t{\n\t\t\tint tx = i + idx[k];\n\t\t\tint ty = j + idy[k];\n\t\t\tvisited[i][j] = 1;\n\t\t\tfind(tx, ty, index + 1, word, board, res,visited);\n\t\t\tvisited[i][j] = 0;\n\n\t\t}\n\t}\n}\n\nbool exist(vector<vector<char>>& board, string word) {\n\tvector<vector<int>> visited(board.size(), vector<int>(board[0].size(), 0));\n\tint res = 0;\n\tint i, j, k;\n\tfor (i = 0; i < board.size(); i++)\n\t{\n\t\tfor (j = 0; j < board[i].size(); j++)\n\t\t{\n\t\t\tif (board[i][j] == word[0])\n\t\t\t{\n\t\t\t\tfind(i, j, 0, word, board, res,visited);\n\t\t\t\tif (res)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (res)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn res;\n}\n```\n## 后记\n这一周也终于结束了,感觉好像有点堕落有点拖拉,效率很低,希望加把劲咯\n总是需要一门很熟悉的语言!go 和 C++?\n\n\n\n\n\n\n","source":"_posts/LeetCode-Week-1.md","raw":"---\ntitle: LeetCode-Week-1\ndate: 2017-10-28 15:13:19\ncategories:\n- LeetCode\ntags:\n- LeetCode\n---\n>写在前面\n\n实在是无法忍受自己写代码如此差劲,心里所想的东西完无法用代码表达出来,因此打算从头开始一点一点的刷 LeetCode \n我的计划是每天**至少**写两道题并进行总结,一周发一次Blog,虽然这个可能花费大量时间,但胜在能够打牢基础,我认为这还是值得的\n每道题都会由以下几个部分组成:\n- 题目\n- 代码\n- 如果没有代码,就写没有考虑到的地方\n- 优秀的代码和解析\n\n\n希望能坚持啊!兄弟!\n\n## Spiral Matrix\nGiven a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order.\n\nFor example,\nGiven the following matrix:\n```c++\n[\n [ 1, 2, 3 ],\n [ 4, 5, 6 ],\n [ 7, 8, 9 ]\n]\n```\nYou should return `[1,2,3,6,9,8,7,4,5]`\n### 解析\n这道题就是以螺旋的方式遍历整个二维数组,我的思路是设置1个变量k用于计数,还有4个变量用于指向四个遍历方向,还有一个二维数组记录每一个位置上的数字是否被访问过,若是,跳过,有点傻有点啰嗦\n### 代码\n```c\n vector<int> spiralOrder(vector<vector<int>>& matrix) {\n vector<int>res;\n if(matrix.empty())\n return res;\n\tint m = matrix.size();\n\tint n = matrix[0].size();\n\tint a[50][50];\n\tmemset(a, 0, sizeof(a));\n\tint k = 0, i, j;\n\tint q, w, e, r;\n\tq = r = 0;\n\tw = n-1;\n\te = m-1;\n\twhile (k < m*n)\n\t{\n\t\tfor (i = 0; i < n; i++)\n\t\t{\n\t\t\tif (a[q][i] == 0)\n\t\t\t{\n\t\t\t\tres.push_back(matrix[q][i]);\n\t\t\t\ta[q][i] = 1;\n\t\t\t\tk++;\n\t\t\t}\n\t\t}\n\t\tq++;\n\t\tfor (i = 0; i < m; i++)\n\t\t{\n\t\t\tif (a[i][w] == 0)\n\t\t\t{\n\t\t\t\tres.push_back(matrix[i][w]);\n\t\t\t\ta[i][w] = 1;\n\t\t\t\tk++;\n\t\t\t}\n\t\t}\n\t\tw--;\n\t\tfor (i = n - 1; i >= 0; i--)\n\t\t{\n\t\t\tif (a[e][i] == 0)\n\t\t\t{\n\t\t\t\tres.push_back(matrix[e][i]);\n\t\t\t\ta[e][i] = 1;\n\t\t\t\tk++;\n\t\t\t}\n\t\t}\n\t\te--;\n\t\tfor (i = m - 1; i >= 0; i--)\n\t\t{\n\t\t\tif (a[i][r] == 0)\n\t\t\t{\n\t\t\t\tres.push_back(matrix[i][r]);\n\t\t\t\ta[i][r] = 1;\n\t\t\t\tk++;\n\t\t\t}\n\t\t}\n\t\tr++;\n\t}\n\treturn res;\n}\n```\n### 简便的代码\n```c\npublic class Solution {\n public List<Integer> spiralOrder(int[][] matrix) {\n \n List<Integer> res = new ArrayList<Integer>();\n \n if (matrix.length == 0) {\n return res;\n }\n \n int rowBegin = 0;\n int rowEnd = matrix.length-1;\n int colBegin = 0;\n int colEnd = matrix[0].length - 1;\n \n while (rowBegin <= rowEnd && colBegin <= colEnd) {\n // Traverse Right\n for (int j = colBegin; j <= colEnd; j ++) {\n res.add(matrix[rowBegin][j]);\n }\n rowBegin++;\n \n // Traverse Down\n for (int j = rowBegin; j <= rowEnd; j ++) {\n res.add(matrix[j][colEnd]);\n }\n colEnd--;\n \n if (rowBegin <= rowEnd) {\n // Traverse Left\n for (int j = colEnd; j >= colBegin; j --) {\n res.add(matrix[rowEnd][j]);\n }\n }\n rowEnd--;\n \n if (colBegin <= colEnd) {\n // Traver Up\n for (int j = rowEnd; j >= rowBegin; j --) {\n res.add(matrix[j][colBegin]);\n }\n }\n colBegin ++;\n }\n \n return res;\n }\n```\n同样也是while中套着循环,同时也是利用了四个变量分别指向行、列的开始、结尾的位置,可以通过这四个变量设置循环的次数,而不用另外开辟一个数组来记录该数字是否被访问过。\n\n## Maximum Subarray\nFind the contiguous subarray within an array (containing at least one number) which has the largest sum.\n\nFor example, given the array`[-2,1,-3,4,-1,2,1,-5,4]`,\nthe contiguous subarray`[4,-1,2,1]`has the largest sum =`6`.\n### 愚蠢的代码\n这代码一看就是会超时的,想过使用DP算法,但是不知道怎么处理**连续**\n```\nint maxSubArray(vector<int>& nums) {\n\tint max_sum = INT_MIN;\n\tint i;\n\tfor (i = 0; i < nums.size(); i++)\n\t{\n\t\tint temp = func(i, nums);\n\t\tmax_sum = max(max_sum, temp);\n\t}\n\treturn max_sum;\n}\n\nint func(int begin, vector<int> &nums)\n{\n\tint sum = 0;\n\tint max_sum = nums[begin];\n\tint i;\n\tfor (i = begin ; i < nums.size(); i++)\n\t{\n\t\tsum += nums[i];\n\t\tmax_sum = max(sum, max_sum);\n\t}\n\treturn max_sum;\n}\n```\n### 优秀的代码\n``` c\npublic int maxSubArray(int[] A) {\n int n = A.length;\n int[] dp = new int[n];//dp[i]表示从A[0]-A[i]中连续的字串最大值\n dp[0] = A[0];\n int max = dp[0];\n \n for(int i = 1; i < n; i++){\n dp[i] = A[i] + (dp[i - 1] > 0 ? dp[i - 1] : 0);\n max = Math.max(max, dp[i]);\n }\n \n return max;\n}\n```\n当前的字串最大值,是当前的值 + 之前的的字串的值 是否大于0,若小于,相当于重新开始计算子串的最大值\n\n## 56. Merge Intervals\n\nGiven a collection of intervals, merge all overlapping intervals.\n\nFor example,\nGiven`[1,3],[2,6],[8,10],[15,18]`,\nreturn`[1,6],[8,10],[15,18]`.\n### 解析\n这道题看上去还是十分简单的,就是遍历如果前一个的end大于后一个start,则赋值并继续循环\n如果不符合,就加到结果的`vector`中,并更新start和end的值\n**注意点**\n\n1. 首先要对其进行排序,按照start升序\n2. 考虑多种情况,也有可能前一个包含后一个\n\n### 代码\n```\nstatic bool cmp(const Interval &a, const Interval &b)\n{\n\treturn a.start < b.start;\n}\n \nvector<Interval> merge(vector<Interval>& intervals) {\n\tint i, j, k;\n\tvector<Interval>res;\n if (intervals.empty())\n\t\treturn res;\n sort(intervals.begin(), intervals.end(), cmp);\n\tint start = intervals[i].start;\n\tint end = intervals[i].end;\n\tfor (i = 1; i < intervals.size(); i++)\n\t{\n if (intervals[i].start >= start && intervals[i].end <= end)\n continue;\n\t\tif (intervals[i].start <= end)\n\t\t{\n\t\t\tend = intervals[i].end;\n\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tInterval tmp = { start, end };\n\t\t\tres.push_back(tmp);\n\t\t\tstart = intervals[i].start;\n\t\t\tend = intervals[i].end;\n\t\t}\n\t}\n Interval tmp = { start, end };\n\tres.push_back(tmp);\n\treturn res;\n \n}\n```\n## 55. Jump Game\nGiven an array of non-negative integers, you are initially positioned at the first index of the array.\n\nEach element in the array represents your maximum jump length at that position.\n\nDetermine if you are able to reach the last index.\n\nFor example:\nA = `[2,3,1,1,4]`, return `true`.\n\nA = `[3,2,1,0,4]`, return `false`.\n\n### 解题思路\n用distance表示i之前最远的距离,如果 `i > diatance`,说明永远到达不了i,就退出循环\n当distance >= n-1时,至少能跳到最后\n\n夏令营时,我采用的是深搜,显然会超时\n\n### 代码\n```\nbool canJump(vector<int>& nums) {\n\tint distance = 0;\n\tint n = nums.size();\n\tfor (int i = 0; i < n && i <= distance; i++)\n\t{\n\t\tdistance = max(nums[i] + i, distance);\n\t}\n\tif (distance < n - 1)\n\t\treturn false;\n\treturn true;\n}\n```\n## 57.Insert Interval\n\nGiven a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary).\n\nYou may assume that the intervals were initially sorted according to their start times.\n\n**Example 1:**\nGiven intervals`[1,3],[6,9]`, insert and merge `[2,5]` in as `[1,5],[6,9]`.\n\n**Example 2:**\nGiven `[1,2],[3,5],[6,7],[8,10],[12,16]`, insert and merge `[4,9]` in as `[1,2],[3,10],[12,16]`.\n\nThis is because the new interval `[4,9]` overlaps with `[3,5],[6,7],[8,10]`.\n### 解题思路\n这个合并可以分为三个部分\n1. `end < newInterval.stat`,直接将其添加至新的`res`中\n2. `start > newInterval.end`,同上\n3. 有部分相交,`start <= newInterval.end`的部分,则`start`取最小值,`end`取最大值,并`push`进入结果中\n### 代码\n\n```c\nvector<Interval> insert(vector<Interval>& intervals, Interval newInterval) {\n\tint i = 0 ;\n\tint s = newInterval.start;\n\tint e = newInterval.end;\n\tvector<Interval>res;\n\tif (intervals.empty())\n\t{\n\t\tintervals.push_back(newInterval);\n\t\treturn intervals;\n\t}\n\t//前部\n\n\twhile (i < intervals.size() && newInterval.start > intervals[i].end)\n\t{\n\t\tres.push_back(intervals[i]);\n\t\ti++;\n\t}\n\t//中间\n\twhile (i < intervals.size() && intervals[i].start <= newInterval.end)\n\t{\n\t\ts = min(s, intervals[i].start);\n\t\te = max(e, intervals[i].end);\n\t\ti++;\n\t}\n\tInterval t{ s, e };\n\tres.push_back(t);\n\n\t//后部\n\twhile (i < intervals.size() && newInterval.end < intervals[i].start)\n\t{\n\t\tres.push_back(intervals[i]);\n\t\ti++;\n\t}\n\treturn res;\n}\n```\n\n## Spiral Matrix II\nGiven an integer n, generate a square matrix filled with elements from 1 to n2 in spiral order.\n\nFor example,\nGiven n = `3`,\n\nYou should return the following matrix:\n```\n[\n [ 1, 2, 3 ],\n [ 8, 9, 4 ],\n [ 7, 6, 5 ]\n]\n```\n\n### 解题思路\n和之前的遍历类似,不再赘述,重要的是如何用verctor定义一个二维数组!!!\n`vector<vector<int>> res(n,vector<int>(n))` !!!\n`vector<vector<int>> res(n,vector<int>(n))` !!!\n### 代码\n```c\nvector<vector<int>> generateMatrix(int n) {\n\tvector<vector<int>> res(n, vector<int>(n));//!!!!!!!!!!!!\n\tint i, j, k, l;\n\tint c = 1;\n\ti = j = 0;\n\tk = l = n - 1;\n\twhile (c <= n*n) \n\t{\n\t\tfor (int m = j; m <= l; m++)\n\t\t{\n\t\t\tres[i][m] = c;\n\t\t\tc++;\n\t\t}\n\t\ti++;\n\t\tfor (int m = i; m <= k; m++)\n\t\t{\n\t\t\tres[m][l] = c;\n\t\t\tc++;\n\t\t}\n\t\tl--;\n\t\tfor (int m = l; m >= j; m--)\n\t\t{\n\t\t\tres[k][m] = c;\n\t\t\tc++;\n\t\t}\n\t\tk--;\n\t\tfor (int m = k; m >= i; m--)\n\t\t{\n\t\t\tres[m][j] = c;\n\t\t\tc++;\n\t\t}\n\t\tj++;\n\t}\n\treturn res;\n}\n```\n## Unique Paths II\nFollow up for \"Unique Paths\":\n\nNow consider if some obstacles are added to the grids. How many unique paths would there be?\n\nAn obstacle and empty space is marked as `1` and `0` respectively in the grid.\n\nFor example,\nThere is one obstacle in the middle of a 3x3 grid as illustrated below.\n```\n[\n [0,0,0],\n [0,1,0],\n [0,0,0]\n]\n```\nThe total number of unique paths is `2`.\n\nNote: m and n will be at most 100.\n\n### 解决思路\n这道题采用DP算法,到达当前点的路径数量等于左边和上面点的路径之和,同时要考虑边界的问题以及碰到障碍物时的处理办法\n###代码\n```\nint uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {\n\tint row = obstacleGrid.size();\n\tint col = obstacleGrid[0].size();\n\tint i, j;\n\tvector<vector<int>> dp(row, vector<int>(col, 0));\n\tfor (i = 0; i < row; i++)\n\t{\n\t\tfor (j = 0; j < col; j++)\n\t\t{\n\t\t\tif (obstacleGrid[i][j] == 1)\n\t\t\t\tdp[i][j] = 0;\n\t\t\telse if (i == 0 && j == 0)\n\t\t\t\tdp[i][j] = 1;\n\t\t\telse if (i == 0 && j > 0)\n\t\t\t\tdp[i][j] = dp[i][j - 1];\n\t\t\telse if (j == 0 && i > 0)\n\t\t\t\tdp[i][j] = dp[i - 1][j];\n\t\t\telse if (i > 0 && j > 0)\n\t\t\t\tdp[i][j] = dp[i - 1][j] + dp[i][j - 1];\n\t\t}\n\t}\n\treturn dp[row - 1][col - 1];\n}\n```\n还有直接将边界扩大一圈,直接考虑边界的问题,代码如下\n```\n int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {\n int m = obstacleGrid.size(), n = obstacleGrid[0].size();\n vector<vector<int> > dp(m + 1, vector<int> (n + 1, 0));\n dp[0][1] = 1;\n for (int i = 1; i <= m; i++)\n for (int j = 1; j <= n; j++)\n if (!obstacleGrid[i - 1][j - 1])\n dp[i][j] = dp[i - 1][j] + dp[i][j - 1];\n return dp[m][n];\n } \n```\n\n## 93. Restore IP Addresses\nGiven a string containing only digits, restore it by returning all possible valid IP address combinations.\n\nFor example:\nGiven `\"25525511135\"`,\n\nreturn `[\"255.255.11.135\", \"255.255.111.35\"]`. (Order does not matter)\n### 解题思路\n采用dfs,每一次长度为1~3,如果这一小块的长度为3,且值>255不符合;如果起始值为0且长度>1则不符合\n最终当count==4并且指针指向结尾的时候符合要求并添加进结果中\n### 代码\n```\nvoid dfs(string ip,vector<string>& res, int idx, int count, string s)\n{\n\tif (count > 4)\n\t\treturn;\n\tif (count == 4 && idx == ip.length())\n\t{\n\t\tres.push_back(s);\n\t\treturn;\n\t}\n\tfor (int i = 1; i < 4; i++)\n\t{\n\t\tif (idx + i > ip.length())\n\t\t\tbreak;\n\t\tstring t = ip.substr(idx , i);\n\t\tif ((t[0] == '0' && t.length() > 1) || (i == 3 && atoi(t.c_str()) > 255))\n\t\t\tcontinue;\n\t\tdfs(ip, res, idx + i, count + 1, s + t + (count == 3 ? \"\" : \".\"));\n\t}\n}\n\n\nvector<string> restoreIpAddresses(string s) {\n\tvector<string>res;\n\tdfs(s, res, 0, 0, \"\");\n\treturn res;\n}\n```\n## 79. Word Search\nGiven a 2D board and a word, find if the word exists in the grid.\n\nThe word can be constructed from letters of sequentially adjacent cell, where \"adjacent\" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.\n\nFor example,\nGiven **board** =\n```\n[\n ['A','B','C','E'],\n ['S','F','C','S'],\n ['A','D','E','E']\n]\n```\nword = `\"ABCCED\"`, -> returns `true`,\nword = `\"SEE\"`, -> returns `true`,\nword = `\"ABCB\"`, -> returns `false`.\n### 解题思路\n这一次真的可以采用深搜的方法,从一个匹配的字母处开始寻找,规定查找的四个方向,如果当前字母与字符串中的字母相同,搜索下一个字母。\n同时,不能重复字母,因此可以创建一个与`board`相同大小的二维向量用于表示是否访问过\n记得回溯!\n## 代码\n```\nvoid find(int i, int j, int index, string word, vector<vector<char>>& board, int& res, vector<vector<int>>& visited)\n{\n int idx[] = { 1, -1, 0, 0 };\n int idy[] = { 0, 0, -1, 1 };\n\tif (res == 0)\n\t{\n\t\tif (i < 0 || i >= board.size() || j < 0 || j >= board[i].size()||visited[i][j])\n\t\t\treturn;\n\t\tif (board[i][j] != word[index])\n\t\t\treturn;\n\t\tif (index == word.length() - 1)\n\t\t{\n\t\t\tres = 1;\n\t\t\treturn;\n\t\t}\n\t\tfor (int k = 0; k < 4; k++)\n\t\t{\n\t\t\tint tx = i + idx[k];\n\t\t\tint ty = j + idy[k];\n\t\t\tvisited[i][j] = 1;\n\t\t\tfind(tx, ty, index + 1, word, board, res,visited);\n\t\t\tvisited[i][j] = 0;\n\n\t\t}\n\t}\n}\n\nbool exist(vector<vector<char>>& board, string word) {\n\tvector<vector<int>> visited(board.size(), vector<int>(board[0].size(), 0));\n\tint res = 0;\n\tint i, j, k;\n\tfor (i = 0; i < board.size(); i++)\n\t{\n\t\tfor (j = 0; j < board[i].size(); j++)\n\t\t{\n\t\t\tif (board[i][j] == word[0])\n\t\t\t{\n\t\t\t\tfind(i, j, 0, word, board, res,visited);\n\t\t\t\tif (res)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (res)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn res;\n}\n```\n## 后记\n这一周也终于结束了,感觉好像有点堕落有点拖拉,效率很低,希望加把劲咯\n总是需要一门很熟悉的语言!go 和 C++?\n\n\n\n\n\n\n","slug":"LeetCode-Week-1","published":1,"updated":"2018-04-30T13:49:56.132Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cjyohfvdz000nsy0yo34ehy88","content":"<blockquote>\n<p>写在前面</p>\n</blockquote>\n<p>实在是无法忍受自己写代码如此差劲,心里所想的东西完无法用代码表达出来,因此打算从头开始一点一点的刷 LeetCode<br>我的计划是每天<strong>至少</strong>写两道题并进行总结,一周发一次Blog,虽然这个可能花费大量时间,但胜在能够打牢基础,我认为这还是值得的<br>每道题都会由以下几个部分组成:</p>\n<ul>\n<li>题目</li>\n<li>代码</li>\n<li>如果没有代码,就写没有考虑到的地方</li>\n<li>优秀的代码和解析</li>\n</ul>\n<p>希望能坚持啊!兄弟!</p>\n<h2 id=\"Spiral-Matrix\"><a href=\"#Spiral-Matrix\" class=\"headerlink\" title=\"Spiral Matrix\"></a>Spiral Matrix</h2><p>Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order.</p>\n<p>For example,<br>Given the following matrix:<br><figure class=\"highlight c++\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div></pre></td><td class=\"code\"><pre><div class=\"line\">[</div><div class=\"line\"> [ <span class=\"number\">1</span>, <span class=\"number\">2</span>, <span class=\"number\">3</span> ],</div><div class=\"line\"> [ <span class=\"number\">4</span>, <span class=\"number\">5</span>, <span class=\"number\">6</span> ],</div><div class=\"line\"> [ <span class=\"number\">7</span>, <span class=\"number\">8</span>, <span class=\"number\">9</span> ]</div><div class=\"line\">]</div></pre></td></tr></table></figure></p>\n<p>You should return <code>[1,2,3,6,9,8,7,4,5]</code></p>\n<h3 id=\"解析\"><a href=\"#解析\" class=\"headerlink\" title=\"解析\"></a>解析</h3><p>这道题就是以螺旋的方式遍历整个二维数组,我的思路是设置1个变量k用于计数,还有4个变量用于指向四个遍历方向,还有一个二维数组记录每一个位置上的数字是否被访问过,若是,跳过,有点傻有点啰嗦</p>\n<h3 id=\"代码\"><a href=\"#代码\" class=\"headerlink\" title=\"代码\"></a>代码</h3><figure class=\"highlight c\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div><div class=\"line\">29</div><div class=\"line\">30</div><div class=\"line\">31</div><div class=\"line\">32</div><div class=\"line\">33</div><div class=\"line\">34</div><div class=\"line\">35</div><div class=\"line\">36</div><div class=\"line\">37</div><div class=\"line\">38</div><div class=\"line\">39</div><div class=\"line\">40</div><div class=\"line\">41</div><div class=\"line\">42</div><div class=\"line\">43</div><div class=\"line\">44</div><div class=\"line\">45</div><div class=\"line\">46</div><div class=\"line\">47</div><div class=\"line\">48</div><div class=\"line\">49</div><div class=\"line\">50</div><div class=\"line\">51</div><div class=\"line\">52</div><div class=\"line\">53</div><div class=\"line\">54</div><div class=\"line\">55</div><div class=\"line\">56</div><div class=\"line\">57</div><div class=\"line\">58</div></pre></td><td class=\"code\"><pre><div class=\"line\"> <span class=\"built_in\">vector</span><<span class=\"keyword\">int</span>> spiralOrder(<span class=\"built_in\">vector</span><<span class=\"built_in\">vector</span><<span class=\"keyword\">int</span>>>& matrix) {</div><div class=\"line\"> <span class=\"built_in\">vector</span><<span class=\"keyword\">int</span>>res;</div><div class=\"line\"> <span class=\"keyword\">if</span>(matrix.empty())</div><div class=\"line\"> <span class=\"keyword\">return</span> res;</div><div class=\"line\">\t<span class=\"keyword\">int</span> m = matrix.size();</div><div class=\"line\">\t<span class=\"keyword\">int</span> n = matrix[<span class=\"number\">0</span>].size();</div><div class=\"line\">\t<span class=\"keyword\">int</span> a[<span class=\"number\">50</span>][<span class=\"number\">50</span>];</div><div class=\"line\">\t<span class=\"built_in\">memset</span>(a, <span class=\"number\">0</span>, <span class=\"keyword\">sizeof</span>(a));</div><div class=\"line\">\t<span class=\"keyword\">int</span> k = <span class=\"number\">0</span>, i, j;</div><div class=\"line\">\t<span class=\"keyword\">int</span> q, w, e, r;</div><div class=\"line\">\tq = r = <span class=\"number\">0</span>;</div><div class=\"line\">\tw = n<span class=\"number\">-1</span>;</div><div class=\"line\">\te = m<span class=\"number\">-1</span>;</div><div class=\"line\">\t<span class=\"keyword\">while</span> (k < m*n)</div><div class=\"line\">\t{</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> (i = <span class=\"number\">0</span>; i < n; i++)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\t<span class=\"keyword\">if</span> (a[q][i] == <span class=\"number\">0</span>)</div><div class=\"line\">\t\t\t{</div><div class=\"line\">\t\t\t\tres.push_back(matrix[q][i]);</div><div class=\"line\">\t\t\t\ta[q][i] = <span class=\"number\">1</span>;</div><div class=\"line\">\t\t\t\tk++;</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tq++;</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> (i = <span class=\"number\">0</span>; i < m; i++)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\t<span class=\"keyword\">if</span> (a[i][w] == <span class=\"number\">0</span>)</div><div class=\"line\">\t\t\t{</div><div class=\"line\">\t\t\t\tres.push_back(matrix[i][w]);</div><div class=\"line\">\t\t\t\ta[i][w] = <span class=\"number\">1</span>;</div><div class=\"line\">\t\t\t\tk++;</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tw--;</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> (i = n - <span class=\"number\">1</span>; i >= <span class=\"number\">0</span>; i--)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\t<span class=\"keyword\">if</span> (a[e][i] == <span class=\"number\">0</span>)</div><div class=\"line\">\t\t\t{</div><div class=\"line\">\t\t\t\tres.push_back(matrix[e][i]);</div><div class=\"line\">\t\t\t\ta[e][i] = <span class=\"number\">1</span>;</div><div class=\"line\">\t\t\t\tk++;</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\te--;</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> (i = m - <span class=\"number\">1</span>; i >= <span class=\"number\">0</span>; i--)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\t<span class=\"keyword\">if</span> (a[i][r] == <span class=\"number\">0</span>)</div><div class=\"line\">\t\t\t{</div><div class=\"line\">\t\t\t\tres.push_back(matrix[i][r]);</div><div class=\"line\">\t\t\t\ta[i][r] = <span class=\"number\">1</span>;</div><div class=\"line\">\t\t\t\tk++;</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tr++;</div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"keyword\">return</span> res;</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h3 id=\"简便的代码\"><a href=\"#简便的代码\" class=\"headerlink\" title=\"简便的代码\"></a>简便的代码</h3><figure class=\"highlight c\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div><div class=\"line\">29</div><div class=\"line\">30</div><div class=\"line\">31</div><div class=\"line\">32</div><div class=\"line\">33</div><div class=\"line\">34</div><div class=\"line\">35</div><div class=\"line\">36</div><div class=\"line\">37</div><div class=\"line\">38</div><div class=\"line\">39</div><div class=\"line\">40</div><div class=\"line\">41</div><div class=\"line\">42</div><div class=\"line\">43</div><div class=\"line\">44</div><div class=\"line\">45</div><div class=\"line\">46</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">public</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Solution</span> {</span></div><div class=\"line\"> <span class=\"keyword\">public</span> List<Integer> spiralOrder(<span class=\"keyword\">int</span>[][] matrix) {</div><div class=\"line\"> </div><div class=\"line\"> List<Integer> res = <span class=\"keyword\">new</span> ArrayList<Integer>();</div><div class=\"line\"> </div><div class=\"line\"> <span class=\"keyword\">if</span> (matrix.length == <span class=\"number\">0</span>) {</div><div class=\"line\"> <span class=\"keyword\">return</span> res;</div><div class=\"line\"> }</div><div class=\"line\"> </div><div class=\"line\"> <span class=\"keyword\">int</span> rowBegin = <span class=\"number\">0</span>;</div><div class=\"line\"> <span class=\"keyword\">int</span> rowEnd = matrix.length<span class=\"number\">-1</span>;</div><div class=\"line\"> <span class=\"keyword\">int</span> colBegin = <span class=\"number\">0</span>;</div><div class=\"line\"> <span class=\"keyword\">int</span> colEnd = matrix[<span class=\"number\">0</span>].length - <span class=\"number\">1</span>;</div><div class=\"line\"> </div><div class=\"line\"> <span class=\"keyword\">while</span> (rowBegin <= rowEnd && colBegin <= colEnd) {</div><div class=\"line\"> <span class=\"comment\">// Traverse Right</span></div><div class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> j = colBegin; j <= colEnd; j ++) {</div><div class=\"line\"> res.add(matrix[rowBegin][j]);</div><div class=\"line\"> }</div><div class=\"line\"> rowBegin++;</div><div class=\"line\"> </div><div class=\"line\"> <span class=\"comment\">// Traverse Down</span></div><div class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> j = rowBegin; j <= rowEnd; j ++) {</div><div class=\"line\"> res.add(matrix[j][colEnd]);</div><div class=\"line\"> }</div><div class=\"line\"> colEnd--;</div><div class=\"line\"> </div><div class=\"line\"> <span class=\"keyword\">if</span> (rowBegin <= rowEnd) {</div><div class=\"line\"> <span class=\"comment\">// Traverse Left</span></div><div class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> j = colEnd; j >= colBegin; j --) {</div><div class=\"line\"> res.add(matrix[rowEnd][j]);</div><div class=\"line\"> }</div><div class=\"line\"> }</div><div class=\"line\"> rowEnd--;</div><div class=\"line\"> </div><div class=\"line\"> <span class=\"keyword\">if</span> (colBegin <= colEnd) {</div><div class=\"line\"> <span class=\"comment\">// Traver Up</span></div><div class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> j = rowEnd; j >= rowBegin; j --) {</div><div class=\"line\"> res.add(matrix[j][colBegin]);</div><div class=\"line\"> }</div><div class=\"line\"> }</div><div class=\"line\"> colBegin ++;</div><div class=\"line\"> }</div><div class=\"line\"> </div><div class=\"line\"> <span class=\"keyword\">return</span> res;</div><div class=\"line\"> }</div></pre></td></tr></table></figure>\n<p>同样也是while中套着循环,同时也是利用了四个变量分别指向行、列的开始、结尾的位置,可以通过这四个变量设置循环的次数,而不用另外开辟一个数组来记录该数字是否被访问过。</p>\n<h2 id=\"Maximum-Subarray\"><a href=\"#Maximum-Subarray\" class=\"headerlink\" title=\"Maximum Subarray\"></a>Maximum Subarray</h2><p>Find the contiguous subarray within an array (containing at least one number) which has the largest sum.</p>\n<p>For example, given the array<code>[-2,1,-3,4,-1,2,1,-5,4]</code>,<br>the contiguous subarray<code>[4,-1,2,1]</code>has the largest sum =<code>6</code>.</p>\n<h3 id=\"愚蠢的代码\"><a href=\"#愚蠢的代码\" class=\"headerlink\" title=\"愚蠢的代码\"></a>愚蠢的代码</h3><p>这代码一看就是会超时的,想过使用DP算法,但是不知道怎么处理<strong>连续</strong><br><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div></pre></td><td class=\"code\"><pre><div class=\"line\">int maxSubArray(vector<int>& nums) {</div><div class=\"line\">\tint max_sum = INT_MIN;</div><div class=\"line\">\tint i;</div><div class=\"line\">\tfor (i = 0; i < nums.size(); i++)</div><div class=\"line\">\t{</div><div class=\"line\">\t\tint temp = func(i, nums);</div><div class=\"line\">\t\tmax_sum = max(max_sum, temp);</div><div class=\"line\">\t}</div><div class=\"line\">\treturn max_sum;</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\">int func(int begin, vector<int> &nums)</div><div class=\"line\">{</div><div class=\"line\">\tint sum = 0;</div><div class=\"line\">\tint max_sum = nums[begin];</div><div class=\"line\">\tint i;</div><div class=\"line\">\tfor (i = begin ; i < nums.size(); i++)</div><div class=\"line\">\t{</div><div class=\"line\">\t\tsum += nums[i];</div><div class=\"line\">\t\tmax_sum = max(sum, max_sum);</div><div class=\"line\">\t}</div><div class=\"line\">\treturn max_sum;</div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<h3 id=\"优秀的代码\"><a href=\"#优秀的代码\" class=\"headerlink\" title=\"优秀的代码\"></a>优秀的代码</h3><figure class=\"highlight c\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">int</span> <span class=\"title\">maxSubArray</span><span class=\"params\">(<span class=\"keyword\">int</span>[] A)</span> </span>{</div><div class=\"line\"> <span class=\"keyword\">int</span> n = A.length;</div><div class=\"line\"> <span class=\"keyword\">int</span>[] dp = <span class=\"keyword\">new</span> <span class=\"keyword\">int</span>[n];<span class=\"comment\">//dp[i]表示从A[0]-A[i]中连续的字串最大值</span></div><div class=\"line\"> dp[<span class=\"number\">0</span>] = A[<span class=\"number\">0</span>];</div><div class=\"line\"> <span class=\"keyword\">int</span> max = dp[<span class=\"number\">0</span>];</div><div class=\"line\"> </div><div class=\"line\"> <span class=\"keyword\">for</span>(<span class=\"keyword\">int</span> i = <span class=\"number\">1</span>; i < n; i++){</div><div class=\"line\"> dp[i] = A[i] + (dp[i - <span class=\"number\">1</span>] > <span class=\"number\">0</span> ? dp[i - <span class=\"number\">1</span>] : <span class=\"number\">0</span>);</div><div class=\"line\"> max = Math.max(max, dp[i]);</div><div class=\"line\"> }</div><div class=\"line\"> </div><div class=\"line\"> <span class=\"keyword\">return</span> max;</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<p>当前的字串最大值,是当前的值 + 之前的的字串的值 是否大于0,若小于,相当于重新开始计算子串的最大值</p>\n<h2 id=\"56-Merge-Intervals\"><a href=\"#56-Merge-Intervals\" class=\"headerlink\" title=\"56. Merge Intervals\"></a>56. Merge Intervals</h2><p>Given a collection of intervals, merge all overlapping intervals.</p>\n<p>For example,<br>Given<code>[1,3],[2,6],[8,10],[15,18]</code>,<br>return<code>[1,6],[8,10],[15,18]</code>.</p>\n<h3 id=\"解析-1\"><a href=\"#解析-1\" class=\"headerlink\" title=\"解析\"></a>解析</h3><p>这道题看上去还是十分简单的,就是遍历如果前一个的end大于后一个start,则赋值并继续循环<br>如果不符合,就加到结果的<code>vector</code>中,并更新start和end的值<br><strong>注意点</strong></p>\n<ol>\n<li>首先要对其进行排序,按照start升序</li>\n<li>考虑多种情况,也有可能前一个包含后一个</li>\n</ol>\n<h3 id=\"代码-1\"><a href=\"#代码-1\" class=\"headerlink\" title=\"代码\"></a>代码</h3><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div><div class=\"line\">29</div><div class=\"line\">30</div><div class=\"line\">31</div><div class=\"line\">32</div><div class=\"line\">33</div><div class=\"line\">34</div><div class=\"line\">35</div></pre></td><td class=\"code\"><pre><div class=\"line\">static bool cmp(const Interval &a, const Interval &b)</div><div class=\"line\">{</div><div class=\"line\">\treturn a.start < b.start;</div><div class=\"line\">}</div><div class=\"line\"> </div><div class=\"line\">vector<Interval> merge(vector<Interval>& intervals) {</div><div class=\"line\">\tint i, j, k;</div><div class=\"line\">\tvector<Interval>res;</div><div class=\"line\"> if (intervals.empty())</div><div class=\"line\">\t\treturn res;</div><div class=\"line\"> sort(intervals.begin(), intervals.end(), cmp);</div><div class=\"line\">\tint start = intervals[i].start;</div><div class=\"line\">\tint end = intervals[i].end;</div><div class=\"line\">\tfor (i = 1; i < intervals.size(); i++)</div><div class=\"line\">\t{</div><div class=\"line\"> if (intervals[i].start >= start && intervals[i].end <= end)</div><div class=\"line\"> continue;</div><div class=\"line\">\t\tif (intervals[i].start <= end)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tend = intervals[i].end;</div><div class=\"line\">\t\t\tcontinue;</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\telse</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tInterval tmp = { start, end };</div><div class=\"line\">\t\t\tres.push_back(tmp);</div><div class=\"line\">\t\t\tstart = intervals[i].start;</div><div class=\"line\">\t\t\tend = intervals[i].end;</div><div class=\"line\">\t\t}</div><div class=\"line\">\t}</div><div class=\"line\"> Interval tmp = { start, end };</div><div class=\"line\">\tres.push_back(tmp);</div><div class=\"line\">\treturn res;</div><div class=\"line\"> </div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h2 id=\"55-Jump-Game\"><a href=\"#55-Jump-Game\" class=\"headerlink\" title=\"55. Jump Game\"></a>55. Jump Game</h2><p>Given an array of non-negative integers, you are initially positioned at the first index of the array.</p>\n<p>Each element in the array represents your maximum jump length at that position.</p>\n<p>Determine if you are able to reach the last index.</p>\n<p>For example:<br>A = <code>[2,3,1,1,4]</code>, return <code>true</code>.</p>\n<p>A = <code>[3,2,1,0,4]</code>, return <code>false</code>.</p>\n<h3 id=\"解题思路\"><a href=\"#解题思路\" class=\"headerlink\" title=\"解题思路\"></a>解题思路</h3><p>用distance表示i之前最远的距离,如果 <code>i > diatance</code>,说明永远到达不了i,就退出循环<br>当distance >= n-1时,至少能跳到最后</p>\n<p>夏令营时,我采用的是深搜,显然会超时</p>\n<h3 id=\"代码-2\"><a href=\"#代码-2\" class=\"headerlink\" title=\"代码\"></a>代码</h3><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div></pre></td><td class=\"code\"><pre><div class=\"line\">bool canJump(vector<int>& nums) {</div><div class=\"line\">\tint distance = 0;</div><div class=\"line\">\tint n = nums.size();</div><div class=\"line\">\tfor (int i = 0; i < n && i <= distance; i++)</div><div class=\"line\">\t{</div><div class=\"line\">\t\tdistance = max(nums[i] + i, distance);</div><div class=\"line\">\t}</div><div class=\"line\">\tif (distance < n - 1)</div><div class=\"line\">\t\treturn false;</div><div class=\"line\">\treturn true;</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h2 id=\"57-Insert-Interval\"><a href=\"#57-Insert-Interval\" class=\"headerlink\" title=\"57.Insert Interval\"></a>57.Insert Interval</h2><p>Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary).</p>\n<p>You may assume that the intervals were initially sorted according to their start times.</p>\n<p><strong>Example 1:</strong><br>Given intervals<code>[1,3],[6,9]</code>, insert and merge <code>[2,5]</code> in as <code>[1,5],[6,9]</code>.</p>\n<p><strong>Example 2:</strong><br>Given <code>[1,2],[3,5],[6,7],[8,10],[12,16]</code>, insert and merge <code>[4,9]</code> in as <code>[1,2],[3,10],[12,16]</code>.</p>\n<p>This is because the new interval <code>[4,9]</code> overlaps with <code>[3,5],[6,7],[8,10]</code>.</p>\n<h3 id=\"解题思路-1\"><a href=\"#解题思路-1\" class=\"headerlink\" title=\"解题思路\"></a>解题思路</h3><p>这个合并可以分为三个部分</p>\n<ol>\n<li><code>end < newInterval.stat</code>,直接将其添加至新的<code>res</code>中</li>\n<li><code>start > newInterval.end</code>,同上</li>\n<li>有部分相交,<code>start <= newInterval.end</code>的部分,则<code>start</code>取最小值,<code>end</code>取最大值,并<code>push</code>进入结果中<h3 id=\"代码-3\"><a href=\"#代码-3\" class=\"headerlink\" title=\"代码\"></a>代码</h3></li>\n</ol>\n<figure class=\"highlight c\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div><div class=\"line\">29</div><div class=\"line\">30</div><div class=\"line\">31</div><div class=\"line\">32</div><div class=\"line\">33</div><div class=\"line\">34</div><div class=\"line\">35</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"built_in\">vector</span><Interval> insert(<span class=\"built_in\">vector</span><Interval>& intervals, Interval newInterval) {</div><div class=\"line\">\t<span class=\"keyword\">int</span> i = <span class=\"number\">0</span> ;</div><div class=\"line\">\t<span class=\"keyword\">int</span> s = newInterval.start;</div><div class=\"line\">\t<span class=\"keyword\">int</span> e = newInterval.end;</div><div class=\"line\">\t<span class=\"built_in\">vector</span><Interval>res;</div><div class=\"line\">\t<span class=\"keyword\">if</span> (intervals.empty())</div><div class=\"line\">\t{</div><div class=\"line\">\t\tintervals.push_back(newInterval);</div><div class=\"line\">\t\t<span class=\"keyword\">return</span> intervals;</div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"comment\">//前部</span></div><div class=\"line\"></div><div class=\"line\">\t<span class=\"keyword\">while</span> (i < intervals.size() && newInterval.start > intervals[i].end)</div><div class=\"line\">\t{</div><div class=\"line\">\t\tres.push_back(intervals[i]);</div><div class=\"line\">\t\ti++;</div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"comment\">//中间</span></div><div class=\"line\">\t<span class=\"keyword\">while</span> (i < intervals.size() && intervals[i].start <= newInterval.end)</div><div class=\"line\">\t{</div><div class=\"line\">\t\ts = min(s, intervals[i].start);</div><div class=\"line\">\t\te = max(e, intervals[i].end);</div><div class=\"line\">\t\ti++;</div><div class=\"line\">\t}</div><div class=\"line\">\tInterval t{ s, e };</div><div class=\"line\">\tres.push_back(t);</div><div class=\"line\"></div><div class=\"line\">\t<span class=\"comment\">//后部</span></div><div class=\"line\">\t<span class=\"keyword\">while</span> (i < intervals.size() && newInterval.end < intervals[i].start)</div><div class=\"line\">\t{</div><div class=\"line\">\t\tres.push_back(intervals[i]);</div><div class=\"line\">\t\ti++;</div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"keyword\">return</span> res;</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h2 id=\"Spiral-Matrix-II\"><a href=\"#Spiral-Matrix-II\" class=\"headerlink\" title=\"Spiral Matrix II\"></a>Spiral Matrix II</h2><p>Given an integer n, generate a square matrix filled with elements from 1 to n2 in spiral order.</p>\n<p>For example,<br>Given n = <code>3</code>,</p>\n<p>You should return the following matrix:<br><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div></pre></td><td class=\"code\"><pre><div class=\"line\">[</div><div class=\"line\"> [ 1, 2, 3 ],</div><div class=\"line\"> [ 8, 9, 4 ],</div><div class=\"line\"> [ 7, 6, 5 ]</div><div class=\"line\">]</div></pre></td></tr></table></figure></p>\n<h3 id=\"解题思路-2\"><a href=\"#解题思路-2\" class=\"headerlink\" title=\"解题思路\"></a>解题思路</h3><p>和之前的遍历类似,不再赘述,重要的是如何用verctor定义一个二维数组!!!<br><code>vector<vector<int>> res(n,vector<int>(n))</code> !!!<br><code>vector<vector<int>> res(n,vector<int>(n))</code> !!!</p>\n<h3 id=\"代码-4\"><a href=\"#代码-4\" class=\"headerlink\" title=\"代码\"></a>代码</h3><figure class=\"highlight c\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div><div class=\"line\">29</div><div class=\"line\">30</div><div class=\"line\">31</div><div class=\"line\">32</div><div class=\"line\">33</div><div class=\"line\">34</div><div class=\"line\">35</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"built_in\">vector</span><<span class=\"built_in\">vector</span><<span class=\"keyword\">int</span>>> generateMatrix(<span class=\"keyword\">int</span> n) {</div><div class=\"line\">\t<span class=\"built_in\">vector</span><<span class=\"built_in\">vector</span><<span class=\"keyword\">int</span>>> res(n, <span class=\"built_in\">vector</span><<span class=\"keyword\">int</span>>(n));<span class=\"comment\">//!!!!!!!!!!!!</span></div><div class=\"line\">\t<span class=\"keyword\">int</span> i, j, k, l;</div><div class=\"line\">\t<span class=\"keyword\">int</span> c = <span class=\"number\">1</span>;</div><div class=\"line\">\ti = j = <span class=\"number\">0</span>;</div><div class=\"line\">\tk = l = n - <span class=\"number\">1</span>;</div><div class=\"line\">\t<span class=\"keyword\">while</span> (c <= n*n) </div><div class=\"line\">\t{</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> m = j; m <= l; m++)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tres[i][m] = c;</div><div class=\"line\">\t\t\tc++;</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\ti++;</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> m = i; m <= k; m++)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tres[m][l] = c;</div><div class=\"line\">\t\t\tc++;</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tl--;</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> m = l; m >= j; m--)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tres[k][m] = c;</div><div class=\"line\">\t\t\tc++;</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tk--;</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> m = k; m >= i; m--)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tres[m][j] = c;</div><div class=\"line\">\t\t\tc++;</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tj++;</div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"keyword\">return</span> res;</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h2 id=\"Unique-Paths-II\"><a href=\"#Unique-Paths-II\" class=\"headerlink\" title=\"Unique Paths II\"></a>Unique Paths II</h2><p>Follow up for “Unique Paths”:</p>\n<p>Now consider if some obstacles are added to the grids. How many unique paths would there be?</p>\n<p>An obstacle and empty space is marked as <code>1</code> and <code>0</code> respectively in the grid.</p>\n<p>For example,<br>There is one obstacle in the middle of a 3x3 grid as illustrated below.<br><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div></pre></td><td class=\"code\"><pre><div class=\"line\">[</div><div class=\"line\"> [0,0,0],</div><div class=\"line\"> [0,1,0],</div><div class=\"line\"> [0,0,0]</div><div class=\"line\">]</div></pre></td></tr></table></figure></p>\n<p>The total number of unique paths is <code>2</code>.</p>\n<p>Note: m and n will be at most 100.</p>\n<h3 id=\"解决思路\"><a href=\"#解决思路\" class=\"headerlink\" title=\"解决思路\"></a>解决思路</h3><p>这道题采用DP算法,到达当前点的路径数量等于左边和上面点的路径之和,同时要考虑边界的问题以及碰到障碍物时的处理办法</p>\n<p>###代码<br><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div></pre></td><td class=\"code\"><pre><div class=\"line\">int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {</div><div class=\"line\">\tint row = obstacleGrid.size();</div><div class=\"line\">\tint col = obstacleGrid[0].size();</div><div class=\"line\">\tint i, j;</div><div class=\"line\">\tvector<vector<int>> dp(row, vector<int>(col, 0));</div><div class=\"line\">\tfor (i = 0; i < row; i++)</div><div class=\"line\">\t{</div><div class=\"line\">\t\tfor (j = 0; j < col; j++)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tif (obstacleGrid[i][j] == 1)</div><div class=\"line\">\t\t\t\tdp[i][j] = 0;</div><div class=\"line\">\t\t\telse if (i == 0 && j == 0)</div><div class=\"line\">\t\t\t\tdp[i][j] = 1;</div><div class=\"line\">\t\t\telse if (i == 0 && j > 0)</div><div class=\"line\">\t\t\t\tdp[i][j] = dp[i][j - 1];</div><div class=\"line\">\t\t\telse if (j == 0 && i > 0)</div><div class=\"line\">\t\t\t\tdp[i][j] = dp[i - 1][j];</div><div class=\"line\">\t\t\telse if (i > 0 && j > 0)</div><div class=\"line\">\t\t\t\tdp[i][j] = dp[i - 1][j] + dp[i][j - 1];</div><div class=\"line\">\t\t}</div><div class=\"line\">\t}</div><div class=\"line\">\treturn dp[row - 1][col - 1];</div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<p>还有直接将边界扩大一圈,直接考虑边界的问题,代码如下<br><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div></pre></td><td class=\"code\"><pre><div class=\"line\">int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {</div><div class=\"line\"> int m = obstacleGrid.size(), n = obstacleGrid[0].size();</div><div class=\"line\"> vector<vector<int> > dp(m + 1, vector<int> (n + 1, 0));</div><div class=\"line\"> dp[0][1] = 1;</div><div class=\"line\"> for (int i = 1; i <= m; i++)</div><div class=\"line\"> for (int j = 1; j <= n; j++)</div><div class=\"line\"> if (!obstacleGrid[i - 1][j - 1])</div><div class=\"line\"> dp[i][j] = dp[i - 1][j] + dp[i][j - 1];</div><div class=\"line\"> return dp[m][n];</div><div class=\"line\"> }</div></pre></td></tr></table></figure></p>\n<h2 id=\"93-Restore-IP-Addresses\"><a href=\"#93-Restore-IP-Addresses\" class=\"headerlink\" title=\"93. Restore IP Addresses\"></a>93. Restore IP Addresses</h2><p>Given a string containing only digits, restore it by returning all possible valid IP address combinations.</p>\n<p>For example:<br>Given <code>"25525511135"</code>,</p>\n<p>return <code>["255.255.11.135", "255.255.111.35"]</code>. (Order does not matter)</p>\n<h3 id=\"解题思路-3\"><a href=\"#解题思路-3\" class=\"headerlink\" title=\"解题思路\"></a>解题思路</h3><p>采用dfs,每一次长度为1~3,如果这一小块的长度为3,且值>255不符合;如果起始值为0且长度>1则不符合<br>最终当count==4并且指针指向结尾的时候符合要求并添加进结果中</p>\n<h3 id=\"代码-5\"><a href=\"#代码-5\" class=\"headerlink\" title=\"代码\"></a>代码</h3><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div></pre></td><td class=\"code\"><pre><div class=\"line\">void dfs(string ip,vector<string>& res, int idx, int count, string s)</div><div class=\"line\">{</div><div class=\"line\">\tif (count > 4)</div><div class=\"line\">\t\treturn;</div><div class=\"line\">\tif (count == 4 && idx == ip.length())</div><div class=\"line\">\t{</div><div class=\"line\">\t\tres.push_back(s);</div><div class=\"line\">\t\treturn;</div><div class=\"line\">\t}</div><div class=\"line\">\tfor (int i = 1; i < 4; i++)</div><div class=\"line\">\t{</div><div class=\"line\">\t\tif (idx + i > ip.length())</div><div class=\"line\">\t\t\tbreak;</div><div class=\"line\">\t\tstring t = ip.substr(idx , i);</div><div class=\"line\">\t\tif ((t[0] == '0' && t.length() > 1) || (i == 3 && atoi(t.c_str()) > 255))</div><div class=\"line\">\t\t\tcontinue;</div><div class=\"line\">\t\tdfs(ip, res, idx + i, count + 1, s + t + (count == 3 ? "" : "."));</div><div class=\"line\">\t}</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"></div><div class=\"line\">vector<string> restoreIpAddresses(string s) {</div><div class=\"line\">\tvector<string>res;</div><div class=\"line\">\tdfs(s, res, 0, 0, "");</div><div class=\"line\">\treturn res;</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h2 id=\"79-Word-Search\"><a href=\"#79-Word-Search\" class=\"headerlink\" title=\"79. Word Search\"></a>79. Word Search</h2><p>Given a 2D board and a word, find if the word exists in the grid.</p>\n<p>The word can be constructed from letters of sequentially adjacent cell, where “adjacent” cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.</p>\n<p>For example,<br>Given <strong>board</strong> =<br><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div></pre></td><td class=\"code\"><pre><div class=\"line\">[</div><div class=\"line\"> ['A','B','C','E'],</div><div class=\"line\"> ['S','F','C','S'],</div><div class=\"line\"> ['A','D','E','E']</div><div class=\"line\">]</div></pre></td></tr></table></figure></p>\n<p>word = <code>"ABCCED"</code>, -> returns <code>true</code>,<br>word = <code>"SEE"</code>, -> returns <code>true</code>,<br>word = <code>"ABCB"</code>, -> returns <code>false</code>.</p>\n<h3 id=\"解题思路-4\"><a href=\"#解题思路-4\" class=\"headerlink\" title=\"解题思路\"></a>解题思路</h3><p>这一次真的可以采用深搜的方法,从一个匹配的字母处开始寻找,规定查找的四个方向,如果当前字母与字符串中的字母相同,搜索下一个字母。<br>同时,不能重复字母,因此可以创建一个与<code>board</code>相同大小的二维向量用于表示是否访问过<br>记得回溯!</p>\n<h2 id=\"代码-6\"><a href=\"#代码-6\" class=\"headerlink\" title=\"代码\"></a>代码</h2><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div><div class=\"line\">29</div><div class=\"line\">30</div><div class=\"line\">31</div><div class=\"line\">32</div><div class=\"line\">33</div><div class=\"line\">34</div><div class=\"line\">35</div><div class=\"line\">36</div><div class=\"line\">37</div><div class=\"line\">38</div><div class=\"line\">39</div><div class=\"line\">40</div><div class=\"line\">41</div><div class=\"line\">42</div><div class=\"line\">43</div><div class=\"line\">44</div><div class=\"line\">45</div><div class=\"line\">46</div><div class=\"line\">47</div></pre></td><td class=\"code\"><pre><div class=\"line\">void find(int i, int j, int index, string word, vector<vector<char>>& board, int& res, vector<vector<int>>& visited)</div><div class=\"line\">{</div><div class=\"line\"> int idx[] = { 1, -1, 0, 0 };</div><div class=\"line\"> int idy[] = { 0, 0, -1, 1 };</div><div class=\"line\">\tif (res == 0)</div><div class=\"line\">\t{</div><div class=\"line\">\t\tif (i < 0 || i >= board.size() || j < 0 || j >= board[i].size()||visited[i][j])</div><div class=\"line\">\t\t\treturn;</div><div class=\"line\">\t\tif (board[i][j] != word[index])</div><div class=\"line\">\t\t\treturn;</div><div class=\"line\">\t\tif (index == word.length() - 1)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tres = 1;</div><div class=\"line\">\t\t\treturn;</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tfor (int k = 0; k < 4; k++)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tint tx = i + idx[k];</div><div class=\"line\">\t\t\tint ty = j + idy[k];</div><div class=\"line\">\t\t\tvisited[i][j] = 1;</div><div class=\"line\">\t\t\tfind(tx, ty, index + 1, word, board, res,visited);</div><div class=\"line\">\t\t\tvisited[i][j] = 0;</div><div class=\"line\"></div><div class=\"line\">\t\t}</div><div class=\"line\">\t}</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\">bool exist(vector<vector<char>>& board, string word) {</div><div class=\"line\">\tvector<vector<int>> visited(board.size(), vector<int>(board[0].size(), 0));</div><div class=\"line\">\tint res = 0;</div><div class=\"line\">\tint i, j, k;</div><div class=\"line\">\tfor (i = 0; i < board.size(); i++)</div><div class=\"line\">\t{</div><div class=\"line\">\t\tfor (j = 0; j < board[i].size(); j++)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tif (board[i][j] == word[0])</div><div class=\"line\">\t\t\t{</div><div class=\"line\">\t\t\t\tfind(i, j, 0, word, board, res,visited);</div><div class=\"line\">\t\t\t\tif (res)</div><div class=\"line\">\t\t\t\t\tbreak;</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t\tif (res)</div><div class=\"line\">\t\t\t\tbreak;</div><div class=\"line\">\t\t}</div><div class=\"line\">\t}</div><div class=\"line\">\treturn res;</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h2 id=\"后记\"><a href=\"#后记\" class=\"headerlink\" title=\"后记\"></a>后记</h2><p>这一周也终于结束了,感觉好像有点堕落有点拖拉,效率很低,希望加把劲咯<br>总是需要一门很熟悉的语言!go 和 C++?</p>\n","site":{"data":{}},"excerpt":"","more":"<blockquote>\n<p>写在前面</p>\n</blockquote>\n<p>实在是无法忍受自己写代码如此差劲,心里所想的东西完无法用代码表达出来,因此打算从头开始一点一点的刷 LeetCode<br>我的计划是每天<strong>至少</strong>写两道题并进行总结,一周发一次Blog,虽然这个可能花费大量时间,但胜在能够打牢基础,我认为这还是值得的<br>每道题都会由以下几个部分组成:</p>\n<ul>\n<li>题目</li>\n<li>代码</li>\n<li>如果没有代码,就写没有考虑到的地方</li>\n<li>优秀的代码和解析</li>\n</ul>\n<p>希望能坚持啊!兄弟!</p>\n<h2 id=\"Spiral-Matrix\"><a href=\"#Spiral-Matrix\" class=\"headerlink\" title=\"Spiral Matrix\"></a>Spiral Matrix</h2><p>Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order.</p>\n<p>For example,<br>Given the following matrix:<br><figure class=\"highlight c++\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div></pre></td><td class=\"code\"><pre><div class=\"line\">[</div><div class=\"line\"> [ <span class=\"number\">1</span>, <span class=\"number\">2</span>, <span class=\"number\">3</span> ],</div><div class=\"line\"> [ <span class=\"number\">4</span>, <span class=\"number\">5</span>, <span class=\"number\">6</span> ],</div><div class=\"line\"> [ <span class=\"number\">7</span>, <span class=\"number\">8</span>, <span class=\"number\">9</span> ]</div><div class=\"line\">]</div></pre></td></tr></table></figure></p>\n<p>You should return <code>[1,2,3,6,9,8,7,4,5]</code></p>\n<h3 id=\"解析\"><a href=\"#解析\" class=\"headerlink\" title=\"解析\"></a>解析</h3><p>这道题就是以螺旋的方式遍历整个二维数组,我的思路是设置1个变量k用于计数,还有4个变量用于指向四个遍历方向,还有一个二维数组记录每一个位置上的数字是否被访问过,若是,跳过,有点傻有点啰嗦</p>\n<h3 id=\"代码\"><a href=\"#代码\" class=\"headerlink\" title=\"代码\"></a>代码</h3><figure class=\"highlight c\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div><div class=\"line\">29</div><div class=\"line\">30</div><div class=\"line\">31</div><div class=\"line\">32</div><div class=\"line\">33</div><div class=\"line\">34</div><div class=\"line\">35</div><div class=\"line\">36</div><div class=\"line\">37</div><div class=\"line\">38</div><div class=\"line\">39</div><div class=\"line\">40</div><div class=\"line\">41</div><div class=\"line\">42</div><div class=\"line\">43</div><div class=\"line\">44</div><div class=\"line\">45</div><div class=\"line\">46</div><div class=\"line\">47</div><div class=\"line\">48</div><div class=\"line\">49</div><div class=\"line\">50</div><div class=\"line\">51</div><div class=\"line\">52</div><div class=\"line\">53</div><div class=\"line\">54</div><div class=\"line\">55</div><div class=\"line\">56</div><div class=\"line\">57</div><div class=\"line\">58</div></pre></td><td class=\"code\"><pre><div class=\"line\"> <span class=\"built_in\">vector</span><<span class=\"keyword\">int</span>> spiralOrder(<span class=\"built_in\">vector</span><<span class=\"built_in\">vector</span><<span class=\"keyword\">int</span>>>& matrix) {</div><div class=\"line\"> <span class=\"built_in\">vector</span><<span class=\"keyword\">int</span>>res;</div><div class=\"line\"> <span class=\"keyword\">if</span>(matrix.empty())</div><div class=\"line\"> <span class=\"keyword\">return</span> res;</div><div class=\"line\">\t<span class=\"keyword\">int</span> m = matrix.size();</div><div class=\"line\">\t<span class=\"keyword\">int</span> n = matrix[<span class=\"number\">0</span>].size();</div><div class=\"line\">\t<span class=\"keyword\">int</span> a[<span class=\"number\">50</span>][<span class=\"number\">50</span>];</div><div class=\"line\">\t<span class=\"built_in\">memset</span>(a, <span class=\"number\">0</span>, <span class=\"keyword\">sizeof</span>(a));</div><div class=\"line\">\t<span class=\"keyword\">int</span> k = <span class=\"number\">0</span>, i, j;</div><div class=\"line\">\t<span class=\"keyword\">int</span> q, w, e, r;</div><div class=\"line\">\tq = r = <span class=\"number\">0</span>;</div><div class=\"line\">\tw = n<span class=\"number\">-1</span>;</div><div class=\"line\">\te = m<span class=\"number\">-1</span>;</div><div class=\"line\">\t<span class=\"keyword\">while</span> (k < m*n)</div><div class=\"line\">\t{</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> (i = <span class=\"number\">0</span>; i < n; i++)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\t<span class=\"keyword\">if</span> (a[q][i] == <span class=\"number\">0</span>)</div><div class=\"line\">\t\t\t{</div><div class=\"line\">\t\t\t\tres.push_back(matrix[q][i]);</div><div class=\"line\">\t\t\t\ta[q][i] = <span class=\"number\">1</span>;</div><div class=\"line\">\t\t\t\tk++;</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tq++;</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> (i = <span class=\"number\">0</span>; i < m; i++)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\t<span class=\"keyword\">if</span> (a[i][w] == <span class=\"number\">0</span>)</div><div class=\"line\">\t\t\t{</div><div class=\"line\">\t\t\t\tres.push_back(matrix[i][w]);</div><div class=\"line\">\t\t\t\ta[i][w] = <span class=\"number\">1</span>;</div><div class=\"line\">\t\t\t\tk++;</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tw--;</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> (i = n - <span class=\"number\">1</span>; i >= <span class=\"number\">0</span>; i--)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\t<span class=\"keyword\">if</span> (a[e][i] == <span class=\"number\">0</span>)</div><div class=\"line\">\t\t\t{</div><div class=\"line\">\t\t\t\tres.push_back(matrix[e][i]);</div><div class=\"line\">\t\t\t\ta[e][i] = <span class=\"number\">1</span>;</div><div class=\"line\">\t\t\t\tk++;</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\te--;</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> (i = m - <span class=\"number\">1</span>; i >= <span class=\"number\">0</span>; i--)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\t<span class=\"keyword\">if</span> (a[i][r] == <span class=\"number\">0</span>)</div><div class=\"line\">\t\t\t{</div><div class=\"line\">\t\t\t\tres.push_back(matrix[i][r]);</div><div class=\"line\">\t\t\t\ta[i][r] = <span class=\"number\">1</span>;</div><div class=\"line\">\t\t\t\tk++;</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tr++;</div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"keyword\">return</span> res;</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h3 id=\"简便的代码\"><a href=\"#简便的代码\" class=\"headerlink\" title=\"简便的代码\"></a>简便的代码</h3><figure class=\"highlight c\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div><div class=\"line\">29</div><div class=\"line\">30</div><div class=\"line\">31</div><div class=\"line\">32</div><div class=\"line\">33</div><div class=\"line\">34</div><div class=\"line\">35</div><div class=\"line\">36</div><div class=\"line\">37</div><div class=\"line\">38</div><div class=\"line\">39</div><div class=\"line\">40</div><div class=\"line\">41</div><div class=\"line\">42</div><div class=\"line\">43</div><div class=\"line\">44</div><div class=\"line\">45</div><div class=\"line\">46</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">public</span> <span class=\"class\"><span class=\"keyword\">class</span> <span class=\"title\">Solution</span> {</span></div><div class=\"line\"> <span class=\"keyword\">public</span> List<Integer> spiralOrder(<span class=\"keyword\">int</span>[][] matrix) {</div><div class=\"line\"> </div><div class=\"line\"> List<Integer> res = <span class=\"keyword\">new</span> ArrayList<Integer>();</div><div class=\"line\"> </div><div class=\"line\"> <span class=\"keyword\">if</span> (matrix.length == <span class=\"number\">0</span>) {</div><div class=\"line\"> <span class=\"keyword\">return</span> res;</div><div class=\"line\"> }</div><div class=\"line\"> </div><div class=\"line\"> <span class=\"keyword\">int</span> rowBegin = <span class=\"number\">0</span>;</div><div class=\"line\"> <span class=\"keyword\">int</span> rowEnd = matrix.length<span class=\"number\">-1</span>;</div><div class=\"line\"> <span class=\"keyword\">int</span> colBegin = <span class=\"number\">0</span>;</div><div class=\"line\"> <span class=\"keyword\">int</span> colEnd = matrix[<span class=\"number\">0</span>].length - <span class=\"number\">1</span>;</div><div class=\"line\"> </div><div class=\"line\"> <span class=\"keyword\">while</span> (rowBegin <= rowEnd && colBegin <= colEnd) {</div><div class=\"line\"> <span class=\"comment\">// Traverse Right</span></div><div class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> j = colBegin; j <= colEnd; j ++) {</div><div class=\"line\"> res.add(matrix[rowBegin][j]);</div><div class=\"line\"> }</div><div class=\"line\"> rowBegin++;</div><div class=\"line\"> </div><div class=\"line\"> <span class=\"comment\">// Traverse Down</span></div><div class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> j = rowBegin; j <= rowEnd; j ++) {</div><div class=\"line\"> res.add(matrix[j][colEnd]);</div><div class=\"line\"> }</div><div class=\"line\"> colEnd--;</div><div class=\"line\"> </div><div class=\"line\"> <span class=\"keyword\">if</span> (rowBegin <= rowEnd) {</div><div class=\"line\"> <span class=\"comment\">// Traverse Left</span></div><div class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> j = colEnd; j >= colBegin; j --) {</div><div class=\"line\"> res.add(matrix[rowEnd][j]);</div><div class=\"line\"> }</div><div class=\"line\"> }</div><div class=\"line\"> rowEnd--;</div><div class=\"line\"> </div><div class=\"line\"> <span class=\"keyword\">if</span> (colBegin <= colEnd) {</div><div class=\"line\"> <span class=\"comment\">// Traver Up</span></div><div class=\"line\"> <span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> j = rowEnd; j >= rowBegin; j --) {</div><div class=\"line\"> res.add(matrix[j][colBegin]);</div><div class=\"line\"> }</div><div class=\"line\"> }</div><div class=\"line\"> colBegin ++;</div><div class=\"line\"> }</div><div class=\"line\"> </div><div class=\"line\"> <span class=\"keyword\">return</span> res;</div><div class=\"line\"> }</div></pre></td></tr></table></figure>\n<p>同样也是while中套着循环,同时也是利用了四个变量分别指向行、列的开始、结尾的位置,可以通过这四个变量设置循环的次数,而不用另外开辟一个数组来记录该数字是否被访问过。</p>\n<h2 id=\"Maximum-Subarray\"><a href=\"#Maximum-Subarray\" class=\"headerlink\" title=\"Maximum Subarray\"></a>Maximum Subarray</h2><p>Find the contiguous subarray within an array (containing at least one number) which has the largest sum.</p>\n<p>For example, given the array<code>[-2,1,-3,4,-1,2,1,-5,4]</code>,<br>the contiguous subarray<code>[4,-1,2,1]</code>has the largest sum =<code>6</code>.</p>\n<h3 id=\"愚蠢的代码\"><a href=\"#愚蠢的代码\" class=\"headerlink\" title=\"愚蠢的代码\"></a>愚蠢的代码</h3><p>这代码一看就是会超时的,想过使用DP算法,但是不知道怎么处理<strong>连续</strong><br><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div></pre></td><td class=\"code\"><pre><div class=\"line\">int maxSubArray(vector<int>& nums) {</div><div class=\"line\">\tint max_sum = INT_MIN;</div><div class=\"line\">\tint i;</div><div class=\"line\">\tfor (i = 0; i < nums.size(); i++)</div><div class=\"line\">\t{</div><div class=\"line\">\t\tint temp = func(i, nums);</div><div class=\"line\">\t\tmax_sum = max(max_sum, temp);</div><div class=\"line\">\t}</div><div class=\"line\">\treturn max_sum;</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\">int func(int begin, vector<int> &nums)</div><div class=\"line\">{</div><div class=\"line\">\tint sum = 0;</div><div class=\"line\">\tint max_sum = nums[begin];</div><div class=\"line\">\tint i;</div><div class=\"line\">\tfor (i = begin ; i < nums.size(); i++)</div><div class=\"line\">\t{</div><div class=\"line\">\t\tsum += nums[i];</div><div class=\"line\">\t\tmax_sum = max(sum, max_sum);</div><div class=\"line\">\t}</div><div class=\"line\">\treturn max_sum;</div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<h3 id=\"优秀的代码\"><a href=\"#优秀的代码\" class=\"headerlink\" title=\"优秀的代码\"></a>优秀的代码</h3><figure class=\"highlight c\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">public</span> <span class=\"keyword\">int</span> <span class=\"title\">maxSubArray</span><span class=\"params\">(<span class=\"keyword\">int</span>[] A)</span> </span>{</div><div class=\"line\"> <span class=\"keyword\">int</span> n = A.length;</div><div class=\"line\"> <span class=\"keyword\">int</span>[] dp = <span class=\"keyword\">new</span> <span class=\"keyword\">int</span>[n];<span class=\"comment\">//dp[i]表示从A[0]-A[i]中连续的字串最大值</span></div><div class=\"line\"> dp[<span class=\"number\">0</span>] = A[<span class=\"number\">0</span>];</div><div class=\"line\"> <span class=\"keyword\">int</span> max = dp[<span class=\"number\">0</span>];</div><div class=\"line\"> </div><div class=\"line\"> <span class=\"keyword\">for</span>(<span class=\"keyword\">int</span> i = <span class=\"number\">1</span>; i < n; i++){</div><div class=\"line\"> dp[i] = A[i] + (dp[i - <span class=\"number\">1</span>] > <span class=\"number\">0</span> ? dp[i - <span class=\"number\">1</span>] : <span class=\"number\">0</span>);</div><div class=\"line\"> max = Math.max(max, dp[i]);</div><div class=\"line\"> }</div><div class=\"line\"> </div><div class=\"line\"> <span class=\"keyword\">return</span> max;</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<p>当前的字串最大值,是当前的值 + 之前的的字串的值 是否大于0,若小于,相当于重新开始计算子串的最大值</p>\n<h2 id=\"56-Merge-Intervals\"><a href=\"#56-Merge-Intervals\" class=\"headerlink\" title=\"56. Merge Intervals\"></a>56. Merge Intervals</h2><p>Given a collection of intervals, merge all overlapping intervals.</p>\n<p>For example,<br>Given<code>[1,3],[2,6],[8,10],[15,18]</code>,<br>return<code>[1,6],[8,10],[15,18]</code>.</p>\n<h3 id=\"解析-1\"><a href=\"#解析-1\" class=\"headerlink\" title=\"解析\"></a>解析</h3><p>这道题看上去还是十分简单的,就是遍历如果前一个的end大于后一个start,则赋值并继续循环<br>如果不符合,就加到结果的<code>vector</code>中,并更新start和end的值<br><strong>注意点</strong></p>\n<ol>\n<li>首先要对其进行排序,按照start升序</li>\n<li>考虑多种情况,也有可能前一个包含后一个</li>\n</ol>\n<h3 id=\"代码-1\"><a href=\"#代码-1\" class=\"headerlink\" title=\"代码\"></a>代码</h3><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div><div class=\"line\">29</div><div class=\"line\">30</div><div class=\"line\">31</div><div class=\"line\">32</div><div class=\"line\">33</div><div class=\"line\">34</div><div class=\"line\">35</div></pre></td><td class=\"code\"><pre><div class=\"line\">static bool cmp(const Interval &a, const Interval &b)</div><div class=\"line\">{</div><div class=\"line\">\treturn a.start < b.start;</div><div class=\"line\">}</div><div class=\"line\"> </div><div class=\"line\">vector<Interval> merge(vector<Interval>& intervals) {</div><div class=\"line\">\tint i, j, k;</div><div class=\"line\">\tvector<Interval>res;</div><div class=\"line\"> if (intervals.empty())</div><div class=\"line\">\t\treturn res;</div><div class=\"line\"> sort(intervals.begin(), intervals.end(), cmp);</div><div class=\"line\">\tint start = intervals[i].start;</div><div class=\"line\">\tint end = intervals[i].end;</div><div class=\"line\">\tfor (i = 1; i < intervals.size(); i++)</div><div class=\"line\">\t{</div><div class=\"line\"> if (intervals[i].start >= start && intervals[i].end <= end)</div><div class=\"line\"> continue;</div><div class=\"line\">\t\tif (intervals[i].start <= end)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tend = intervals[i].end;</div><div class=\"line\">\t\t\tcontinue;</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\telse</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tInterval tmp = { start, end };</div><div class=\"line\">\t\t\tres.push_back(tmp);</div><div class=\"line\">\t\t\tstart = intervals[i].start;</div><div class=\"line\">\t\t\tend = intervals[i].end;</div><div class=\"line\">\t\t}</div><div class=\"line\">\t}</div><div class=\"line\"> Interval tmp = { start, end };</div><div class=\"line\">\tres.push_back(tmp);</div><div class=\"line\">\treturn res;</div><div class=\"line\"> </div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h2 id=\"55-Jump-Game\"><a href=\"#55-Jump-Game\" class=\"headerlink\" title=\"55. Jump Game\"></a>55. Jump Game</h2><p>Given an array of non-negative integers, you are initially positioned at the first index of the array.</p>\n<p>Each element in the array represents your maximum jump length at that position.</p>\n<p>Determine if you are able to reach the last index.</p>\n<p>For example:<br>A = <code>[2,3,1,1,4]</code>, return <code>true</code>.</p>\n<p>A = <code>[3,2,1,0,4]</code>, return <code>false</code>.</p>\n<h3 id=\"解题思路\"><a href=\"#解题思路\" class=\"headerlink\" title=\"解题思路\"></a>解题思路</h3><p>用distance表示i之前最远的距离,如果 <code>i > diatance</code>,说明永远到达不了i,就退出循环<br>当distance >= n-1时,至少能跳到最后</p>\n<p>夏令营时,我采用的是深搜,显然会超时</p>\n<h3 id=\"代码-2\"><a href=\"#代码-2\" class=\"headerlink\" title=\"代码\"></a>代码</h3><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div></pre></td><td class=\"code\"><pre><div class=\"line\">bool canJump(vector<int>& nums) {</div><div class=\"line\">\tint distance = 0;</div><div class=\"line\">\tint n = nums.size();</div><div class=\"line\">\tfor (int i = 0; i < n && i <= distance; i++)</div><div class=\"line\">\t{</div><div class=\"line\">\t\tdistance = max(nums[i] + i, distance);</div><div class=\"line\">\t}</div><div class=\"line\">\tif (distance < n - 1)</div><div class=\"line\">\t\treturn false;</div><div class=\"line\">\treturn true;</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h2 id=\"57-Insert-Interval\"><a href=\"#57-Insert-Interval\" class=\"headerlink\" title=\"57.Insert Interval\"></a>57.Insert Interval</h2><p>Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary).</p>\n<p>You may assume that the intervals were initially sorted according to their start times.</p>\n<p><strong>Example 1:</strong><br>Given intervals<code>[1,3],[6,9]</code>, insert and merge <code>[2,5]</code> in as <code>[1,5],[6,9]</code>.</p>\n<p><strong>Example 2:</strong><br>Given <code>[1,2],[3,5],[6,7],[8,10],[12,16]</code>, insert and merge <code>[4,9]</code> in as <code>[1,2],[3,10],[12,16]</code>.</p>\n<p>This is because the new interval <code>[4,9]</code> overlaps with <code>[3,5],[6,7],[8,10]</code>.</p>\n<h3 id=\"解题思路-1\"><a href=\"#解题思路-1\" class=\"headerlink\" title=\"解题思路\"></a>解题思路</h3><p>这个合并可以分为三个部分</p>\n<ol>\n<li><code>end < newInterval.stat</code>,直接将其添加至新的<code>res</code>中</li>\n<li><code>start > newInterval.end</code>,同上</li>\n<li>有部分相交,<code>start <= newInterval.end</code>的部分,则<code>start</code>取最小值,<code>end</code>取最大值,并<code>push</code>进入结果中<h3 id=\"代码-3\"><a href=\"#代码-3\" class=\"headerlink\" title=\"代码\"></a>代码</h3></li>\n</ol>\n<figure class=\"highlight c\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div><div class=\"line\">29</div><div class=\"line\">30</div><div class=\"line\">31</div><div class=\"line\">32</div><div class=\"line\">33</div><div class=\"line\">34</div><div class=\"line\">35</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"built_in\">vector</span><Interval> insert(<span class=\"built_in\">vector</span><Interval>& intervals, Interval newInterval) {</div><div class=\"line\">\t<span class=\"keyword\">int</span> i = <span class=\"number\">0</span> ;</div><div class=\"line\">\t<span class=\"keyword\">int</span> s = newInterval.start;</div><div class=\"line\">\t<span class=\"keyword\">int</span> e = newInterval.end;</div><div class=\"line\">\t<span class=\"built_in\">vector</span><Interval>res;</div><div class=\"line\">\t<span class=\"keyword\">if</span> (intervals.empty())</div><div class=\"line\">\t{</div><div class=\"line\">\t\tintervals.push_back(newInterval);</div><div class=\"line\">\t\t<span class=\"keyword\">return</span> intervals;</div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"comment\">//前部</span></div><div class=\"line\"></div><div class=\"line\">\t<span class=\"keyword\">while</span> (i < intervals.size() && newInterval.start > intervals[i].end)</div><div class=\"line\">\t{</div><div class=\"line\">\t\tres.push_back(intervals[i]);</div><div class=\"line\">\t\ti++;</div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"comment\">//中间</span></div><div class=\"line\">\t<span class=\"keyword\">while</span> (i < intervals.size() && intervals[i].start <= newInterval.end)</div><div class=\"line\">\t{</div><div class=\"line\">\t\ts = min(s, intervals[i].start);</div><div class=\"line\">\t\te = max(e, intervals[i].end);</div><div class=\"line\">\t\ti++;</div><div class=\"line\">\t}</div><div class=\"line\">\tInterval t{ s, e };</div><div class=\"line\">\tres.push_back(t);</div><div class=\"line\"></div><div class=\"line\">\t<span class=\"comment\">//后部</span></div><div class=\"line\">\t<span class=\"keyword\">while</span> (i < intervals.size() && newInterval.end < intervals[i].start)</div><div class=\"line\">\t{</div><div class=\"line\">\t\tres.push_back(intervals[i]);</div><div class=\"line\">\t\ti++;</div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"keyword\">return</span> res;</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h2 id=\"Spiral-Matrix-II\"><a href=\"#Spiral-Matrix-II\" class=\"headerlink\" title=\"Spiral Matrix II\"></a>Spiral Matrix II</h2><p>Given an integer n, generate a square matrix filled with elements from 1 to n2 in spiral order.</p>\n<p>For example,<br>Given n = <code>3</code>,</p>\n<p>You should return the following matrix:<br><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div></pre></td><td class=\"code\"><pre><div class=\"line\">[</div><div class=\"line\"> [ 1, 2, 3 ],</div><div class=\"line\"> [ 8, 9, 4 ],</div><div class=\"line\"> [ 7, 6, 5 ]</div><div class=\"line\">]</div></pre></td></tr></table></figure></p>\n<h3 id=\"解题思路-2\"><a href=\"#解题思路-2\" class=\"headerlink\" title=\"解题思路\"></a>解题思路</h3><p>和之前的遍历类似,不再赘述,重要的是如何用verctor定义一个二维数组!!!<br><code>vector<vector<int>> res(n,vector<int>(n))</code> !!!<br><code>vector<vector<int>> res(n,vector<int>(n))</code> !!!</p>\n<h3 id=\"代码-4\"><a href=\"#代码-4\" class=\"headerlink\" title=\"代码\"></a>代码</h3><figure class=\"highlight c\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div><div class=\"line\">29</div><div class=\"line\">30</div><div class=\"line\">31</div><div class=\"line\">32</div><div class=\"line\">33</div><div class=\"line\">34</div><div class=\"line\">35</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"built_in\">vector</span><<span class=\"built_in\">vector</span><<span class=\"keyword\">int</span>>> generateMatrix(<span class=\"keyword\">int</span> n) {</div><div class=\"line\">\t<span class=\"built_in\">vector</span><<span class=\"built_in\">vector</span><<span class=\"keyword\">int</span>>> res(n, <span class=\"built_in\">vector</span><<span class=\"keyword\">int</span>>(n));<span class=\"comment\">//!!!!!!!!!!!!</span></div><div class=\"line\">\t<span class=\"keyword\">int</span> i, j, k, l;</div><div class=\"line\">\t<span class=\"keyword\">int</span> c = <span class=\"number\">1</span>;</div><div class=\"line\">\ti = j = <span class=\"number\">0</span>;</div><div class=\"line\">\tk = l = n - <span class=\"number\">1</span>;</div><div class=\"line\">\t<span class=\"keyword\">while</span> (c <= n*n) </div><div class=\"line\">\t{</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> m = j; m <= l; m++)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tres[i][m] = c;</div><div class=\"line\">\t\t\tc++;</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\ti++;</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> m = i; m <= k; m++)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tres[m][l] = c;</div><div class=\"line\">\t\t\tc++;</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tl--;</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> m = l; m >= j; m--)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tres[k][m] = c;</div><div class=\"line\">\t\t\tc++;</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tk--;</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> (<span class=\"keyword\">int</span> m = k; m >= i; m--)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tres[m][j] = c;</div><div class=\"line\">\t\t\tc++;</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tj++;</div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"keyword\">return</span> res;</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h2 id=\"Unique-Paths-II\"><a href=\"#Unique-Paths-II\" class=\"headerlink\" title=\"Unique Paths II\"></a>Unique Paths II</h2><p>Follow up for “Unique Paths”:</p>\n<p>Now consider if some obstacles are added to the grids. How many unique paths would there be?</p>\n<p>An obstacle and empty space is marked as <code>1</code> and <code>0</code> respectively in the grid.</p>\n<p>For example,<br>There is one obstacle in the middle of a 3x3 grid as illustrated below.<br><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div></pre></td><td class=\"code\"><pre><div class=\"line\">[</div><div class=\"line\"> [0,0,0],</div><div class=\"line\"> [0,1,0],</div><div class=\"line\"> [0,0,0]</div><div class=\"line\">]</div></pre></td></tr></table></figure></p>\n<p>The total number of unique paths is <code>2</code>.</p>\n<p>Note: m and n will be at most 100.</p>\n<h3 id=\"解决思路\"><a href=\"#解决思路\" class=\"headerlink\" title=\"解决思路\"></a>解决思路</h3><p>这道题采用DP算法,到达当前点的路径数量等于左边和上面点的路径之和,同时要考虑边界的问题以及碰到障碍物时的处理办法</p>\n<p>###代码<br><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div></pre></td><td class=\"code\"><pre><div class=\"line\">int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {</div><div class=\"line\">\tint row = obstacleGrid.size();</div><div class=\"line\">\tint col = obstacleGrid[0].size();</div><div class=\"line\">\tint i, j;</div><div class=\"line\">\tvector<vector<int>> dp(row, vector<int>(col, 0));</div><div class=\"line\">\tfor (i = 0; i < row; i++)</div><div class=\"line\">\t{</div><div class=\"line\">\t\tfor (j = 0; j < col; j++)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tif (obstacleGrid[i][j] == 1)</div><div class=\"line\">\t\t\t\tdp[i][j] = 0;</div><div class=\"line\">\t\t\telse if (i == 0 && j == 0)</div><div class=\"line\">\t\t\t\tdp[i][j] = 1;</div><div class=\"line\">\t\t\telse if (i == 0 && j > 0)</div><div class=\"line\">\t\t\t\tdp[i][j] = dp[i][j - 1];</div><div class=\"line\">\t\t\telse if (j == 0 && i > 0)</div><div class=\"line\">\t\t\t\tdp[i][j] = dp[i - 1][j];</div><div class=\"line\">\t\t\telse if (i > 0 && j > 0)</div><div class=\"line\">\t\t\t\tdp[i][j] = dp[i - 1][j] + dp[i][j - 1];</div><div class=\"line\">\t\t}</div><div class=\"line\">\t}</div><div class=\"line\">\treturn dp[row - 1][col - 1];</div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<p>还有直接将边界扩大一圈,直接考虑边界的问题,代码如下<br><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div></pre></td><td class=\"code\"><pre><div class=\"line\">int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {</div><div class=\"line\"> int m = obstacleGrid.size(), n = obstacleGrid[0].size();</div><div class=\"line\"> vector<vector<int> > dp(m + 1, vector<int> (n + 1, 0));</div><div class=\"line\"> dp[0][1] = 1;</div><div class=\"line\"> for (int i = 1; i <= m; i++)</div><div class=\"line\"> for (int j = 1; j <= n; j++)</div><div class=\"line\"> if (!obstacleGrid[i - 1][j - 1])</div><div class=\"line\"> dp[i][j] = dp[i - 1][j] + dp[i][j - 1];</div><div class=\"line\"> return dp[m][n];</div><div class=\"line\"> }</div></pre></td></tr></table></figure></p>\n<h2 id=\"93-Restore-IP-Addresses\"><a href=\"#93-Restore-IP-Addresses\" class=\"headerlink\" title=\"93. Restore IP Addresses\"></a>93. Restore IP Addresses</h2><p>Given a string containing only digits, restore it by returning all possible valid IP address combinations.</p>\n<p>For example:<br>Given <code>"25525511135"</code>,</p>\n<p>return <code>["255.255.11.135", "255.255.111.35"]</code>. (Order does not matter)</p>\n<h3 id=\"解题思路-3\"><a href=\"#解题思路-3\" class=\"headerlink\" title=\"解题思路\"></a>解题思路</h3><p>采用dfs,每一次长度为1~3,如果这一小块的长度为3,且值>255不符合;如果起始值为0且长度>1则不符合<br>最终当count==4并且指针指向结尾的时候符合要求并添加进结果中</p>\n<h3 id=\"代码-5\"><a href=\"#代码-5\" class=\"headerlink\" title=\"代码\"></a>代码</h3><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div></pre></td><td class=\"code\"><pre><div class=\"line\">void dfs(string ip,vector<string>& res, int idx, int count, string s)</div><div class=\"line\">{</div><div class=\"line\">\tif (count > 4)</div><div class=\"line\">\t\treturn;</div><div class=\"line\">\tif (count == 4 && idx == ip.length())</div><div class=\"line\">\t{</div><div class=\"line\">\t\tres.push_back(s);</div><div class=\"line\">\t\treturn;</div><div class=\"line\">\t}</div><div class=\"line\">\tfor (int i = 1; i < 4; i++)</div><div class=\"line\">\t{</div><div class=\"line\">\t\tif (idx + i > ip.length())</div><div class=\"line\">\t\t\tbreak;</div><div class=\"line\">\t\tstring t = ip.substr(idx , i);</div><div class=\"line\">\t\tif ((t[0] == '0' && t.length() > 1) || (i == 3 && atoi(t.c_str()) > 255))</div><div class=\"line\">\t\t\tcontinue;</div><div class=\"line\">\t\tdfs(ip, res, idx + i, count + 1, s + t + (count == 3 ? "" : "."));</div><div class=\"line\">\t}</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"></div><div class=\"line\">vector<string> restoreIpAddresses(string s) {</div><div class=\"line\">\tvector<string>res;</div><div class=\"line\">\tdfs(s, res, 0, 0, "");</div><div class=\"line\">\treturn res;</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h2 id=\"79-Word-Search\"><a href=\"#79-Word-Search\" class=\"headerlink\" title=\"79. Word Search\"></a>79. Word Search</h2><p>Given a 2D board and a word, find if the word exists in the grid.</p>\n<p>The word can be constructed from letters of sequentially adjacent cell, where “adjacent” cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.</p>\n<p>For example,<br>Given <strong>board</strong> =<br><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div></pre></td><td class=\"code\"><pre><div class=\"line\">[</div><div class=\"line\"> ['A','B','C','E'],</div><div class=\"line\"> ['S','F','C','S'],</div><div class=\"line\"> ['A','D','E','E']</div><div class=\"line\">]</div></pre></td></tr></table></figure></p>\n<p>word = <code>"ABCCED"</code>, -> returns <code>true</code>,<br>word = <code>"SEE"</code>, -> returns <code>true</code>,<br>word = <code>"ABCB"</code>, -> returns <code>false</code>.</p>\n<h3 id=\"解题思路-4\"><a href=\"#解题思路-4\" class=\"headerlink\" title=\"解题思路\"></a>解题思路</h3><p>这一次真的可以采用深搜的方法,从一个匹配的字母处开始寻找,规定查找的四个方向,如果当前字母与字符串中的字母相同,搜索下一个字母。<br>同时,不能重复字母,因此可以创建一个与<code>board</code>相同大小的二维向量用于表示是否访问过<br>记得回溯!</p>\n<h2 id=\"代码-6\"><a href=\"#代码-6\" class=\"headerlink\" title=\"代码\"></a>代码</h2><figure class=\"highlight plain\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div><div class=\"line\">29</div><div class=\"line\">30</div><div class=\"line\">31</div><div class=\"line\">32</div><div class=\"line\">33</div><div class=\"line\">34</div><div class=\"line\">35</div><div class=\"line\">36</div><div class=\"line\">37</div><div class=\"line\">38</div><div class=\"line\">39</div><div class=\"line\">40</div><div class=\"line\">41</div><div class=\"line\">42</div><div class=\"line\">43</div><div class=\"line\">44</div><div class=\"line\">45</div><div class=\"line\">46</div><div class=\"line\">47</div></pre></td><td class=\"code\"><pre><div class=\"line\">void find(int i, int j, int index, string word, vector<vector<char>>& board, int& res, vector<vector<int>>& visited)</div><div class=\"line\">{</div><div class=\"line\"> int idx[] = { 1, -1, 0, 0 };</div><div class=\"line\"> int idy[] = { 0, 0, -1, 1 };</div><div class=\"line\">\tif (res == 0)</div><div class=\"line\">\t{</div><div class=\"line\">\t\tif (i < 0 || i >= board.size() || j < 0 || j >= board[i].size()||visited[i][j])</div><div class=\"line\">\t\t\treturn;</div><div class=\"line\">\t\tif (board[i][j] != word[index])</div><div class=\"line\">\t\t\treturn;</div><div class=\"line\">\t\tif (index == word.length() - 1)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tres = 1;</div><div class=\"line\">\t\t\treturn;</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tfor (int k = 0; k < 4; k++)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tint tx = i + idx[k];</div><div class=\"line\">\t\t\tint ty = j + idy[k];</div><div class=\"line\">\t\t\tvisited[i][j] = 1;</div><div class=\"line\">\t\t\tfind(tx, ty, index + 1, word, board, res,visited);</div><div class=\"line\">\t\t\tvisited[i][j] = 0;</div><div class=\"line\"></div><div class=\"line\">\t\t}</div><div class=\"line\">\t}</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\">bool exist(vector<vector<char>>& board, string word) {</div><div class=\"line\">\tvector<vector<int>> visited(board.size(), vector<int>(board[0].size(), 0));</div><div class=\"line\">\tint res = 0;</div><div class=\"line\">\tint i, j, k;</div><div class=\"line\">\tfor (i = 0; i < board.size(); i++)</div><div class=\"line\">\t{</div><div class=\"line\">\t\tfor (j = 0; j < board[i].size(); j++)</div><div class=\"line\">\t\t{</div><div class=\"line\">\t\t\tif (board[i][j] == word[0])</div><div class=\"line\">\t\t\t{</div><div class=\"line\">\t\t\t\tfind(i, j, 0, word, board, res,visited);</div><div class=\"line\">\t\t\t\tif (res)</div><div class=\"line\">\t\t\t\t\tbreak;</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t\tif (res)</div><div class=\"line\">\t\t\t\tbreak;</div><div class=\"line\">\t\t}</div><div class=\"line\">\t}</div><div class=\"line\">\treturn res;</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h2 id=\"后记\"><a href=\"#后记\" class=\"headerlink\" title=\"后记\"></a>后记</h2><p>这一周也终于结束了,感觉好像有点堕落有点拖拉,效率很低,希望加把劲咯<br>总是需要一门很熟悉的语言!go 和 C++?</p>\n"},{"title":"Raft-PartA","date":"2017-10-22T10:39:29.000Z","_content":"本次的实验为MIT6.824 Distribute System 中 Lab2 raft part A,通过课程所给的代码的骨架,要求实验raft中选举部分的功能。由于我的水平较渣,觉得课程的难度较大,与其说是实验比如说是对Github上的大神的代码进行理解,并结合论文进行思考。希望有朝一日能够自己做出一个实验啊!\n\n> Implement leader election and heartbeats (`AppendEntries` RPCs with no log entries). The goal for Part 2A is for a single leader to be elected, for the leader to remain the leader if there are no failures, and for a new leader to take over if the old leader fails or if packets to/from the old leader are lost. Run `go test -run 2A` to test your 2A code.\n\nPart A 部分要求实现raft的选举功能,其中raft中的每一个server都会维护自己的一个raft的结构体,其中只有两种RPC:`AppendEntries RPC` 和 `RequestVote RPC`。其中当`AppendEntres RPC`中的log为空时用作`HeartBeat`维护server 和 follower之间的通信,而`RequestVote RPC`则用作当follower转变成candidate时,向其他的server请求投票时的通信。整个选举的过程可以概括为下面的这张图,而其中又有许多的细节需要结合论文中的Figure 2中的每一个条件实现;\n\n{% asset_image election.png election%}\n## 可以概括为如下几个步骤\n1. 系统初始化时,所有的server都是FLLOWER状态,并维护一个计时器,计时结束会成为CANDIDATE并开始选举;FOLLOWER每当收到一次`Herat Beat`或`RequestVote RPC`时重置计时器;\n2. CANDIDATE在选举的时候依旧会维护一个计时器向所有的SERVER发送`Request Vote`,在结束时仍未选举完,重新开始选举。当CANDIDATE获得大部分的投票后变为LEADER;在选举的过程中若收到来自更新的term的`AppendEntries RPC`或者自己的term小于其他server的term时,转变为FOLLOWER;\n3. LEADER开始广播`Heart Beat`包,广播自己成为LEADER的事实,并且维护和FOLLOWER之间的通信。当发现FOLLOWER的reply中有更高的term时转变为FOLLOWER;\n\n## 代码解析\n\n\n### 定义一些常量\n``` go\nconst (\n\tFOLLOWER = iota\n\tCANDIDATE\n\tLEADER\n\n\tHEARTBEAT_INTERVAL = 100\n\tMIN_ELECTION_INTERVAL = 400\n\tMAX_ELECTION_INTERVAL = 500\n)\n```\n### 创建必要的结构体\nraft相当于server中的state machine\n``` go\ntype Raft struct {\n\tmu sync.Mutex // Lock to protect shared access to this peer's state\n\tpeers []*labrpc.ClientEnd // RPC end points of all peers\n\tpersister *Persister // Object to hold this peer's persisted state\n\tme int // this peer's index into peers[]\n\n\t// Your data here (2A, 2B, 2C).\n\t// Look at the paper's Figure 2 for a description of what\n\t// state a Raft server must maintain.\n\n\tvotedFor int //投票给某个CANDIDATE id\n\tvoteAcquired int //收到的票数总数,用于判断能否成为LEADER\n\tstate int32 //server当前的状态\n\tcurrentTerm int32 //server当前的term\n\n\tvoteCh chan struct{} //由于是并行发送用于指示server收到VoteRequest后的操作\n\tappendCh chan struct{} //收到Append Entries后的操作\n\n\telectionTimer *time.Timer //用于FOLLOWER和CANDIDATE的操作\n}\n```\n发送`RequestVote RPC`包中的内容\n```go\ntype RequestVoteArgs struct {\n\tTerm int32\n\tCandidateId int\n}\n```\nserver对于`RequestVote RPC`包的回复\n```go\ntype RequestVoteReply struct {\n\t// Your data here (2A).\n\tTerm int32\n\tVoteGranted bool\n}\n```\n发送`AppendEntries RPC`包中的内容\n```go\n//此处只涉及选举,不考虑replication及log中的内容\ntype AppendEntriesArgs struct {\n\tTerm int32\n\tLeaderID int\n}\n```\n接收`AppendEntries RPC`包,并回复\n同样这里不考虑CANDIDATE中的log是否是最新的,只要有合适的`VoteReuest PRC`就投票\n```go\ntype AppendEntriesReply struct {\n\tTerm int32\n\tSuccess bool\n}\n```\n###一些重要的工具函数\n\n\n```go\nfunc (rf *Raft) getTerm() int32 {\n\treturn atomic.LoadInt32(&rf.currentTerm)\n}\n\nfunc (rf *Raft) incrementTerm() int32 {\n\treturn atomic.AddInt32(&rf.currentTerm, 1)\n}\n\nfunc (rf *Raft) isState(state int32) bool {\n\treturn atomic.LoadInt32(&rf.state) == state\n}\n\nfunc (rf *Raft) GetState() (int, bool) {\n\n\tvar term int\n\tvar isleader bool\n\t// Your code here (2A).\n\tterm = int(rf.getTerm())\n\tisleader = rf.isState(LEADER)\n\treturn term, isleader\n}\n\nfunc randElectionDuration() time.Duration {\n\tr := rand.New(rand.NewSource(time.Now().UnixNano()))\n\treturn time.Millisecond * time.Duration(\n \t\t\tr.Int63n(MAX_ELECTION_INTERVAL-MIN_ELECTION_INTERVAL)+MIN_ELECTION_INTERVAL\n \t)\n}\n```\n\n### CANDIDATE发送`RequestVote RPC`包\n用于CANDIDATE发送包和对reply的处理\n\n- 遍历每一个server,若投票了则voteGranted自增1\n- 若发现reply中的term大于自身当前的term,则转变为FOLLOWER\n\n```go\nfunc (rf *Raft) broadcastVoteReq() {\n\targs := RequestVoteArgs{Term: atomic.LoadInt32(&rf.currentTerm), CandidateId: rf.me}\n\tfor i, _ := range rf.peers {\n\t\tif i == rf.me {\n\t\t\tcontinue\n\t\t}\n\t\t// is it is candidate then send vote req\n\t\tgo func(server int) {\n\t\t\tvar reply RequestVoteReply\n\t\t\tif rf.isState(CANDIDATE) && rf.sendRequestVote(server, &args, &reply) {\n\t\t\t\trf.mu.Lock()\n\t\t\t\tdefer rf.mu.Unlock()\n\t\t\t\t//deal with the reply\n\t\t\t\tif reply.VoteGranted {\n\t\t\t\t\trf.voteAcquired += 1\n\t\t\t\t} else {\n\t\t\t\t\tif reply.Term > rf.currentTerm {\n\t\t\t\t\t\trf.currentTerm = reply.Term\n\t\t\t\t\t\trf.updateStateTo(FOLLOWER)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfmt.Printf(\"Server %d send vote req failed.\\n\", rf.me)\n\t\t\t}\n\t\t}(i)\n\t}\n}\n```\n\n\n### server对`RequestVote RPC`的处理\n依据论文Figure 2中的要求,有以下几个处理的要点:\n- 判断自身的term是否大于CANDIDATE的term,若是则拒绝投票,并更新reply中的term\n- 若当前的term小于CANDIDATE发送的term,更新自身的term,并将自己的状态变为follower(当前状态为CANDIDATE或LEADER)\n- 当两者的term相等时,必须是初始状态,否则拒绝投票(FOLLOWER转变为CANDIDATE时候term自增1,因此必须大于其他server)\n- 投票成功后,利用信道通知主循环(整个操作是并行的)\n\n\n```go\nfunc (rf *Raft) RequestVote(args *RequestVoteArgs, reply *RequestVoteReply) {\n\t// Your code here (2A, 2B).\n\trf.mu.Lock()\n\tdefer rf.mu.Unlock()\n\tif args.Term < rf.currentTerm {\n\t\treply.VoteGranted = false\n\t\treply.Term = rf.currentTerm\n\t} else if args.Term > rf.currentTerm {\n\t\trf.currentTerm = args.Term\n\t\trf.updateStateTo(FOLLOWER)\n\t\trf.votedFor = args.CandidateId\n\t\treply.VoteGranted = true\n\t} else {\n\t\tif rf.votedFor == -1 {\n\t\t\trf.votedFor = args.CandidateId\n\t\t\treply.VoteGranted = true\n\t\t} else {\n\t\t\treply.VoteGranted = false\n\t\t}\n\t}\n\tif reply.VoteGranted == true {\n\t\tgo func() { rf.voteCh <- struct{}{} }()\n\t}\n}\n```\n\n\n### LEADER广播`AppendEntries RPC`,和对reply的处理\n\n由于这里只是单纯的选举所以处理的逻辑很简单,只处理失败的回复:\n\n- 如果reply中的term大于当前LEADER中的term,则LEADER将自己的状态变成FOLLOWER\n\n\n```go\nfunc (rf *Raft) broadcastAppendEnrties() {\n\targs := AppendEntriesArgs{Term: atomic.LoadInt32(&rf.currentTerm), LeaderID: rf.me}\n\tfor i, _ := range rf.peers {\n\t\tif i == rf.me {\n\t\t\tcontinue\n\t\t}\n\t\tgo func(server int) {\n\t\t\tvar reply AppendEntriesReply\n\t\t\tif rf.isState(LEADER) && rf.sendAppendEntries(server, &args, &reply) {\n\t\t\t\trf.mu.Lock()\n\t\t\t\tdefer rf.mu.Unlock()\n\t\t\t\tif reply.Success {\n\n\t\t\t\t} else {\n\t\t\t\t\tif reply.Term > rf.currentTerm {\n\t\t\t\t\t\trf.currentTerm = reply.Term\n\t\t\t\t\t\trf.updateStateTo(FOLLOWER)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}(i)\n\t}\n}\n```\n\n### server对`AppendEntries RPC`的处理\n同样,raft中的term可以作为判断是不是最新的依据,根据Figure 2,server在处理`AppendEntries RPC`,有如下注意点:\n- 如果`args.Term < rf.currentTerm`,`reply.Term = rf.currentTerm`;\n- 如果`args.Term > rf.currentTerm`,更新当前的term并且将状态改变为FOLLOWER(针对当前状态为CANDIDATE的server)\n\n\n```go\nfunc (rf *Raft) AppendEntries(args *AppendEntriesArgs, reply *AppendEntriesReply) {\n\trf.mu.Lock()\n\tdefer rf.mu.Unlock()\n\t//deal with the msg and reply\n\tif args.Term < rf.currentTerm {\n\t\treply.Success = false\n\t\treply.Term = rf.currentTerm\n\t} else if args.Term > rf.currentTerm {\n\t\trf.currentTerm = args.Term\n\t\trf.updateStateTo(FOLLOWER)\n\t\treply.Success = true\n\t} else {\n\t\treply.Success = true\n\t}\n\tgo func() {\n\t\trf.appendCh <- struct{}{}\n\t}()\n}\n```\n\n\n### 状态转变的函数\n\n需要注意的是当转变成FOLLOWER时,要将自己的`VoteFor`初始化为-1,当转变成CANDIDATE时候要注意开始选举LEADER\n```go\nfunc (rf *Raft) updateStateTo(state int32) {\n\tif rf.isState(state) {\n\t\treturn\n\t}\n\tstateDesc := []string{\"FOLLOWER\", \"CANDIDATE\", \"LEADER\"}\n\tpreState := rf.state\n\tswitch state {\n\tcase FOLLOWER:\n\t\trf.state = FOLLOWER\n\t\trf.votedFor = -1\n\tcase CANDIDATE:\n\t\trf.state = CANDIDATE\n\t\trf.startElection()\n\tcase LEADER:\n\t\trf.state = LEADER\n\tdefault:\n\t\tfmt.Printf(\"Warning: invalid state %d, do nothing.\\n\", state)\n\t}\n\tfmt.Printf(\"In term %d: Server %d transfer from %s to %s\\n\",\n\t\trf.currentTerm, rf.me, stateDesc[preState], stateDesc[rf.state])\n\n}\n```\n### 开始新一轮选举的函数\n按照论文的要求在开始选举新的LEADER的时候要重置计时器,因为CANDIDATE会为自己投票所以票数初始置为1\n```go\nfunc (rf *Raft) startElection() {\n\trf.incrementTerm() //first increase current term\n\trf.votedFor = rf.me //vote for self\n\trf.voteAcquired = 1 //acquire self's vote\n\trf.electionTimer.Reset(randElectionDuration())\n\trf.broadcastVoteReq()\n}\n```\n\n#### 整个状态的循环实现\n不断的循环,并根据当前server的角色进行不同的操作\n\n\n#### FOLLOWER\n\n\n- 当收到CANDIDATE发送的`VoteRequest RPC`时,重置计时器\n- 当收到LEADER发送的`AppendEntries RPC`时,重置计时器\n- 当计时器结束时,说明当前的server断绝联系,于是FOLLOWER转变为CANDIDATE开始新一轮的选举\n\n\n#### CANDIDATE\n- 在收到`AppendEntries RPC`时,说明LEADER依旧存在,停止选举转变为FOLLOWER\n- 当计时器结束时,重新开始选举\n- 当投票数大于总数的一半时,当选LEADER\n\n\n#### LEADER\n- 间隔一段时间发送`HeartBeat`心跳包维持LEADER和其他的server的通信\n\n\n```go\nfunc (rf *Raft) startLoop() {\n\trf.electionTimer = time.NewTimer(randElectionDuration())\n\tfor {\n\t\tswitch atomic.LoadInt32(&rf.state) {\n\t\tcase FOLLOWER:\n\t\t\tselect {\n\t\t\tcase <-rf.voteCh:\n\t\t\t\trf.electionTimer.Reset(randElectionDuration())\n\t\t\tcase <-rf.appendCh:\n\t\t\t\trf.electionTimer.Reset(randElectionDuration())\n\t\t\tcase <-rf.electionTimer.C:\n\t\t\t\t//time out\n\t\t\t\trf.mu.Lock()\n\t\t\t\trf.updateStateTo(CANDIDATE)\n\t\t\t\trf.mu.Unlock()\n\t\t\t}\n\t\tcase CANDIDATE:\n\t\t\trf.mu.Lock()\n\t\t\tselect {\n\t\t\tcase <-rf.appendCh:\n\t\t\t\trf.updateStateTo(FOLLOWER)\n\t\t\tcase <-rf.electionTimer.C:\n\t\t\t\trf.electionTimer.Reset(randElectionDuration())\n\t\t\t\trf.startElection()\n\t\t\tdefault:\n\t\t\t\tif rf.voteAcquired > len(rf.peers)/2 {\n\t\t\t\t\trf.updateStateTo(LEADER)\n\t\t\t\t}\n\t\t\t}\n\t\t\trf.mu.Unlock()\n\t\tcase LEADER:\n\t\t\trf.broadcastAppendEnrties()\n\t\t\ttime.Sleep(HEARTBEAT_INTERVAL * time.Millisecond)\n\t\t}\n\n\t}\n```\n\n### 初始化并开始循环\n```go\nfunc Make(peers []*labrpc.ClientEnd, me int,\n\tpersister *Persister, applyCh chan ApplyMsg) *Raft {\n\trf := &Raft{}\n\trf.peers = peers\n\trf.persister = persister\n\trf.me = me\n\n\t// Your initialization code here (2A, 2B, 2C).\n\trf.state = FOLLOWER\n\trf.votedFor = -1\n\trf.voteCh = make(chan struct{})\n\trf.appendCh = make(chan struct{})\n\n\t// initialize from state persisted before a crash\n\trf.readPersist(persister.ReadRaftState())\n\n\tgo rf.startLoop()\n\n\treturn rf\n}\n```\n\n## 总结\n\n---\n第一次将论文中的算法“实现”,虽然是参考了大神的代码,还是觉得收获满满。\n首先,纠正了我看论文一目十行的方法,以前总觉得论文这东西太虚,只是所谓的想法,经过这次无数遍的研读之后,发现论文中的每一句话对于整个算法的执行都有至关重要的作用。因此,以后在看论文的时候,需要逐句理解,最好能够抽象出整个模型,并且思考论文这么做对于整个系统的实现有什么好处\n其次,自己的代码的能力实在是弱的不行,我能看得懂这些代码,并能在脑中把每一个函数链接起来,但是距离完全靠自己的能力去实现一个这样的系统还有相当长的路要走。\n希望能坚持将自己所看的论文、方法都能用自己的话去说出来,加深下理解。\n\n\n\n\n\n\n\n","source":"_posts/Raft-PartA.md","raw":"---\ntitle: Raft-PartA\ndate: 2017-10-22 18:39:29\ncategories:\n- 分布式系统\n- MIT 6.824\ntags:\n- raft\n- golang\n---\n本次的实验为MIT6.824 Distribute System 中 Lab2 raft part A,通过课程所给的代码的骨架,要求实验raft中选举部分的功能。由于我的水平较渣,觉得课程的难度较大,与其说是实验比如说是对Github上的大神的代码进行理解,并结合论文进行思考。希望有朝一日能够自己做出一个实验啊!\n\n> Implement leader election and heartbeats (`AppendEntries` RPCs with no log entries). The goal for Part 2A is for a single leader to be elected, for the leader to remain the leader if there are no failures, and for a new leader to take over if the old leader fails or if packets to/from the old leader are lost. Run `go test -run 2A` to test your 2A code.\n\nPart A 部分要求实现raft的选举功能,其中raft中的每一个server都会维护自己的一个raft的结构体,其中只有两种RPC:`AppendEntries RPC` 和 `RequestVote RPC`。其中当`AppendEntres RPC`中的log为空时用作`HeartBeat`维护server 和 follower之间的通信,而`RequestVote RPC`则用作当follower转变成candidate时,向其他的server请求投票时的通信。整个选举的过程可以概括为下面的这张图,而其中又有许多的细节需要结合论文中的Figure 2中的每一个条件实现;\n\n{% asset_image election.png election%}\n## 可以概括为如下几个步骤\n1. 系统初始化时,所有的server都是FLLOWER状态,并维护一个计时器,计时结束会成为CANDIDATE并开始选举;FOLLOWER每当收到一次`Herat Beat`或`RequestVote RPC`时重置计时器;\n2. CANDIDATE在选举的时候依旧会维护一个计时器向所有的SERVER发送`Request Vote`,在结束时仍未选举完,重新开始选举。当CANDIDATE获得大部分的投票后变为LEADER;在选举的过程中若收到来自更新的term的`AppendEntries RPC`或者自己的term小于其他server的term时,转变为FOLLOWER;\n3. LEADER开始广播`Heart Beat`包,广播自己成为LEADER的事实,并且维护和FOLLOWER之间的通信。当发现FOLLOWER的reply中有更高的term时转变为FOLLOWER;\n\n## 代码解析\n\n\n### 定义一些常量\n``` go\nconst (\n\tFOLLOWER = iota\n\tCANDIDATE\n\tLEADER\n\n\tHEARTBEAT_INTERVAL = 100\n\tMIN_ELECTION_INTERVAL = 400\n\tMAX_ELECTION_INTERVAL = 500\n)\n```\n### 创建必要的结构体\nraft相当于server中的state machine\n``` go\ntype Raft struct {\n\tmu sync.Mutex // Lock to protect shared access to this peer's state\n\tpeers []*labrpc.ClientEnd // RPC end points of all peers\n\tpersister *Persister // Object to hold this peer's persisted state\n\tme int // this peer's index into peers[]\n\n\t// Your data here (2A, 2B, 2C).\n\t// Look at the paper's Figure 2 for a description of what\n\t// state a Raft server must maintain.\n\n\tvotedFor int //投票给某个CANDIDATE id\n\tvoteAcquired int //收到的票数总数,用于判断能否成为LEADER\n\tstate int32 //server当前的状态\n\tcurrentTerm int32 //server当前的term\n\n\tvoteCh chan struct{} //由于是并行发送用于指示server收到VoteRequest后的操作\n\tappendCh chan struct{} //收到Append Entries后的操作\n\n\telectionTimer *time.Timer //用于FOLLOWER和CANDIDATE的操作\n}\n```\n发送`RequestVote RPC`包中的内容\n```go\ntype RequestVoteArgs struct {\n\tTerm int32\n\tCandidateId int\n}\n```\nserver对于`RequestVote RPC`包的回复\n```go\ntype RequestVoteReply struct {\n\t// Your data here (2A).\n\tTerm int32\n\tVoteGranted bool\n}\n```\n发送`AppendEntries RPC`包中的内容\n```go\n//此处只涉及选举,不考虑replication及log中的内容\ntype AppendEntriesArgs struct {\n\tTerm int32\n\tLeaderID int\n}\n```\n接收`AppendEntries RPC`包,并回复\n同样这里不考虑CANDIDATE中的log是否是最新的,只要有合适的`VoteReuest PRC`就投票\n```go\ntype AppendEntriesReply struct {\n\tTerm int32\n\tSuccess bool\n}\n```\n###一些重要的工具函数\n\n\n```go\nfunc (rf *Raft) getTerm() int32 {\n\treturn atomic.LoadInt32(&rf.currentTerm)\n}\n\nfunc (rf *Raft) incrementTerm() int32 {\n\treturn atomic.AddInt32(&rf.currentTerm, 1)\n}\n\nfunc (rf *Raft) isState(state int32) bool {\n\treturn atomic.LoadInt32(&rf.state) == state\n}\n\nfunc (rf *Raft) GetState() (int, bool) {\n\n\tvar term int\n\tvar isleader bool\n\t// Your code here (2A).\n\tterm = int(rf.getTerm())\n\tisleader = rf.isState(LEADER)\n\treturn term, isleader\n}\n\nfunc randElectionDuration() time.Duration {\n\tr := rand.New(rand.NewSource(time.Now().UnixNano()))\n\treturn time.Millisecond * time.Duration(\n \t\t\tr.Int63n(MAX_ELECTION_INTERVAL-MIN_ELECTION_INTERVAL)+MIN_ELECTION_INTERVAL\n \t)\n}\n```\n\n### CANDIDATE发送`RequestVote RPC`包\n用于CANDIDATE发送包和对reply的处理\n\n- 遍历每一个server,若投票了则voteGranted自增1\n- 若发现reply中的term大于自身当前的term,则转变为FOLLOWER\n\n```go\nfunc (rf *Raft) broadcastVoteReq() {\n\targs := RequestVoteArgs{Term: atomic.LoadInt32(&rf.currentTerm), CandidateId: rf.me}\n\tfor i, _ := range rf.peers {\n\t\tif i == rf.me {\n\t\t\tcontinue\n\t\t}\n\t\t// is it is candidate then send vote req\n\t\tgo func(server int) {\n\t\t\tvar reply RequestVoteReply\n\t\t\tif rf.isState(CANDIDATE) && rf.sendRequestVote(server, &args, &reply) {\n\t\t\t\trf.mu.Lock()\n\t\t\t\tdefer rf.mu.Unlock()\n\t\t\t\t//deal with the reply\n\t\t\t\tif reply.VoteGranted {\n\t\t\t\t\trf.voteAcquired += 1\n\t\t\t\t} else {\n\t\t\t\t\tif reply.Term > rf.currentTerm {\n\t\t\t\t\t\trf.currentTerm = reply.Term\n\t\t\t\t\t\trf.updateStateTo(FOLLOWER)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfmt.Printf(\"Server %d send vote req failed.\\n\", rf.me)\n\t\t\t}\n\t\t}(i)\n\t}\n}\n```\n\n\n### server对`RequestVote RPC`的处理\n依据论文Figure 2中的要求,有以下几个处理的要点:\n- 判断自身的term是否大于CANDIDATE的term,若是则拒绝投票,并更新reply中的term\n- 若当前的term小于CANDIDATE发送的term,更新自身的term,并将自己的状态变为follower(当前状态为CANDIDATE或LEADER)\n- 当两者的term相等时,必须是初始状态,否则拒绝投票(FOLLOWER转变为CANDIDATE时候term自增1,因此必须大于其他server)\n- 投票成功后,利用信道通知主循环(整个操作是并行的)\n\n\n```go\nfunc (rf *Raft) RequestVote(args *RequestVoteArgs, reply *RequestVoteReply) {\n\t// Your code here (2A, 2B).\n\trf.mu.Lock()\n\tdefer rf.mu.Unlock()\n\tif args.Term < rf.currentTerm {\n\t\treply.VoteGranted = false\n\t\treply.Term = rf.currentTerm\n\t} else if args.Term > rf.currentTerm {\n\t\trf.currentTerm = args.Term\n\t\trf.updateStateTo(FOLLOWER)\n\t\trf.votedFor = args.CandidateId\n\t\treply.VoteGranted = true\n\t} else {\n\t\tif rf.votedFor == -1 {\n\t\t\trf.votedFor = args.CandidateId\n\t\t\treply.VoteGranted = true\n\t\t} else {\n\t\t\treply.VoteGranted = false\n\t\t}\n\t}\n\tif reply.VoteGranted == true {\n\t\tgo func() { rf.voteCh <- struct{}{} }()\n\t}\n}\n```\n\n\n### LEADER广播`AppendEntries RPC`,和对reply的处理\n\n由于这里只是单纯的选举所以处理的逻辑很简单,只处理失败的回复:\n\n- 如果reply中的term大于当前LEADER中的term,则LEADER将自己的状态变成FOLLOWER\n\n\n```go\nfunc (rf *Raft) broadcastAppendEnrties() {\n\targs := AppendEntriesArgs{Term: atomic.LoadInt32(&rf.currentTerm), LeaderID: rf.me}\n\tfor i, _ := range rf.peers {\n\t\tif i == rf.me {\n\t\t\tcontinue\n\t\t}\n\t\tgo func(server int) {\n\t\t\tvar reply AppendEntriesReply\n\t\t\tif rf.isState(LEADER) && rf.sendAppendEntries(server, &args, &reply) {\n\t\t\t\trf.mu.Lock()\n\t\t\t\tdefer rf.mu.Unlock()\n\t\t\t\tif reply.Success {\n\n\t\t\t\t} else {\n\t\t\t\t\tif reply.Term > rf.currentTerm {\n\t\t\t\t\t\trf.currentTerm = reply.Term\n\t\t\t\t\t\trf.updateStateTo(FOLLOWER)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}(i)\n\t}\n}\n```\n\n### server对`AppendEntries RPC`的处理\n同样,raft中的term可以作为判断是不是最新的依据,根据Figure 2,server在处理`AppendEntries RPC`,有如下注意点:\n- 如果`args.Term < rf.currentTerm`,`reply.Term = rf.currentTerm`;\n- 如果`args.Term > rf.currentTerm`,更新当前的term并且将状态改变为FOLLOWER(针对当前状态为CANDIDATE的server)\n\n\n```go\nfunc (rf *Raft) AppendEntries(args *AppendEntriesArgs, reply *AppendEntriesReply) {\n\trf.mu.Lock()\n\tdefer rf.mu.Unlock()\n\t//deal with the msg and reply\n\tif args.Term < rf.currentTerm {\n\t\treply.Success = false\n\t\treply.Term = rf.currentTerm\n\t} else if args.Term > rf.currentTerm {\n\t\trf.currentTerm = args.Term\n\t\trf.updateStateTo(FOLLOWER)\n\t\treply.Success = true\n\t} else {\n\t\treply.Success = true\n\t}\n\tgo func() {\n\t\trf.appendCh <- struct{}{}\n\t}()\n}\n```\n\n\n### 状态转变的函数\n\n需要注意的是当转变成FOLLOWER时,要将自己的`VoteFor`初始化为-1,当转变成CANDIDATE时候要注意开始选举LEADER\n```go\nfunc (rf *Raft) updateStateTo(state int32) {\n\tif rf.isState(state) {\n\t\treturn\n\t}\n\tstateDesc := []string{\"FOLLOWER\", \"CANDIDATE\", \"LEADER\"}\n\tpreState := rf.state\n\tswitch state {\n\tcase FOLLOWER:\n\t\trf.state = FOLLOWER\n\t\trf.votedFor = -1\n\tcase CANDIDATE:\n\t\trf.state = CANDIDATE\n\t\trf.startElection()\n\tcase LEADER:\n\t\trf.state = LEADER\n\tdefault:\n\t\tfmt.Printf(\"Warning: invalid state %d, do nothing.\\n\", state)\n\t}\n\tfmt.Printf(\"In term %d: Server %d transfer from %s to %s\\n\",\n\t\trf.currentTerm, rf.me, stateDesc[preState], stateDesc[rf.state])\n\n}\n```\n### 开始新一轮选举的函数\n按照论文的要求在开始选举新的LEADER的时候要重置计时器,因为CANDIDATE会为自己投票所以票数初始置为1\n```go\nfunc (rf *Raft) startElection() {\n\trf.incrementTerm() //first increase current term\n\trf.votedFor = rf.me //vote for self\n\trf.voteAcquired = 1 //acquire self's vote\n\trf.electionTimer.Reset(randElectionDuration())\n\trf.broadcastVoteReq()\n}\n```\n\n#### 整个状态的循环实现\n不断的循环,并根据当前server的角色进行不同的操作\n\n\n#### FOLLOWER\n\n\n- 当收到CANDIDATE发送的`VoteRequest RPC`时,重置计时器\n- 当收到LEADER发送的`AppendEntries RPC`时,重置计时器\n- 当计时器结束时,说明当前的server断绝联系,于是FOLLOWER转变为CANDIDATE开始新一轮的选举\n\n\n#### CANDIDATE\n- 在收到`AppendEntries RPC`时,说明LEADER依旧存在,停止选举转变为FOLLOWER\n- 当计时器结束时,重新开始选举\n- 当投票数大于总数的一半时,当选LEADER\n\n\n#### LEADER\n- 间隔一段时间发送`HeartBeat`心跳包维持LEADER和其他的server的通信\n\n\n```go\nfunc (rf *Raft) startLoop() {\n\trf.electionTimer = time.NewTimer(randElectionDuration())\n\tfor {\n\t\tswitch atomic.LoadInt32(&rf.state) {\n\t\tcase FOLLOWER:\n\t\t\tselect {\n\t\t\tcase <-rf.voteCh:\n\t\t\t\trf.electionTimer.Reset(randElectionDuration())\n\t\t\tcase <-rf.appendCh:\n\t\t\t\trf.electionTimer.Reset(randElectionDuration())\n\t\t\tcase <-rf.electionTimer.C:\n\t\t\t\t//time out\n\t\t\t\trf.mu.Lock()\n\t\t\t\trf.updateStateTo(CANDIDATE)\n\t\t\t\trf.mu.Unlock()\n\t\t\t}\n\t\tcase CANDIDATE:\n\t\t\trf.mu.Lock()\n\t\t\tselect {\n\t\t\tcase <-rf.appendCh:\n\t\t\t\trf.updateStateTo(FOLLOWER)\n\t\t\tcase <-rf.electionTimer.C:\n\t\t\t\trf.electionTimer.Reset(randElectionDuration())\n\t\t\t\trf.startElection()\n\t\t\tdefault:\n\t\t\t\tif rf.voteAcquired > len(rf.peers)/2 {\n\t\t\t\t\trf.updateStateTo(LEADER)\n\t\t\t\t}\n\t\t\t}\n\t\t\trf.mu.Unlock()\n\t\tcase LEADER:\n\t\t\trf.broadcastAppendEnrties()\n\t\t\ttime.Sleep(HEARTBEAT_INTERVAL * time.Millisecond)\n\t\t}\n\n\t}\n```\n\n### 初始化并开始循环\n```go\nfunc Make(peers []*labrpc.ClientEnd, me int,\n\tpersister *Persister, applyCh chan ApplyMsg) *Raft {\n\trf := &Raft{}\n\trf.peers = peers\n\trf.persister = persister\n\trf.me = me\n\n\t// Your initialization code here (2A, 2B, 2C).\n\trf.state = FOLLOWER\n\trf.votedFor = -1\n\trf.voteCh = make(chan struct{})\n\trf.appendCh = make(chan struct{})\n\n\t// initialize from state persisted before a crash\n\trf.readPersist(persister.ReadRaftState())\n\n\tgo rf.startLoop()\n\n\treturn rf\n}\n```\n\n## 总结\n\n---\n第一次将论文中的算法“实现”,虽然是参考了大神的代码,还是觉得收获满满。\n首先,纠正了我看论文一目十行的方法,以前总觉得论文这东西太虚,只是所谓的想法,经过这次无数遍的研读之后,发现论文中的每一句话对于整个算法的执行都有至关重要的作用。因此,以后在看论文的时候,需要逐句理解,最好能够抽象出整个模型,并且思考论文这么做对于整个系统的实现有什么好处\n其次,自己的代码的能力实在是弱的不行,我能看得懂这些代码,并能在脑中把每一个函数链接起来,但是距离完全靠自己的能力去实现一个这样的系统还有相当长的路要走。\n希望能坚持将自己所看的论文、方法都能用自己的话去说出来,加深下理解。\n\n\n\n\n\n\n\n","slug":"Raft-PartA","published":1,"updated":"2018-04-30T13:49:56.135Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cjyohfve0000osy0ygzy3b03c","content":"<p>本次的实验为MIT6.824 Distribute System 中 Lab2 raft part A,通过课程所给的代码的骨架,要求实验raft中选举部分的功能。由于我的水平较渣,觉得课程的难度较大,与其说是实验比如说是对Github上的大神的代码进行理解,并结合论文进行思考。希望有朝一日能够自己做出一个实验啊!</p>\n<blockquote>\n<p>Implement leader election and heartbeats (<code>AppendEntries</code> RPCs with no log entries). The goal for Part 2A is for a single leader to be elected, for the leader to remain the leader if there are no failures, and for a new leader to take over if the old leader fails or if packets to/from the old leader are lost. Run <code>go test -run 2A</code> to test your 2A code.</p>\n</blockquote>\n<p>Part A 部分要求实现raft的选举功能,其中raft中的每一个server都会维护自己的一个raft的结构体,其中只有两种RPC:<code>AppendEntries RPC</code> 和 <code>RequestVote RPC</code>。其中当<code>AppendEntres RPC</code>中的log为空时用作<code>HeartBeat</code>维护server 和 follower之间的通信,而<code>RequestVote RPC</code>则用作当follower转变成candidate时,向其他的server请求投票时的通信。整个选举的过程可以概括为下面的这张图,而其中又有许多的细节需要结合论文中的Figure 2中的每一个条件实现;</p>\n<img src=\"/2017/10/22/Raft-PartA/election.png\" alt=\"election\" title=\"election\">\n<h2 id=\"可以概括为如下几个步骤\"><a href=\"#可以概括为如下几个步骤\" class=\"headerlink\" title=\"可以概括为如下几个步骤\"></a>可以概括为如下几个步骤</h2><ol>\n<li>系统初始化时,所有的server都是FLLOWER状态,并维护一个计时器,计时结束会成为CANDIDATE并开始选举;FOLLOWER每当收到一次<code>Herat Beat</code>或<code>RequestVote RPC</code>时重置计时器;</li>\n<li>CANDIDATE在选举的时候依旧会维护一个计时器向所有的SERVER发送<code>Request Vote</code>,在结束时仍未选举完,重新开始选举。当CANDIDATE获得大部分的投票后变为LEADER;在选举的过程中若收到来自更新的term的<code>AppendEntries RPC</code>或者自己的term小于其他server的term时,转变为FOLLOWER;</li>\n<li>LEADER开始广播<code>Heart Beat</code>包,广播自己成为LEADER的事实,并且维护和FOLLOWER之间的通信。当发现FOLLOWER的reply中有更高的term时转变为FOLLOWER;</li>\n</ol>\n<h2 id=\"代码解析\"><a href=\"#代码解析\" class=\"headerlink\" title=\"代码解析\"></a>代码解析</h2><h3 id=\"定义一些常量\"><a href=\"#定义一些常量\" class=\"headerlink\" title=\"定义一些常量\"></a>定义一些常量</h3><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">const</span> (</div><div class=\"line\">\tFOLLOWER = <span class=\"literal\">iota</span></div><div class=\"line\">\tCANDIDATE</div><div class=\"line\">\tLEADER</div><div class=\"line\"></div><div class=\"line\">\tHEARTBEAT_INTERVAL = <span class=\"number\">100</span></div><div class=\"line\">\tMIN_ELECTION_INTERVAL = <span class=\"number\">400</span></div><div class=\"line\">\tMAX_ELECTION_INTERVAL = <span class=\"number\">500</span></div><div class=\"line\">)</div></pre></td></tr></table></figure>\n<h3 id=\"创建必要的结构体\"><a href=\"#创建必要的结构体\" class=\"headerlink\" title=\"创建必要的结构体\"></a>创建必要的结构体</h3><p>raft相当于server中的state machine<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">type</span> Raft <span class=\"keyword\">struct</span> {</div><div class=\"line\">\tmu sync.Mutex <span class=\"comment\">// Lock to protect shared access to this peer's state</span></div><div class=\"line\">\tpeers []*labrpc.ClientEnd <span class=\"comment\">// RPC end points of all peers</span></div><div class=\"line\">\tpersister *Persister <span class=\"comment\">// Object to hold this peer's persisted state</span></div><div class=\"line\">\tme <span class=\"keyword\">int</span> <span class=\"comment\">// this peer's index into peers[]</span></div><div class=\"line\"></div><div class=\"line\">\t<span class=\"comment\">// Your data here (2A, 2B, 2C).</span></div><div class=\"line\">\t<span class=\"comment\">// Look at the paper's Figure 2 for a description of what</span></div><div class=\"line\">\t<span class=\"comment\">// state a Raft server must maintain.</span></div><div class=\"line\"></div><div class=\"line\">\tvotedFor <span class=\"keyword\">int</span> <span class=\"comment\">//投票给某个CANDIDATE id</span></div><div class=\"line\">\tvoteAcquired <span class=\"keyword\">int</span> <span class=\"comment\">//收到的票数总数,用于判断能否成为LEADER</span></div><div class=\"line\">\tstate <span class=\"keyword\">int32</span> <span class=\"comment\">//server当前的状态</span></div><div class=\"line\">\tcurrentTerm <span class=\"keyword\">int32</span> <span class=\"comment\">//server当前的term</span></div><div class=\"line\"></div><div class=\"line\">\tvoteCh <span class=\"keyword\">chan</span> <span class=\"keyword\">struct</span>{} <span class=\"comment\">//由于是并行发送用于指示server收到VoteRequest后的操作</span></div><div class=\"line\">\tappendCh <span class=\"keyword\">chan</span> <span class=\"keyword\">struct</span>{} <span class=\"comment\">//收到Append Entries后的操作</span></div><div class=\"line\"></div><div class=\"line\">\telectionTimer *time.Timer <span class=\"comment\">//用于FOLLOWER和CANDIDATE的操作</span></div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<p>发送<code>RequestVote RPC</code>包中的内容<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">type</span> RequestVoteArgs <span class=\"keyword\">struct</span> {</div><div class=\"line\">\tTerm <span class=\"keyword\">int32</span></div><div class=\"line\">\tCandidateId <span class=\"keyword\">int</span></div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<p>server对于<code>RequestVote RPC</code>包的回复<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">type</span> RequestVoteReply <span class=\"keyword\">struct</span> {</div><div class=\"line\">\t<span class=\"comment\">// Your data here (2A).</span></div><div class=\"line\">\tTerm <span class=\"keyword\">int32</span></div><div class=\"line\">\tVoteGranted <span class=\"keyword\">bool</span></div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<p>发送<code>AppendEntries RPC</code>包中的内容<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"comment\">//此处只涉及选举,不考虑replication及log中的内容</span></div><div class=\"line\"><span class=\"keyword\">type</span> AppendEntriesArgs <span class=\"keyword\">struct</span> {</div><div class=\"line\">\tTerm <span class=\"keyword\">int32</span></div><div class=\"line\">\tLeaderID <span class=\"keyword\">int</span></div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<p>接收<code>AppendEntries RPC</code>包,并回复<br>同样这里不考虑CANDIDATE中的log是否是最新的,只要有合适的<code>VoteReuest PRC</code>就投票<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">type</span> AppendEntriesReply <span class=\"keyword\">struct</span> {</div><div class=\"line\">\tTerm <span class=\"keyword\">int32</span></div><div class=\"line\">\tSuccess <span class=\"keyword\">bool</span></div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<p>###一些重要的工具函数</p>\n<figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">getTerm</span><span class=\"params\">()</span> <span class=\"title\">int32</span></span> {</div><div class=\"line\">\t<span class=\"keyword\">return</span> atomic.LoadInt32(&rf.currentTerm)</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">incrementTerm</span><span class=\"params\">()</span> <span class=\"title\">int32</span></span> {</div><div class=\"line\">\t<span class=\"keyword\">return</span> atomic.AddInt32(&rf.currentTerm, <span class=\"number\">1</span>)</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">isState</span><span class=\"params\">(state <span class=\"keyword\">int32</span>)</span> <span class=\"title\">bool</span></span> {</div><div class=\"line\">\t<span class=\"keyword\">return</span> atomic.LoadInt32(&rf.state) == state</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">GetState</span><span class=\"params\">()</span> <span class=\"params\">(<span class=\"keyword\">int</span>, <span class=\"keyword\">bool</span>)</span></span> {</div><div class=\"line\"></div><div class=\"line\">\t<span class=\"keyword\">var</span> term <span class=\"keyword\">int</span></div><div class=\"line\">\t<span class=\"keyword\">var</span> isleader <span class=\"keyword\">bool</span></div><div class=\"line\">\t<span class=\"comment\">// Your code here (2A).</span></div><div class=\"line\">\tterm = <span class=\"keyword\">int</span>(rf.getTerm())</div><div class=\"line\">\tisleader = rf.isState(LEADER)</div><div class=\"line\">\t<span class=\"keyword\">return</span> term, isleader</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">randElectionDuration</span><span class=\"params\">()</span> <span class=\"title\">time</span>.<span class=\"title\">Duration</span></span> {</div><div class=\"line\">\tr := rand.New(rand.NewSource(time.Now().UnixNano()))</div><div class=\"line\">\t<span class=\"keyword\">return</span> time.Millisecond * time.Duration(</div><div class=\"line\"> \t\t\tr.Int63n(MAX_ELECTION_INTERVAL-MIN_ELECTION_INTERVAL)+MIN_ELECTION_INTERVAL</div><div class=\"line\"> \t)</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h3 id=\"CANDIDATE发送RequestVote-RPC包\"><a href=\"#CANDIDATE发送RequestVote-RPC包\" class=\"headerlink\" title=\"CANDIDATE发送RequestVote RPC包\"></a>CANDIDATE发送<code>RequestVote RPC</code>包</h3><p>用于CANDIDATE发送包和对reply的处理</p>\n<ul>\n<li>遍历每一个server,若投票了则voteGranted自增1</li>\n<li>若发现reply中的term大于自身当前的term,则转变为FOLLOWER</li>\n</ul>\n<figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">broadcastVoteReq</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\targs := RequestVoteArgs{Term: atomic.LoadInt32(&rf.currentTerm), CandidateId: rf.me}</div><div class=\"line\">\t<span class=\"keyword\">for</span> i, _ := <span class=\"keyword\">range</span> rf.peers {</div><div class=\"line\">\t\t<span class=\"keyword\">if</span> i == rf.me {</div><div class=\"line\">\t\t\t<span class=\"keyword\">continue</span></div><div class=\"line\">\t\t}</div><div class=\"line\">\t\t<span class=\"comment\">// is it is candidate then send vote req</span></div><div class=\"line\">\t\t<span class=\"keyword\">go</span> <span class=\"function\"><span class=\"keyword\">func</span><span class=\"params\">(server <span class=\"keyword\">int</span>)</span></span> {</div><div class=\"line\">\t\t\t<span class=\"keyword\">var</span> reply RequestVoteReply</div><div class=\"line\">\t\t\t<span class=\"keyword\">if</span> rf.isState(CANDIDATE) && rf.sendRequestVote(server, &args, &reply) {</div><div class=\"line\">\t\t\t\trf.mu.Lock()</div><div class=\"line\">\t\t\t\t<span class=\"keyword\">defer</span> rf.mu.Unlock()</div><div class=\"line\">\t\t\t\t<span class=\"comment\">//deal with the reply</span></div><div class=\"line\">\t\t\t\t<span class=\"keyword\">if</span> reply.VoteGranted {</div><div class=\"line\">\t\t\t\t\trf.voteAcquired += <span class=\"number\">1</span></div><div class=\"line\">\t\t\t\t} <span class=\"keyword\">else</span> {</div><div class=\"line\">\t\t\t\t\t<span class=\"keyword\">if</span> reply.Term > rf.currentTerm {</div><div class=\"line\">\t\t\t\t\t\trf.currentTerm = reply.Term</div><div class=\"line\">\t\t\t\t\t\trf.updateStateTo(FOLLOWER)</div><div class=\"line\">\t\t\t\t\t}</div><div class=\"line\">\t\t\t\t}</div><div class=\"line\">\t\t\t} <span class=\"keyword\">else</span> {</div><div class=\"line\">\t\t\t\tfmt.Printf(<span class=\"string\">\"Server %d send vote req failed.\\n\"</span>, rf.me)</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t}(i)</div><div class=\"line\">\t}</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h3 id=\"server对RequestVote-RPC的处理\"><a href=\"#server对RequestVote-RPC的处理\" class=\"headerlink\" title=\"server对RequestVote RPC的处理\"></a>server对<code>RequestVote RPC</code>的处理</h3><p>依据论文Figure 2中的要求,有以下几个处理的要点:</p>\n<ul>\n<li>判断自身的term是否大于CANDIDATE的term,若是则拒绝投票,并更新reply中的term</li>\n<li>若当前的term小于CANDIDATE发送的term,更新自身的term,并将自己的状态变为follower(当前状态为CANDIDATE或LEADER)</li>\n<li>当两者的term相等时,必须是初始状态,否则拒绝投票(FOLLOWER转变为CANDIDATE时候term自增1,因此必须大于其他server)</li>\n<li>投票成功后,利用信道通知主循环(整个操作是并行的)</li>\n</ul>\n<figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">RequestVote</span><span class=\"params\">(args *RequestVoteArgs, reply *RequestVoteReply)</span></span> {</div><div class=\"line\">\t<span class=\"comment\">// Your code here (2A, 2B).</span></div><div class=\"line\">\trf.mu.Lock()</div><div class=\"line\">\t<span class=\"keyword\">defer</span> rf.mu.Unlock()</div><div class=\"line\">\t<span class=\"keyword\">if</span> args.Term < rf.currentTerm {</div><div class=\"line\">\t\treply.VoteGranted = <span class=\"literal\">false</span></div><div class=\"line\">\t\treply.Term = rf.currentTerm</div><div class=\"line\">\t} <span class=\"keyword\">else</span> <span class=\"keyword\">if</span> args.Term > rf.currentTerm {</div><div class=\"line\">\t\trf.currentTerm = args.Term</div><div class=\"line\">\t\trf.updateStateTo(FOLLOWER)</div><div class=\"line\">\t\trf.votedFor = args.CandidateId</div><div class=\"line\">\t\treply.VoteGranted = <span class=\"literal\">true</span></div><div class=\"line\">\t} <span class=\"keyword\">else</span> {</div><div class=\"line\">\t\t<span class=\"keyword\">if</span> rf.votedFor == <span class=\"number\">-1</span> {</div><div class=\"line\">\t\t\trf.votedFor = args.CandidateId</div><div class=\"line\">\t\t\treply.VoteGranted = <span class=\"literal\">true</span></div><div class=\"line\">\t\t} <span class=\"keyword\">else</span> {</div><div class=\"line\">\t\t\treply.VoteGranted = <span class=\"literal\">false</span></div><div class=\"line\">\t\t}</div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"keyword\">if</span> reply.VoteGranted == <span class=\"literal\">true</span> {</div><div class=\"line\">\t\t<span class=\"keyword\">go</span> <span class=\"function\"><span class=\"keyword\">func</span><span class=\"params\">()</span></span> { rf.voteCh <- <span class=\"keyword\">struct</span>{}{} }()</div><div class=\"line\">\t}</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h3 id=\"LEADER广播AppendEntries-RPC,和对reply的处理\"><a href=\"#LEADER广播AppendEntries-RPC,和对reply的处理\" class=\"headerlink\" title=\"LEADER广播AppendEntries RPC,和对reply的处理\"></a>LEADER广播<code>AppendEntries RPC</code>,和对reply的处理</h3><p>由于这里只是单纯的选举所以处理的逻辑很简单,只处理失败的回复:</p>\n<ul>\n<li>如果reply中的term大于当前LEADER中的term,则LEADER将自己的状态变成FOLLOWER</li>\n</ul>\n<figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">broadcastAppendEnrties</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\targs := AppendEntriesArgs{Term: atomic.LoadInt32(&rf.currentTerm), LeaderID: rf.me}</div><div class=\"line\">\t<span class=\"keyword\">for</span> i, _ := <span class=\"keyword\">range</span> rf.peers {</div><div class=\"line\">\t\t<span class=\"keyword\">if</span> i == rf.me {</div><div class=\"line\">\t\t\t<span class=\"keyword\">continue</span></div><div class=\"line\">\t\t}</div><div class=\"line\">\t\t<span class=\"keyword\">go</span> <span class=\"function\"><span class=\"keyword\">func</span><span class=\"params\">(server <span class=\"keyword\">int</span>)</span></span> {</div><div class=\"line\">\t\t\t<span class=\"keyword\">var</span> reply AppendEntriesReply</div><div class=\"line\">\t\t\t<span class=\"keyword\">if</span> rf.isState(LEADER) && rf.sendAppendEntries(server, &args, &reply) {</div><div class=\"line\">\t\t\t\trf.mu.Lock()</div><div class=\"line\">\t\t\t\t<span class=\"keyword\">defer</span> rf.mu.Unlock()</div><div class=\"line\">\t\t\t\t<span class=\"keyword\">if</span> reply.Success {</div><div class=\"line\"></div><div class=\"line\">\t\t\t\t} <span class=\"keyword\">else</span> {</div><div class=\"line\">\t\t\t\t\t<span class=\"keyword\">if</span> reply.Term > rf.currentTerm {</div><div class=\"line\">\t\t\t\t\t\trf.currentTerm = reply.Term</div><div class=\"line\">\t\t\t\t\t\trf.updateStateTo(FOLLOWER)</div><div class=\"line\">\t\t\t\t\t}</div><div class=\"line\">\t\t\t\t}</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t}(i)</div><div class=\"line\">\t}</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h3 id=\"server对AppendEntries-RPC的处理\"><a href=\"#server对AppendEntries-RPC的处理\" class=\"headerlink\" title=\"server对AppendEntries RPC的处理\"></a>server对<code>AppendEntries RPC</code>的处理</h3><p>同样,raft中的term可以作为判断是不是最新的依据,根据Figure 2,server在处理<code>AppendEntries RPC</code>,有如下注意点:</p>\n<ul>\n<li>如果<code>args.Term < rf.currentTerm</code>,<code>reply.Term = rf.currentTerm</code>;</li>\n<li>如果<code>args.Term > rf.currentTerm</code>,更新当前的term并且将状态改变为FOLLOWER(针对当前状态为CANDIDATE的server)</li>\n</ul>\n<figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">AppendEntries</span><span class=\"params\">(args *AppendEntriesArgs, reply *AppendEntriesReply)</span></span> {</div><div class=\"line\">\trf.mu.Lock()</div><div class=\"line\">\t<span class=\"keyword\">defer</span> rf.mu.Unlock()</div><div class=\"line\">\t<span class=\"comment\">//deal with the msg and reply</span></div><div class=\"line\">\t<span class=\"keyword\">if</span> args.Term < rf.currentTerm {</div><div class=\"line\">\t\treply.Success = <span class=\"literal\">false</span></div><div class=\"line\">\t\treply.Term = rf.currentTerm</div><div class=\"line\">\t} <span class=\"keyword\">else</span> <span class=\"keyword\">if</span> args.Term > rf.currentTerm {</div><div class=\"line\">\t\trf.currentTerm = args.Term</div><div class=\"line\">\t\trf.updateStateTo(FOLLOWER)</div><div class=\"line\">\t\treply.Success = <span class=\"literal\">true</span></div><div class=\"line\">\t} <span class=\"keyword\">else</span> {</div><div class=\"line\">\t\treply.Success = <span class=\"literal\">true</span></div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"keyword\">go</span> <span class=\"function\"><span class=\"keyword\">func</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\t\trf.appendCh <- <span class=\"keyword\">struct</span>{}{}</div><div class=\"line\">\t}()</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h3 id=\"状态转变的函数\"><a href=\"#状态转变的函数\" class=\"headerlink\" title=\"状态转变的函数\"></a>状态转变的函数</h3><p>需要注意的是当转变成FOLLOWER时,要将自己的<code>VoteFor</code>初始化为-1,当转变成CANDIDATE时候要注意开始选举LEADER<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">updateStateTo</span><span class=\"params\">(state <span class=\"keyword\">int32</span>)</span></span> {</div><div class=\"line\">\t<span class=\"keyword\">if</span> rf.isState(state) {</div><div class=\"line\">\t\t<span class=\"keyword\">return</span></div><div class=\"line\">\t}</div><div class=\"line\">\tstateDesc := []<span class=\"keyword\">string</span>{<span class=\"string\">\"FOLLOWER\"</span>, <span class=\"string\">\"CANDIDATE\"</span>, <span class=\"string\">\"LEADER\"</span>}</div><div class=\"line\">\tpreState := rf.state</div><div class=\"line\">\t<span class=\"keyword\">switch</span> state {</div><div class=\"line\">\t<span class=\"keyword\">case</span> FOLLOWER:</div><div class=\"line\">\t\trf.state = FOLLOWER</div><div class=\"line\">\t\trf.votedFor = <span class=\"number\">-1</span></div><div class=\"line\">\t<span class=\"keyword\">case</span> CANDIDATE:</div><div class=\"line\">\t\trf.state = CANDIDATE</div><div class=\"line\">\t\trf.startElection()</div><div class=\"line\">\t<span class=\"keyword\">case</span> LEADER:</div><div class=\"line\">\t\trf.state = LEADER</div><div class=\"line\">\t<span class=\"keyword\">default</span>:</div><div class=\"line\">\t\tfmt.Printf(<span class=\"string\">\"Warning: invalid state %d, do nothing.\\n\"</span>, state)</div><div class=\"line\">\t}</div><div class=\"line\">\tfmt.Printf(<span class=\"string\">\"In term %d: Server %d transfer from %s to %s\\n\"</span>,</div><div class=\"line\">\t\trf.currentTerm, rf.me, stateDesc[preState], stateDesc[rf.state])</div><div class=\"line\"></div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<h3 id=\"开始新一轮选举的函数\"><a href=\"#开始新一轮选举的函数\" class=\"headerlink\" title=\"开始新一轮选举的函数\"></a>开始新一轮选举的函数</h3><p>按照论文的要求在开始选举新的LEADER的时候要重置计时器,因为CANDIDATE会为自己投票所以票数初始置为1<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">startElection</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\trf.incrementTerm() <span class=\"comment\">//first increase current term</span></div><div class=\"line\">\trf.votedFor = rf.me <span class=\"comment\">//vote for self</span></div><div class=\"line\">\trf.voteAcquired = <span class=\"number\">1</span> <span class=\"comment\">//acquire self's vote</span></div><div class=\"line\">\trf.electionTimer.Reset(randElectionDuration())</div><div class=\"line\">\trf.broadcastVoteReq()</div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<h4 id=\"整个状态的循环实现\"><a href=\"#整个状态的循环实现\" class=\"headerlink\" title=\"整个状态的循环实现\"></a>整个状态的循环实现</h4><p>不断的循环,并根据当前server的角色进行不同的操作</p>\n<h4 id=\"FOLLOWER\"><a href=\"#FOLLOWER\" class=\"headerlink\" title=\"FOLLOWER\"></a>FOLLOWER</h4><ul>\n<li>当收到CANDIDATE发送的<code>VoteRequest RPC</code>时,重置计时器</li>\n<li>当收到LEADER发送的<code>AppendEntries RPC</code>时,重置计时器</li>\n<li>当计时器结束时,说明当前的server断绝联系,于是FOLLOWER转变为CANDIDATE开始新一轮的选举</li>\n</ul>\n<h4 id=\"CANDIDATE\"><a href=\"#CANDIDATE\" class=\"headerlink\" title=\"CANDIDATE\"></a>CANDIDATE</h4><ul>\n<li>在收到<code>AppendEntries RPC</code>时,说明LEADER依旧存在,停止选举转变为FOLLOWER</li>\n<li>当计时器结束时,重新开始选举</li>\n<li>当投票数大于总数的一半时,当选LEADER</li>\n</ul>\n<h4 id=\"LEADER\"><a href=\"#LEADER\" class=\"headerlink\" title=\"LEADER\"></a>LEADER</h4><ul>\n<li>间隔一段时间发送<code>HeartBeat</code>心跳包维持LEADER和其他的server的通信</li>\n</ul>\n<figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div><div class=\"line\">29</div><div class=\"line\">30</div><div class=\"line\">31</div><div class=\"line\">32</div><div class=\"line\">33</div><div class=\"line\">34</div><div class=\"line\">35</div><div class=\"line\">36</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">startLoop</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\trf.electionTimer = time.NewTimer(randElectionDuration())</div><div class=\"line\">\t<span class=\"keyword\">for</span> {</div><div class=\"line\">\t\t<span class=\"keyword\">switch</span> atomic.LoadInt32(&rf.state) {</div><div class=\"line\">\t\t<span class=\"keyword\">case</span> FOLLOWER:</div><div class=\"line\">\t\t\t<span class=\"keyword\">select</span> {</div><div class=\"line\">\t\t\t<span class=\"keyword\">case</span> <-rf.voteCh:</div><div class=\"line\">\t\t\t\trf.electionTimer.Reset(randElectionDuration())</div><div class=\"line\">\t\t\t<span class=\"keyword\">case</span> <-rf.appendCh:</div><div class=\"line\">\t\t\t\trf.electionTimer.Reset(randElectionDuration())</div><div class=\"line\">\t\t\t<span class=\"keyword\">case</span> <-rf.electionTimer.C:</div><div class=\"line\">\t\t\t\t<span class=\"comment\">//time out</span></div><div class=\"line\">\t\t\t\trf.mu.Lock()</div><div class=\"line\">\t\t\t\trf.updateStateTo(CANDIDATE)</div><div class=\"line\">\t\t\t\trf.mu.Unlock()</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t<span class=\"keyword\">case</span> CANDIDATE:</div><div class=\"line\">\t\t\trf.mu.Lock()</div><div class=\"line\">\t\t\t<span class=\"keyword\">select</span> {</div><div class=\"line\">\t\t\t<span class=\"keyword\">case</span> <-rf.appendCh:</div><div class=\"line\">\t\t\t\trf.updateStateTo(FOLLOWER)</div><div class=\"line\">\t\t\t<span class=\"keyword\">case</span> <-rf.electionTimer.C:</div><div class=\"line\">\t\t\t\trf.electionTimer.Reset(randElectionDuration())</div><div class=\"line\">\t\t\t\trf.startElection()</div><div class=\"line\">\t\t\t<span class=\"keyword\">default</span>:</div><div class=\"line\">\t\t\t\t<span class=\"keyword\">if</span> rf.voteAcquired > <span class=\"built_in\">len</span>(rf.peers)/<span class=\"number\">2</span> {</div><div class=\"line\">\t\t\t\t\trf.updateStateTo(LEADER)</div><div class=\"line\">\t\t\t\t}</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t\trf.mu.Unlock()</div><div class=\"line\">\t\t<span class=\"keyword\">case</span> LEADER:</div><div class=\"line\">\t\t\trf.broadcastAppendEnrties()</div><div class=\"line\">\t\t\ttime.Sleep(HEARTBEAT_INTERVAL * time.Millisecond)</div><div class=\"line\">\t\t}</div><div class=\"line\"></div><div class=\"line\">\t}</div></pre></td></tr></table></figure>\n<h3 id=\"初始化并开始循环\"><a href=\"#初始化并开始循环\" class=\"headerlink\" title=\"初始化并开始循环\"></a>初始化并开始循环</h3><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">Make</span><span class=\"params\">(peers []*labrpc.ClientEnd, me <span class=\"keyword\">int</span>,</span></span></div><div class=\"line\"><span class=\"function\"><span class=\"params\">\tpersister *Persister, applyCh <span class=\"keyword\">chan</span> ApplyMsg)</span> *<span class=\"title\">Raft</span></span> {</div><div class=\"line\">\trf := &Raft{}</div><div class=\"line\">\trf.peers = peers</div><div class=\"line\">\trf.persister = persister</div><div class=\"line\">\trf.me = me</div><div class=\"line\"></div><div class=\"line\">\t<span class=\"comment\">// Your initialization code here (2A, 2B, 2C).</span></div><div class=\"line\">\trf.state = FOLLOWER</div><div class=\"line\">\trf.votedFor = <span class=\"number\">-1</span></div><div class=\"line\">\trf.voteCh = <span class=\"built_in\">make</span>(<span class=\"keyword\">chan</span> <span class=\"keyword\">struct</span>{})</div><div class=\"line\">\trf.appendCh = <span class=\"built_in\">make</span>(<span class=\"keyword\">chan</span> <span class=\"keyword\">struct</span>{})</div><div class=\"line\"></div><div class=\"line\">\t<span class=\"comment\">// initialize from state persisted before a crash</span></div><div class=\"line\">\trf.readPersist(persister.ReadRaftState())</div><div class=\"line\"></div><div class=\"line\">\t<span class=\"keyword\">go</span> rf.startLoop()</div><div class=\"line\"></div><div class=\"line\">\t<span class=\"keyword\">return</span> rf</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h2 id=\"总结\"><a href=\"#总结\" class=\"headerlink\" title=\"总结\"></a>总结</h2><hr>\n<p>第一次将论文中的算法“实现”,虽然是参考了大神的代码,还是觉得收获满满。<br>首先,纠正了我看论文一目十行的方法,以前总觉得论文这东西太虚,只是所谓的想法,经过这次无数遍的研读之后,发现论文中的每一句话对于整个算法的执行都有至关重要的作用。因此,以后在看论文的时候,需要逐句理解,最好能够抽象出整个模型,并且思考论文这么做对于整个系统的实现有什么好处<br>其次,自己的代码的能力实在是弱的不行,我能看得懂这些代码,并能在脑中把每一个函数链接起来,但是距离完全靠自己的能力去实现一个这样的系统还有相当长的路要走。<br>希望能坚持将自己所看的论文、方法都能用自己的话去说出来,加深下理解。</p>\n","site":{"data":{}},"excerpt":"","more":"<p>本次的实验为MIT6.824 Distribute System 中 Lab2 raft part A,通过课程所给的代码的骨架,要求实验raft中选举部分的功能。由于我的水平较渣,觉得课程的难度较大,与其说是实验比如说是对Github上的大神的代码进行理解,并结合论文进行思考。希望有朝一日能够自己做出一个实验啊!</p>\n<blockquote>\n<p>Implement leader election and heartbeats (<code>AppendEntries</code> RPCs with no log entries). The goal for Part 2A is for a single leader to be elected, for the leader to remain the leader if there are no failures, and for a new leader to take over if the old leader fails or if packets to/from the old leader are lost. Run <code>go test -run 2A</code> to test your 2A code.</p>\n</blockquote>\n<p>Part A 部分要求实现raft的选举功能,其中raft中的每一个server都会维护自己的一个raft的结构体,其中只有两种RPC:<code>AppendEntries RPC</code> 和 <code>RequestVote RPC</code>。其中当<code>AppendEntres RPC</code>中的log为空时用作<code>HeartBeat</code>维护server 和 follower之间的通信,而<code>RequestVote RPC</code>则用作当follower转变成candidate时,向其他的server请求投票时的通信。整个选举的过程可以概括为下面的这张图,而其中又有许多的细节需要结合论文中的Figure 2中的每一个条件实现;</p>\n<img src=\"/2017/10/22/Raft-PartA/election.png\" alt=\"election\" title=\"election\">\n<h2 id=\"可以概括为如下几个步骤\"><a href=\"#可以概括为如下几个步骤\" class=\"headerlink\" title=\"可以概括为如下几个步骤\"></a>可以概括为如下几个步骤</h2><ol>\n<li>系统初始化时,所有的server都是FLLOWER状态,并维护一个计时器,计时结束会成为CANDIDATE并开始选举;FOLLOWER每当收到一次<code>Herat Beat</code>或<code>RequestVote RPC</code>时重置计时器;</li>\n<li>CANDIDATE在选举的时候依旧会维护一个计时器向所有的SERVER发送<code>Request Vote</code>,在结束时仍未选举完,重新开始选举。当CANDIDATE获得大部分的投票后变为LEADER;在选举的过程中若收到来自更新的term的<code>AppendEntries RPC</code>或者自己的term小于其他server的term时,转变为FOLLOWER;</li>\n<li>LEADER开始广播<code>Heart Beat</code>包,广播自己成为LEADER的事实,并且维护和FOLLOWER之间的通信。当发现FOLLOWER的reply中有更高的term时转变为FOLLOWER;</li>\n</ol>\n<h2 id=\"代码解析\"><a href=\"#代码解析\" class=\"headerlink\" title=\"代码解析\"></a>代码解析</h2><h3 id=\"定义一些常量\"><a href=\"#定义一些常量\" class=\"headerlink\" title=\"定义一些常量\"></a>定义一些常量</h3><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">const</span> (</div><div class=\"line\">\tFOLLOWER = <span class=\"literal\">iota</span></div><div class=\"line\">\tCANDIDATE</div><div class=\"line\">\tLEADER</div><div class=\"line\"></div><div class=\"line\">\tHEARTBEAT_INTERVAL = <span class=\"number\">100</span></div><div class=\"line\">\tMIN_ELECTION_INTERVAL = <span class=\"number\">400</span></div><div class=\"line\">\tMAX_ELECTION_INTERVAL = <span class=\"number\">500</span></div><div class=\"line\">)</div></pre></td></tr></table></figure>\n<h3 id=\"创建必要的结构体\"><a href=\"#创建必要的结构体\" class=\"headerlink\" title=\"创建必要的结构体\"></a>创建必要的结构体</h3><p>raft相当于server中的state machine<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">type</span> Raft <span class=\"keyword\">struct</span> {</div><div class=\"line\">\tmu sync.Mutex <span class=\"comment\">// Lock to protect shared access to this peer's state</span></div><div class=\"line\">\tpeers []*labrpc.ClientEnd <span class=\"comment\">// RPC end points of all peers</span></div><div class=\"line\">\tpersister *Persister <span class=\"comment\">// Object to hold this peer's persisted state</span></div><div class=\"line\">\tme <span class=\"keyword\">int</span> <span class=\"comment\">// this peer's index into peers[]</span></div><div class=\"line\"></div><div class=\"line\">\t<span class=\"comment\">// Your data here (2A, 2B, 2C).</span></div><div class=\"line\">\t<span class=\"comment\">// Look at the paper's Figure 2 for a description of what</span></div><div class=\"line\">\t<span class=\"comment\">// state a Raft server must maintain.</span></div><div class=\"line\"></div><div class=\"line\">\tvotedFor <span class=\"keyword\">int</span> <span class=\"comment\">//投票给某个CANDIDATE id</span></div><div class=\"line\">\tvoteAcquired <span class=\"keyword\">int</span> <span class=\"comment\">//收到的票数总数,用于判断能否成为LEADER</span></div><div class=\"line\">\tstate <span class=\"keyword\">int32</span> <span class=\"comment\">//server当前的状态</span></div><div class=\"line\">\tcurrentTerm <span class=\"keyword\">int32</span> <span class=\"comment\">//server当前的term</span></div><div class=\"line\"></div><div class=\"line\">\tvoteCh <span class=\"keyword\">chan</span> <span class=\"keyword\">struct</span>{} <span class=\"comment\">//由于是并行发送用于指示server收到VoteRequest后的操作</span></div><div class=\"line\">\tappendCh <span class=\"keyword\">chan</span> <span class=\"keyword\">struct</span>{} <span class=\"comment\">//收到Append Entries后的操作</span></div><div class=\"line\"></div><div class=\"line\">\telectionTimer *time.Timer <span class=\"comment\">//用于FOLLOWER和CANDIDATE的操作</span></div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<p>发送<code>RequestVote RPC</code>包中的内容<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">type</span> RequestVoteArgs <span class=\"keyword\">struct</span> {</div><div class=\"line\">\tTerm <span class=\"keyword\">int32</span></div><div class=\"line\">\tCandidateId <span class=\"keyword\">int</span></div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<p>server对于<code>RequestVote RPC</code>包的回复<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">type</span> RequestVoteReply <span class=\"keyword\">struct</span> {</div><div class=\"line\">\t<span class=\"comment\">// Your data here (2A).</span></div><div class=\"line\">\tTerm <span class=\"keyword\">int32</span></div><div class=\"line\">\tVoteGranted <span class=\"keyword\">bool</span></div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<p>发送<code>AppendEntries RPC</code>包中的内容<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"comment\">//此处只涉及选举,不考虑replication及log中的内容</span></div><div class=\"line\"><span class=\"keyword\">type</span> AppendEntriesArgs <span class=\"keyword\">struct</span> {</div><div class=\"line\">\tTerm <span class=\"keyword\">int32</span></div><div class=\"line\">\tLeaderID <span class=\"keyword\">int</span></div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<p>接收<code>AppendEntries RPC</code>包,并回复<br>同样这里不考虑CANDIDATE中的log是否是最新的,只要有合适的<code>VoteReuest PRC</code>就投票<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">type</span> AppendEntriesReply <span class=\"keyword\">struct</span> {</div><div class=\"line\">\tTerm <span class=\"keyword\">int32</span></div><div class=\"line\">\tSuccess <span class=\"keyword\">bool</span></div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<p>###一些重要的工具函数</p>\n<figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">getTerm</span><span class=\"params\">()</span> <span class=\"title\">int32</span></span> {</div><div class=\"line\">\t<span class=\"keyword\">return</span> atomic.LoadInt32(&rf.currentTerm)</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">incrementTerm</span><span class=\"params\">()</span> <span class=\"title\">int32</span></span> {</div><div class=\"line\">\t<span class=\"keyword\">return</span> atomic.AddInt32(&rf.currentTerm, <span class=\"number\">1</span>)</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">isState</span><span class=\"params\">(state <span class=\"keyword\">int32</span>)</span> <span class=\"title\">bool</span></span> {</div><div class=\"line\">\t<span class=\"keyword\">return</span> atomic.LoadInt32(&rf.state) == state</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">GetState</span><span class=\"params\">()</span> <span class=\"params\">(<span class=\"keyword\">int</span>, <span class=\"keyword\">bool</span>)</span></span> {</div><div class=\"line\"></div><div class=\"line\">\t<span class=\"keyword\">var</span> term <span class=\"keyword\">int</span></div><div class=\"line\">\t<span class=\"keyword\">var</span> isleader <span class=\"keyword\">bool</span></div><div class=\"line\">\t<span class=\"comment\">// Your code here (2A).</span></div><div class=\"line\">\tterm = <span class=\"keyword\">int</span>(rf.getTerm())</div><div class=\"line\">\tisleader = rf.isState(LEADER)</div><div class=\"line\">\t<span class=\"keyword\">return</span> term, isleader</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">randElectionDuration</span><span class=\"params\">()</span> <span class=\"title\">time</span>.<span class=\"title\">Duration</span></span> {</div><div class=\"line\">\tr := rand.New(rand.NewSource(time.Now().UnixNano()))</div><div class=\"line\">\t<span class=\"keyword\">return</span> time.Millisecond * time.Duration(</div><div class=\"line\"> \t\t\tr.Int63n(MAX_ELECTION_INTERVAL-MIN_ELECTION_INTERVAL)+MIN_ELECTION_INTERVAL</div><div class=\"line\"> \t)</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h3 id=\"CANDIDATE发送RequestVote-RPC包\"><a href=\"#CANDIDATE发送RequestVote-RPC包\" class=\"headerlink\" title=\"CANDIDATE发送RequestVote RPC包\"></a>CANDIDATE发送<code>RequestVote RPC</code>包</h3><p>用于CANDIDATE发送包和对reply的处理</p>\n<ul>\n<li>遍历每一个server,若投票了则voteGranted自增1</li>\n<li>若发现reply中的term大于自身当前的term,则转变为FOLLOWER</li>\n</ul>\n<figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">broadcastVoteReq</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\targs := RequestVoteArgs{Term: atomic.LoadInt32(&rf.currentTerm), CandidateId: rf.me}</div><div class=\"line\">\t<span class=\"keyword\">for</span> i, _ := <span class=\"keyword\">range</span> rf.peers {</div><div class=\"line\">\t\t<span class=\"keyword\">if</span> i == rf.me {</div><div class=\"line\">\t\t\t<span class=\"keyword\">continue</span></div><div class=\"line\">\t\t}</div><div class=\"line\">\t\t<span class=\"comment\">// is it is candidate then send vote req</span></div><div class=\"line\">\t\t<span class=\"keyword\">go</span> <span class=\"function\"><span class=\"keyword\">func</span><span class=\"params\">(server <span class=\"keyword\">int</span>)</span></span> {</div><div class=\"line\">\t\t\t<span class=\"keyword\">var</span> reply RequestVoteReply</div><div class=\"line\">\t\t\t<span class=\"keyword\">if</span> rf.isState(CANDIDATE) && rf.sendRequestVote(server, &args, &reply) {</div><div class=\"line\">\t\t\t\trf.mu.Lock()</div><div class=\"line\">\t\t\t\t<span class=\"keyword\">defer</span> rf.mu.Unlock()</div><div class=\"line\">\t\t\t\t<span class=\"comment\">//deal with the reply</span></div><div class=\"line\">\t\t\t\t<span class=\"keyword\">if</span> reply.VoteGranted {</div><div class=\"line\">\t\t\t\t\trf.voteAcquired += <span class=\"number\">1</span></div><div class=\"line\">\t\t\t\t} <span class=\"keyword\">else</span> {</div><div class=\"line\">\t\t\t\t\t<span class=\"keyword\">if</span> reply.Term > rf.currentTerm {</div><div class=\"line\">\t\t\t\t\t\trf.currentTerm = reply.Term</div><div class=\"line\">\t\t\t\t\t\trf.updateStateTo(FOLLOWER)</div><div class=\"line\">\t\t\t\t\t}</div><div class=\"line\">\t\t\t\t}</div><div class=\"line\">\t\t\t} <span class=\"keyword\">else</span> {</div><div class=\"line\">\t\t\t\tfmt.Printf(<span class=\"string\">\"Server %d send vote req failed.\\n\"</span>, rf.me)</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t}(i)</div><div class=\"line\">\t}</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h3 id=\"server对RequestVote-RPC的处理\"><a href=\"#server对RequestVote-RPC的处理\" class=\"headerlink\" title=\"server对RequestVote RPC的处理\"></a>server对<code>RequestVote RPC</code>的处理</h3><p>依据论文Figure 2中的要求,有以下几个处理的要点:</p>\n<ul>\n<li>判断自身的term是否大于CANDIDATE的term,若是则拒绝投票,并更新reply中的term</li>\n<li>若当前的term小于CANDIDATE发送的term,更新自身的term,并将自己的状态变为follower(当前状态为CANDIDATE或LEADER)</li>\n<li>当两者的term相等时,必须是初始状态,否则拒绝投票(FOLLOWER转变为CANDIDATE时候term自增1,因此必须大于其他server)</li>\n<li>投票成功后,利用信道通知主循环(整个操作是并行的)</li>\n</ul>\n<figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">RequestVote</span><span class=\"params\">(args *RequestVoteArgs, reply *RequestVoteReply)</span></span> {</div><div class=\"line\">\t<span class=\"comment\">// Your code here (2A, 2B).</span></div><div class=\"line\">\trf.mu.Lock()</div><div class=\"line\">\t<span class=\"keyword\">defer</span> rf.mu.Unlock()</div><div class=\"line\">\t<span class=\"keyword\">if</span> args.Term < rf.currentTerm {</div><div class=\"line\">\t\treply.VoteGranted = <span class=\"literal\">false</span></div><div class=\"line\">\t\treply.Term = rf.currentTerm</div><div class=\"line\">\t} <span class=\"keyword\">else</span> <span class=\"keyword\">if</span> args.Term > rf.currentTerm {</div><div class=\"line\">\t\trf.currentTerm = args.Term</div><div class=\"line\">\t\trf.updateStateTo(FOLLOWER)</div><div class=\"line\">\t\trf.votedFor = args.CandidateId</div><div class=\"line\">\t\treply.VoteGranted = <span class=\"literal\">true</span></div><div class=\"line\">\t} <span class=\"keyword\">else</span> {</div><div class=\"line\">\t\t<span class=\"keyword\">if</span> rf.votedFor == <span class=\"number\">-1</span> {</div><div class=\"line\">\t\t\trf.votedFor = args.CandidateId</div><div class=\"line\">\t\t\treply.VoteGranted = <span class=\"literal\">true</span></div><div class=\"line\">\t\t} <span class=\"keyword\">else</span> {</div><div class=\"line\">\t\t\treply.VoteGranted = <span class=\"literal\">false</span></div><div class=\"line\">\t\t}</div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"keyword\">if</span> reply.VoteGranted == <span class=\"literal\">true</span> {</div><div class=\"line\">\t\t<span class=\"keyword\">go</span> <span class=\"function\"><span class=\"keyword\">func</span><span class=\"params\">()</span></span> { rf.voteCh <- <span class=\"keyword\">struct</span>{}{} }()</div><div class=\"line\">\t}</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h3 id=\"LEADER广播AppendEntries-RPC,和对reply的处理\"><a href=\"#LEADER广播AppendEntries-RPC,和对reply的处理\" class=\"headerlink\" title=\"LEADER广播AppendEntries RPC,和对reply的处理\"></a>LEADER广播<code>AppendEntries RPC</code>,和对reply的处理</h3><p>由于这里只是单纯的选举所以处理的逻辑很简单,只处理失败的回复:</p>\n<ul>\n<li>如果reply中的term大于当前LEADER中的term,则LEADER将自己的状态变成FOLLOWER</li>\n</ul>\n<figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">broadcastAppendEnrties</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\targs := AppendEntriesArgs{Term: atomic.LoadInt32(&rf.currentTerm), LeaderID: rf.me}</div><div class=\"line\">\t<span class=\"keyword\">for</span> i, _ := <span class=\"keyword\">range</span> rf.peers {</div><div class=\"line\">\t\t<span class=\"keyword\">if</span> i == rf.me {</div><div class=\"line\">\t\t\t<span class=\"keyword\">continue</span></div><div class=\"line\">\t\t}</div><div class=\"line\">\t\t<span class=\"keyword\">go</span> <span class=\"function\"><span class=\"keyword\">func</span><span class=\"params\">(server <span class=\"keyword\">int</span>)</span></span> {</div><div class=\"line\">\t\t\t<span class=\"keyword\">var</span> reply AppendEntriesReply</div><div class=\"line\">\t\t\t<span class=\"keyword\">if</span> rf.isState(LEADER) && rf.sendAppendEntries(server, &args, &reply) {</div><div class=\"line\">\t\t\t\trf.mu.Lock()</div><div class=\"line\">\t\t\t\t<span class=\"keyword\">defer</span> rf.mu.Unlock()</div><div class=\"line\">\t\t\t\t<span class=\"keyword\">if</span> reply.Success {</div><div class=\"line\"></div><div class=\"line\">\t\t\t\t} <span class=\"keyword\">else</span> {</div><div class=\"line\">\t\t\t\t\t<span class=\"keyword\">if</span> reply.Term > rf.currentTerm {</div><div class=\"line\">\t\t\t\t\t\trf.currentTerm = reply.Term</div><div class=\"line\">\t\t\t\t\t\trf.updateStateTo(FOLLOWER)</div><div class=\"line\">\t\t\t\t\t}</div><div class=\"line\">\t\t\t\t}</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t}(i)</div><div class=\"line\">\t}</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h3 id=\"server对AppendEntries-RPC的处理\"><a href=\"#server对AppendEntries-RPC的处理\" class=\"headerlink\" title=\"server对AppendEntries RPC的处理\"></a>server对<code>AppendEntries RPC</code>的处理</h3><p>同样,raft中的term可以作为判断是不是最新的依据,根据Figure 2,server在处理<code>AppendEntries RPC</code>,有如下注意点:</p>\n<ul>\n<li>如果<code>args.Term < rf.currentTerm</code>,<code>reply.Term = rf.currentTerm</code>;</li>\n<li>如果<code>args.Term > rf.currentTerm</code>,更新当前的term并且将状态改变为FOLLOWER(针对当前状态为CANDIDATE的server)</li>\n</ul>\n<figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">AppendEntries</span><span class=\"params\">(args *AppendEntriesArgs, reply *AppendEntriesReply)</span></span> {</div><div class=\"line\">\trf.mu.Lock()</div><div class=\"line\">\t<span class=\"keyword\">defer</span> rf.mu.Unlock()</div><div class=\"line\">\t<span class=\"comment\">//deal with the msg and reply</span></div><div class=\"line\">\t<span class=\"keyword\">if</span> args.Term < rf.currentTerm {</div><div class=\"line\">\t\treply.Success = <span class=\"literal\">false</span></div><div class=\"line\">\t\treply.Term = rf.currentTerm</div><div class=\"line\">\t} <span class=\"keyword\">else</span> <span class=\"keyword\">if</span> args.Term > rf.currentTerm {</div><div class=\"line\">\t\trf.currentTerm = args.Term</div><div class=\"line\">\t\trf.updateStateTo(FOLLOWER)</div><div class=\"line\">\t\treply.Success = <span class=\"literal\">true</span></div><div class=\"line\">\t} <span class=\"keyword\">else</span> {</div><div class=\"line\">\t\treply.Success = <span class=\"literal\">true</span></div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"keyword\">go</span> <span class=\"function\"><span class=\"keyword\">func</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\t\trf.appendCh <- <span class=\"keyword\">struct</span>{}{}</div><div class=\"line\">\t}()</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h3 id=\"状态转变的函数\"><a href=\"#状态转变的函数\" class=\"headerlink\" title=\"状态转变的函数\"></a>状态转变的函数</h3><p>需要注意的是当转变成FOLLOWER时,要将自己的<code>VoteFor</code>初始化为-1,当转变成CANDIDATE时候要注意开始选举LEADER<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">updateStateTo</span><span class=\"params\">(state <span class=\"keyword\">int32</span>)</span></span> {</div><div class=\"line\">\t<span class=\"keyword\">if</span> rf.isState(state) {</div><div class=\"line\">\t\t<span class=\"keyword\">return</span></div><div class=\"line\">\t}</div><div class=\"line\">\tstateDesc := []<span class=\"keyword\">string</span>{<span class=\"string\">\"FOLLOWER\"</span>, <span class=\"string\">\"CANDIDATE\"</span>, <span class=\"string\">\"LEADER\"</span>}</div><div class=\"line\">\tpreState := rf.state</div><div class=\"line\">\t<span class=\"keyword\">switch</span> state {</div><div class=\"line\">\t<span class=\"keyword\">case</span> FOLLOWER:</div><div class=\"line\">\t\trf.state = FOLLOWER</div><div class=\"line\">\t\trf.votedFor = <span class=\"number\">-1</span></div><div class=\"line\">\t<span class=\"keyword\">case</span> CANDIDATE:</div><div class=\"line\">\t\trf.state = CANDIDATE</div><div class=\"line\">\t\trf.startElection()</div><div class=\"line\">\t<span class=\"keyword\">case</span> LEADER:</div><div class=\"line\">\t\trf.state = LEADER</div><div class=\"line\">\t<span class=\"keyword\">default</span>:</div><div class=\"line\">\t\tfmt.Printf(<span class=\"string\">\"Warning: invalid state %d, do nothing.\\n\"</span>, state)</div><div class=\"line\">\t}</div><div class=\"line\">\tfmt.Printf(<span class=\"string\">\"In term %d: Server %d transfer from %s to %s\\n\"</span>,</div><div class=\"line\">\t\trf.currentTerm, rf.me, stateDesc[preState], stateDesc[rf.state])</div><div class=\"line\"></div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<h3 id=\"开始新一轮选举的函数\"><a href=\"#开始新一轮选举的函数\" class=\"headerlink\" title=\"开始新一轮选举的函数\"></a>开始新一轮选举的函数</h3><p>按照论文的要求在开始选举新的LEADER的时候要重置计时器,因为CANDIDATE会为自己投票所以票数初始置为1<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">startElection</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\trf.incrementTerm() <span class=\"comment\">//first increase current term</span></div><div class=\"line\">\trf.votedFor = rf.me <span class=\"comment\">//vote for self</span></div><div class=\"line\">\trf.voteAcquired = <span class=\"number\">1</span> <span class=\"comment\">//acquire self's vote</span></div><div class=\"line\">\trf.electionTimer.Reset(randElectionDuration())</div><div class=\"line\">\trf.broadcastVoteReq()</div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<h4 id=\"整个状态的循环实现\"><a href=\"#整个状态的循环实现\" class=\"headerlink\" title=\"整个状态的循环实现\"></a>整个状态的循环实现</h4><p>不断的循环,并根据当前server的角色进行不同的操作</p>\n<h4 id=\"FOLLOWER\"><a href=\"#FOLLOWER\" class=\"headerlink\" title=\"FOLLOWER\"></a>FOLLOWER</h4><ul>\n<li>当收到CANDIDATE发送的<code>VoteRequest RPC</code>时,重置计时器</li>\n<li>当收到LEADER发送的<code>AppendEntries RPC</code>时,重置计时器</li>\n<li>当计时器结束时,说明当前的server断绝联系,于是FOLLOWER转变为CANDIDATE开始新一轮的选举</li>\n</ul>\n<h4 id=\"CANDIDATE\"><a href=\"#CANDIDATE\" class=\"headerlink\" title=\"CANDIDATE\"></a>CANDIDATE</h4><ul>\n<li>在收到<code>AppendEntries RPC</code>时,说明LEADER依旧存在,停止选举转变为FOLLOWER</li>\n<li>当计时器结束时,重新开始选举</li>\n<li>当投票数大于总数的一半时,当选LEADER</li>\n</ul>\n<h4 id=\"LEADER\"><a href=\"#LEADER\" class=\"headerlink\" title=\"LEADER\"></a>LEADER</h4><ul>\n<li>间隔一段时间发送<code>HeartBeat</code>心跳包维持LEADER和其他的server的通信</li>\n</ul>\n<figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div><div class=\"line\">29</div><div class=\"line\">30</div><div class=\"line\">31</div><div class=\"line\">32</div><div class=\"line\">33</div><div class=\"line\">34</div><div class=\"line\">35</div><div class=\"line\">36</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(rf *Raft)</span> <span class=\"title\">startLoop</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\trf.electionTimer = time.NewTimer(randElectionDuration())</div><div class=\"line\">\t<span class=\"keyword\">for</span> {</div><div class=\"line\">\t\t<span class=\"keyword\">switch</span> atomic.LoadInt32(&rf.state) {</div><div class=\"line\">\t\t<span class=\"keyword\">case</span> FOLLOWER:</div><div class=\"line\">\t\t\t<span class=\"keyword\">select</span> {</div><div class=\"line\">\t\t\t<span class=\"keyword\">case</span> <-rf.voteCh:</div><div class=\"line\">\t\t\t\trf.electionTimer.Reset(randElectionDuration())</div><div class=\"line\">\t\t\t<span class=\"keyword\">case</span> <-rf.appendCh:</div><div class=\"line\">\t\t\t\trf.electionTimer.Reset(randElectionDuration())</div><div class=\"line\">\t\t\t<span class=\"keyword\">case</span> <-rf.electionTimer.C:</div><div class=\"line\">\t\t\t\t<span class=\"comment\">//time out</span></div><div class=\"line\">\t\t\t\trf.mu.Lock()</div><div class=\"line\">\t\t\t\trf.updateStateTo(CANDIDATE)</div><div class=\"line\">\t\t\t\trf.mu.Unlock()</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t<span class=\"keyword\">case</span> CANDIDATE:</div><div class=\"line\">\t\t\trf.mu.Lock()</div><div class=\"line\">\t\t\t<span class=\"keyword\">select</span> {</div><div class=\"line\">\t\t\t<span class=\"keyword\">case</span> <-rf.appendCh:</div><div class=\"line\">\t\t\t\trf.updateStateTo(FOLLOWER)</div><div class=\"line\">\t\t\t<span class=\"keyword\">case</span> <-rf.electionTimer.C:</div><div class=\"line\">\t\t\t\trf.electionTimer.Reset(randElectionDuration())</div><div class=\"line\">\t\t\t\trf.startElection()</div><div class=\"line\">\t\t\t<span class=\"keyword\">default</span>:</div><div class=\"line\">\t\t\t\t<span class=\"keyword\">if</span> rf.voteAcquired > <span class=\"built_in\">len</span>(rf.peers)/<span class=\"number\">2</span> {</div><div class=\"line\">\t\t\t\t\trf.updateStateTo(LEADER)</div><div class=\"line\">\t\t\t\t}</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t\trf.mu.Unlock()</div><div class=\"line\">\t\t<span class=\"keyword\">case</span> LEADER:</div><div class=\"line\">\t\t\trf.broadcastAppendEnrties()</div><div class=\"line\">\t\t\ttime.Sleep(HEARTBEAT_INTERVAL * time.Millisecond)</div><div class=\"line\">\t\t}</div><div class=\"line\"></div><div class=\"line\">\t}</div></pre></td></tr></table></figure>\n<h3 id=\"初始化并开始循环\"><a href=\"#初始化并开始循环\" class=\"headerlink\" title=\"初始化并开始循环\"></a>初始化并开始循环</h3><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">Make</span><span class=\"params\">(peers []*labrpc.ClientEnd, me <span class=\"keyword\">int</span>,</span></span></div><div class=\"line\"><span class=\"function\"><span class=\"params\">\tpersister *Persister, applyCh <span class=\"keyword\">chan</span> ApplyMsg)</span> *<span class=\"title\">Raft</span></span> {</div><div class=\"line\">\trf := &Raft{}</div><div class=\"line\">\trf.peers = peers</div><div class=\"line\">\trf.persister = persister</div><div class=\"line\">\trf.me = me</div><div class=\"line\"></div><div class=\"line\">\t<span class=\"comment\">// Your initialization code here (2A, 2B, 2C).</span></div><div class=\"line\">\trf.state = FOLLOWER</div><div class=\"line\">\trf.votedFor = <span class=\"number\">-1</span></div><div class=\"line\">\trf.voteCh = <span class=\"built_in\">make</span>(<span class=\"keyword\">chan</span> <span class=\"keyword\">struct</span>{})</div><div class=\"line\">\trf.appendCh = <span class=\"built_in\">make</span>(<span class=\"keyword\">chan</span> <span class=\"keyword\">struct</span>{})</div><div class=\"line\"></div><div class=\"line\">\t<span class=\"comment\">// initialize from state persisted before a crash</span></div><div class=\"line\">\trf.readPersist(persister.ReadRaftState())</div><div class=\"line\"></div><div class=\"line\">\t<span class=\"keyword\">go</span> rf.startLoop()</div><div class=\"line\"></div><div class=\"line\">\t<span class=\"keyword\">return</span> rf</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h2 id=\"总结\"><a href=\"#总结\" class=\"headerlink\" title=\"总结\"></a>总结</h2><hr>\n<p>第一次将论文中的算法“实现”,虽然是参考了大神的代码,还是觉得收获满满。<br>首先,纠正了我看论文一目十行的方法,以前总觉得论文这东西太虚,只是所谓的想法,经过这次无数遍的研读之后,发现论文中的每一句话对于整个算法的执行都有至关重要的作用。因此,以后在看论文的时候,需要逐句理解,最好能够抽象出整个模型,并且思考论文这么做对于整个系统的实现有什么好处<br>其次,自己的代码的能力实在是弱的不行,我能看得懂这些代码,并能在脑中把每一个函数链接起来,但是距离完全靠自己的能力去实现一个这样的系统还有相当长的路要走。<br>希望能坚持将自己所看的论文、方法都能用自己的话去说出来,加深下理解。</p>\n"},{"title":"The Concurrency on Go","date":"2017-10-25T11:17:29.000Z","_content":"之前在学习MIT6.824的实验中,遇到了大量考虑程序并发性的内容,而go语言的特色之处就是在对并发性的支持上,因此我们来总结一下go语言在并发性方面的编程,希望给自己加深一下理解\n\n## goroutinue\ngoroutine在我的理解中就是快速的开辟一个线程执行相应的函数\n`go f(x, y, z)`\n在新的线程中执行\n`f(x, y, z)`\n```go\npackage main\nimport (\n\t\"fmt\"\n\t\"time\"\n)\nfunc say(s string) {\n\tfor i := 0; i < 5; i++ {\n\t\ttime.Sleep(100 * time.Millisecond)\n\t\tfmt.Println(s)\n\t}\n}\nfunc main() {\n\tgo say(\"world\")\n\tsay(\"hello\")\n}\n```\n执行的结果为\n{% asset_image all_1.png %}\n\n## channel\n\nchannel在我理解是一个信道,信道中传输的数据类型可以是任意的,在另一端准备好之前,发送端和接收端都会被阻塞,在接收端接收的顺序是**先进先出**,类似于队列。\n```go\nch <- v\nx := <- ch\n```\n这一段代码的意思是在发送方将v送入,在接收端用x去接收,channel在使用前必须创建,\n`ch := make(chan int)`\n实验的代码如下所示\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc sum(a []int, c chan int) {\n\tsum := 0\n\tfor _, v := range a {\n\t\tsum += v\n\t}\n\tc <- sum // 将和送入 c\n}\n\nfunc main() {\n\ta := []int{7, 2, 8, -9, 4, 0}\n\tc := make(chan int)\n\tgo sum(a[:len(a)/2], c)\n\tgo sum(a[len(a)/2:], c)\n\tx := <-c\n\ty := <-c\n\tfmt.Println(x, y, x+y)\n}\n```\n代码的执行结果如下所示:\n`17 -5 12`\n由于此处使用的channel不带有缓冲,每在channel中放入一个变量,在取走之前`c <- sum`之前的代码阻塞,直到channel中的变量被取走,程序才会继续执行\n\n## 带有缓冲的channel\n\n带有缓冲的channel在构建时,`make()`的第二个参数作为缓冲区的大小\n向缓冲区填充变量的时候,只有当缓冲区满的时候发送数据程序会阻塞,而当缓冲区为空的时候,接收变量的程序会阻塞\n\n## range 和 close\n`range`可以源源不断的从channel中读取数据直到关闭\n`close`可以关闭channel表示再也没有值被发送了\n`v, ok = <- ch`可以用来测试channel是否被关闭,若关闭ok的返回值为`false`\n实验的代码如下所示:\n\n```go\npackage main\nimport (\n\t\"fmt\"\n)\nfunc fibonacci(n int, c chan int) {\n\tx, y := 0, 1\n\tfor i := 0; i < n; i++ {\n\t\tc <- x\n\t\tx, y = y, x+y\n\t}\n\tclose(c)\n}\nfunc main() {\n\tc := make(chan int, 10)\n\tgo fibonacci(cap(c), c)\n\tfor i := range c {\n\t\tfmt.Println(i)\n\t}\n\t_, ok := <-c\n\tfmt.Print(ok)\n}\n```\n实验中实现的是打印斐波那契数列中的前10个数字,当产生完10个数字并全部放入缓冲区后,程序阻塞,主程序中的`range`开始读取channel缓存,当读取完毕后,函数`fibonacci()`关闭了channel,于是`ok = false`\n\n实验的结果如下所示:\n\n{% asset_image test4.png %}\n\n## select\n`select`有两种执行情况\n\n1. 阻塞直到条件分支中的某个分支可以执行时,便会执行这个分支\n2. 当有多个分支符合条件的时候,便会随机选择一个分支执行\n\n\n实验的代码如下所示:\n```go\npackage main\n\nimport \"fmt\"\n\nfunc fibonacci(c, quit chan int) {\n\tx, y := 0, 1\n\tfor {\n\t\tselect {\n\t\tcase c <- x:\n\t\t\tx, y = y, x+y\n\t\tcase <-quit:\n\t\t\tfmt.Println(\"quit\")\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc main() {\n\tc := make(chan int)\n\tquit := make(chan int)\n\tgo func() {\n\t\tfor i := 0; i < 10; i++ {\n\t\t\tfmt.Println(<-c)\n\t\t}\n\t\tquit <- 0\n\t}()\n\tfibonacci(c, quit)\n}\n```\n实验的结果如下图所示:\n{% asset_image test5.png %}\n\n### 结果解析\n\n1. 序先执行至`fmt.Println(<-c)`时阻塞,接收程序缓冲区为空\n2. 在`fibonacci()`函数中,`select`语句智能选择往channel c中放入数据,由于`quit`为空,一直阻塞\n3. 当打印完成结果后,`quit <- 0`执行,`select`可以执行`case <- quit`之后的代码,打印`quit`并返回主程序\n\n### 默认选择\n\n当`select`中的其他条件分支都没有准备好的时候`default`分支会被执行\n通常用作非阻塞的发送或接收程序\n\n## sync.Mutex\n当我们不需要在goroutine中通信时,我们可以使用互斥锁在使用**共享**资源时进行封锁,使用结束后进行解锁\ngo库中提供了`sync.Mutex`类型以及两个方法:\n`Lock()`\n`Unlock()`\n参考的代码如下所示:\n\n\n```go\nfunc (c *SafeCounter) Inc(key string) {\n\tc.mux.Lock()\n\t// Lock 之后同一时刻只有一个 goroutine 能访问 c.v\n\tc.v[key]++\n\tc.mux.Unlock()\n}\n```\n\n在增加`c.v[key]`之前先对其进行封锁,使用结束后解锁,而在定义这个可以封锁的资源时,我们采用了结构体的方式,将这个互斥锁**绑定**在资源上\n```go\ntype SafeCounter struct {\n\tv map[string]int\n\tmux sync.Mutex\n}\n```\n## 多种方式的crawer\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n)\n\n//\n// Several solutions to the crawler exercise from the Go tutorial (https://tour.golang.org/concurrency/10)\n//\n\n//\n// Serial crawler\n//\n\nfunc CrawlSerial(url string, fetcher Fetcher, fetched map[string]bool) {\n\tif fetched[url] {\n\t\treturn\n\t}\n\tfetched[url] = true\n\tbody, urls, err := fetcher.Fetch(url)\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\tfmt.Printf(\"found: %s %q\\n\", url, body)\n\tfor _, u := range urls {\n\t\tCrawlSerial(u, fetcher, fetched)\n\t}\n\treturn\n}\n\n//\n// Concurrent crawler with Mutex and WaitGroup\n//\n\ntype fetchState struct {\n\tmu sync.Mutex\n\tfetched map[string]bool\n}\n\nfunc (f *fetchState) CheckAndMark(url string) bool {\n\t//defer f.mu.Unlock()\n\n\t//f.mu.Lock()\n\tif f.fetched[url] {\n\t\treturn true\n\t}\n\tf.fetched[url] = true\n\treturn false\n}\n\nfunc mkFetchState() *fetchState {\n\tf := &fetchState{}\n\tf.fetched = make(map[string]bool)\n\treturn f\n}\n\nfunc CrawlConcurrentMutex(url string, fetcher Fetcher, f *fetchState) {\n\tif f.CheckAndMark(url) {\n\t\treturn\n\t}\n\n\tbody, urls, err := fetcher.Fetch(url)\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\tfmt.Printf(\"found: %s %q\\n\", url, body)\n\tvar done sync.WaitGroup\n\tfor _, u := range urls {\n\t\tdone.Add(1)\n\t\tgo func(u string) {\n\t\t\tdefer done.Done()\n\t\t\tCrawlConcurrentMutex(u, fetcher, f)\n\t\t}(u) // Without the u argument there is a race\n\t}\n\tdone.Wait()\n\treturn\n}\n\n//\n// Concurrent crawler with channels\n//\n\nfunc dofetch(url1 string, ch chan []string, fetcher Fetcher) {\n\tbody, urls, err := fetcher.Fetch(url1)\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\tch <- []string{}\n\t} else {\n\t\tfmt.Printf(\"found: %s %q\\n\", url1, body)\n\t\tch <- urls\n\t}\n}\n\nfunc master(ch chan []string, fetcher Fetcher) {\n\tn := 1\n\tfetched := make(map[string]bool)\n\tfor urls := range ch {\n\t\tfor _, u := range urls {\n\t\t\tif _, ok := fetched[u]; ok == false {\n\t\t\t\tfetched[u] = true\n\t\t\t\tn += 1\n\t\t\t\tgo dofetch(u, ch, fetcher)\n\t\t\t}\n\t\t}\n\t\tn -= 1\n\t\tif n == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc CrawlConcurrentChannel(url string, fetcher Fetcher) {\n\tch := make(chan []string)\n\tgo func() {\n\t\tch <- []string{url}\n\t}()\n\tmaster(ch, fetcher)\n}\n\n//\n// main\n//\n\nfunc main() {\n\tfmt.Printf(\"=== Serial===\\n\")\n\tCrawlSerial(\"http://golang.org/\", fetcher, make(map[string]bool))\n\n\tfmt.Printf(\"=== ConcurrentMutex ===\\n\")\n\tCrawlConcurrentMutex(\"http://golang.org/\", fetcher, mkFetchState())\n\n\tfmt.Printf(\"=== ConcurrentChannel ===\\n\")\n\tCrawlConcurrentChannel(\"http://golang.org/\", fetcher)\n}\n\n//\n// Fetcher\n//\n\ntype Fetcher interface {\n\t// Fetch returns the body of URL and\n\t// a slice of URLs found on that page.\n\tFetch(url string) (body string, urls []string, err error)\n}\n\n// fakeFetcher is Fetcher that returns canned results.\ntype fakeFetcher map[string]*fakeResult\n\ntype fakeResult struct {\n\tbody string\n\turls []string\n}\n\nfunc (f fakeFetcher) Fetch(url string) (string, []string, error) {\n\tif res, ok := f[url]; ok {\n\t\treturn res.body, res.urls, nil\n\t}\n\treturn \"\", nil, fmt.Errorf(\"not found: %s\", url)\n}\n\n// fetcher is a populated fakeFetcher.\nvar fetcher = fakeFetcher{\n\t\"http://golang.org/\": &fakeResult{\n\t\t\"The Go Programming Language\",\n\t\t[]string{\n\t\t\t\"http://golang.org/pkg/\",\n\t\t\t\"http://golang.org/cmd/\",\n\t\t},\n\t},\n\t\"http://golang.org/pkg/\": &fakeResult{\n\t\t\"Packages\",\n\t\t[]string{\n\t\t\t\"http://golang.org/\",\n\t\t\t\"http://golang.org/cmd/\",\n\t\t\t\"http://golang.org/pkg/fmt/\",\n\t\t\t\"http://golang.org/pkg/os/\",\n\t\t},\n\t},\n\t\"http://golang.org/pkg/fmt/\": &fakeResult{\n\t\t\"Package fmt\",\n\t\t[]string{\n\t\t\t\"http://golang.org/\",\n\t\t\t\"http://golang.org/pkg/\",\n\t\t},\n\t},\n\t\"http://golang.org/pkg/os/\": &fakeResult{\n\t\t\"Package os\",\n\t\t[]string{\n\t\t\t\"http://golang.org/\",\n\t\t\t\"http://golang.org/pkg/\",\n\t\t},\n\t},\n}\n```\n\n### 代码解析\n#### 串行的方式爬取内容\n1. 在`CrawlSewrial`中首先判断当前这个`url`是否被爬取过\n2. 若未爬取`body, urls, err := fetcher.Feach(url)`,打印当前的`body`\n3. 遍历urls,继续爬取内容\n\n\n#### 使用Mutex并行爬取数据\n```go\nvar done sync.WaitGroup\nfor _, u := range urls {\n\tdone.Add(1)\n\tgo func(u string) {\n\t\tdefer done.Done()\n\t\tCrawlConcurrentMutex(u, fetcher, f)\n\t}(u) // Without the u argument there is a race\n}\ndone.Wait()\n```\n遍历`urls`,每读取一条就并行的执行,并将任务添加进入`var done sync.WaitGroup`中\n在循环外设置`done.Wait()`等待所有的线程执行完毕后继续执行主线程代码\n\n#### 使用Channel并行爬取数据\n```go\nfunc master(ch chan []string, fetcher Fetcher) {\n\tn := 1\n\tfetched := make(map[string]bool)\n\tfor urls := range ch {\n\t\tfor _, u := range urls {\n\t\t\tif _, ok := fetched[u]; ok == false {\n\t\t\t\tfetched[u] = true\n\t\t\t\tn += 1\n\t\t\t\tgo dofetch(u, ch, fetcher)\n\t\t\t}\n\t\t}\n\t\tn -= 1\n\t\tif n == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n}\n```\n将要爬取的`url`放入`channel`中,使用`master`处理,判断是否已经被访问过\n若否开辟一个线程去访问,并将新的`urls`放入`channel`中 `n+1`\n读完一个`ch`中的`urls`后`n-1`直到最后`n=0`退出循环,所有的`url`都已经访问\n\n\n\n\n\n\n\n\n\n\n","source":"_posts/the-concurrency-on-go.md","raw":"---\ntitle: The Concurrency on Go\ndate: 2017-10-25 19:17:29\ncategories:\n- golang\ntags:\n- concurrency\n---\n之前在学习MIT6.824的实验中,遇到了大量考虑程序并发性的内容,而go语言的特色之处就是在对并发性的支持上,因此我们来总结一下go语言在并发性方面的编程,希望给自己加深一下理解\n\n## goroutinue\ngoroutine在我的理解中就是快速的开辟一个线程执行相应的函数\n`go f(x, y, z)`\n在新的线程中执行\n`f(x, y, z)`\n```go\npackage main\nimport (\n\t\"fmt\"\n\t\"time\"\n)\nfunc say(s string) {\n\tfor i := 0; i < 5; i++ {\n\t\ttime.Sleep(100 * time.Millisecond)\n\t\tfmt.Println(s)\n\t}\n}\nfunc main() {\n\tgo say(\"world\")\n\tsay(\"hello\")\n}\n```\n执行的结果为\n{% asset_image all_1.png %}\n\n## channel\n\nchannel在我理解是一个信道,信道中传输的数据类型可以是任意的,在另一端准备好之前,发送端和接收端都会被阻塞,在接收端接收的顺序是**先进先出**,类似于队列。\n```go\nch <- v\nx := <- ch\n```\n这一段代码的意思是在发送方将v送入,在接收端用x去接收,channel在使用前必须创建,\n`ch := make(chan int)`\n实验的代码如下所示\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc sum(a []int, c chan int) {\n\tsum := 0\n\tfor _, v := range a {\n\t\tsum += v\n\t}\n\tc <- sum // 将和送入 c\n}\n\nfunc main() {\n\ta := []int{7, 2, 8, -9, 4, 0}\n\tc := make(chan int)\n\tgo sum(a[:len(a)/2], c)\n\tgo sum(a[len(a)/2:], c)\n\tx := <-c\n\ty := <-c\n\tfmt.Println(x, y, x+y)\n}\n```\n代码的执行结果如下所示:\n`17 -5 12`\n由于此处使用的channel不带有缓冲,每在channel中放入一个变量,在取走之前`c <- sum`之前的代码阻塞,直到channel中的变量被取走,程序才会继续执行\n\n## 带有缓冲的channel\n\n带有缓冲的channel在构建时,`make()`的第二个参数作为缓冲区的大小\n向缓冲区填充变量的时候,只有当缓冲区满的时候发送数据程序会阻塞,而当缓冲区为空的时候,接收变量的程序会阻塞\n\n## range 和 close\n`range`可以源源不断的从channel中读取数据直到关闭\n`close`可以关闭channel表示再也没有值被发送了\n`v, ok = <- ch`可以用来测试channel是否被关闭,若关闭ok的返回值为`false`\n实验的代码如下所示:\n\n```go\npackage main\nimport (\n\t\"fmt\"\n)\nfunc fibonacci(n int, c chan int) {\n\tx, y := 0, 1\n\tfor i := 0; i < n; i++ {\n\t\tc <- x\n\t\tx, y = y, x+y\n\t}\n\tclose(c)\n}\nfunc main() {\n\tc := make(chan int, 10)\n\tgo fibonacci(cap(c), c)\n\tfor i := range c {\n\t\tfmt.Println(i)\n\t}\n\t_, ok := <-c\n\tfmt.Print(ok)\n}\n```\n实验中实现的是打印斐波那契数列中的前10个数字,当产生完10个数字并全部放入缓冲区后,程序阻塞,主程序中的`range`开始读取channel缓存,当读取完毕后,函数`fibonacci()`关闭了channel,于是`ok = false`\n\n实验的结果如下所示:\n\n{% asset_image test4.png %}\n\n## select\n`select`有两种执行情况\n\n1. 阻塞直到条件分支中的某个分支可以执行时,便会执行这个分支\n2. 当有多个分支符合条件的时候,便会随机选择一个分支执行\n\n\n实验的代码如下所示:\n```go\npackage main\n\nimport \"fmt\"\n\nfunc fibonacci(c, quit chan int) {\n\tx, y := 0, 1\n\tfor {\n\t\tselect {\n\t\tcase c <- x:\n\t\t\tx, y = y, x+y\n\t\tcase <-quit:\n\t\t\tfmt.Println(\"quit\")\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc main() {\n\tc := make(chan int)\n\tquit := make(chan int)\n\tgo func() {\n\t\tfor i := 0; i < 10; i++ {\n\t\t\tfmt.Println(<-c)\n\t\t}\n\t\tquit <- 0\n\t}()\n\tfibonacci(c, quit)\n}\n```\n实验的结果如下图所示:\n{% asset_image test5.png %}\n\n### 结果解析\n\n1. 序先执行至`fmt.Println(<-c)`时阻塞,接收程序缓冲区为空\n2. 在`fibonacci()`函数中,`select`语句智能选择往channel c中放入数据,由于`quit`为空,一直阻塞\n3. 当打印完成结果后,`quit <- 0`执行,`select`可以执行`case <- quit`之后的代码,打印`quit`并返回主程序\n\n### 默认选择\n\n当`select`中的其他条件分支都没有准备好的时候`default`分支会被执行\n通常用作非阻塞的发送或接收程序\n\n## sync.Mutex\n当我们不需要在goroutine中通信时,我们可以使用互斥锁在使用**共享**资源时进行封锁,使用结束后进行解锁\ngo库中提供了`sync.Mutex`类型以及两个方法:\n`Lock()`\n`Unlock()`\n参考的代码如下所示:\n\n\n```go\nfunc (c *SafeCounter) Inc(key string) {\n\tc.mux.Lock()\n\t// Lock 之后同一时刻只有一个 goroutine 能访问 c.v\n\tc.v[key]++\n\tc.mux.Unlock()\n}\n```\n\n在增加`c.v[key]`之前先对其进行封锁,使用结束后解锁,而在定义这个可以封锁的资源时,我们采用了结构体的方式,将这个互斥锁**绑定**在资源上\n```go\ntype SafeCounter struct {\n\tv map[string]int\n\tmux sync.Mutex\n}\n```\n## 多种方式的crawer\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n)\n\n//\n// Several solutions to the crawler exercise from the Go tutorial (https://tour.golang.org/concurrency/10)\n//\n\n//\n// Serial crawler\n//\n\nfunc CrawlSerial(url string, fetcher Fetcher, fetched map[string]bool) {\n\tif fetched[url] {\n\t\treturn\n\t}\n\tfetched[url] = true\n\tbody, urls, err := fetcher.Fetch(url)\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\tfmt.Printf(\"found: %s %q\\n\", url, body)\n\tfor _, u := range urls {\n\t\tCrawlSerial(u, fetcher, fetched)\n\t}\n\treturn\n}\n\n//\n// Concurrent crawler with Mutex and WaitGroup\n//\n\ntype fetchState struct {\n\tmu sync.Mutex\n\tfetched map[string]bool\n}\n\nfunc (f *fetchState) CheckAndMark(url string) bool {\n\t//defer f.mu.Unlock()\n\n\t//f.mu.Lock()\n\tif f.fetched[url] {\n\t\treturn true\n\t}\n\tf.fetched[url] = true\n\treturn false\n}\n\nfunc mkFetchState() *fetchState {\n\tf := &fetchState{}\n\tf.fetched = make(map[string]bool)\n\treturn f\n}\n\nfunc CrawlConcurrentMutex(url string, fetcher Fetcher, f *fetchState) {\n\tif f.CheckAndMark(url) {\n\t\treturn\n\t}\n\n\tbody, urls, err := fetcher.Fetch(url)\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\tfmt.Printf(\"found: %s %q\\n\", url, body)\n\tvar done sync.WaitGroup\n\tfor _, u := range urls {\n\t\tdone.Add(1)\n\t\tgo func(u string) {\n\t\t\tdefer done.Done()\n\t\t\tCrawlConcurrentMutex(u, fetcher, f)\n\t\t}(u) // Without the u argument there is a race\n\t}\n\tdone.Wait()\n\treturn\n}\n\n//\n// Concurrent crawler with channels\n//\n\nfunc dofetch(url1 string, ch chan []string, fetcher Fetcher) {\n\tbody, urls, err := fetcher.Fetch(url1)\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\tch <- []string{}\n\t} else {\n\t\tfmt.Printf(\"found: %s %q\\n\", url1, body)\n\t\tch <- urls\n\t}\n}\n\nfunc master(ch chan []string, fetcher Fetcher) {\n\tn := 1\n\tfetched := make(map[string]bool)\n\tfor urls := range ch {\n\t\tfor _, u := range urls {\n\t\t\tif _, ok := fetched[u]; ok == false {\n\t\t\t\tfetched[u] = true\n\t\t\t\tn += 1\n\t\t\t\tgo dofetch(u, ch, fetcher)\n\t\t\t}\n\t\t}\n\t\tn -= 1\n\t\tif n == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc CrawlConcurrentChannel(url string, fetcher Fetcher) {\n\tch := make(chan []string)\n\tgo func() {\n\t\tch <- []string{url}\n\t}()\n\tmaster(ch, fetcher)\n}\n\n//\n// main\n//\n\nfunc main() {\n\tfmt.Printf(\"=== Serial===\\n\")\n\tCrawlSerial(\"http://golang.org/\", fetcher, make(map[string]bool))\n\n\tfmt.Printf(\"=== ConcurrentMutex ===\\n\")\n\tCrawlConcurrentMutex(\"http://golang.org/\", fetcher, mkFetchState())\n\n\tfmt.Printf(\"=== ConcurrentChannel ===\\n\")\n\tCrawlConcurrentChannel(\"http://golang.org/\", fetcher)\n}\n\n//\n// Fetcher\n//\n\ntype Fetcher interface {\n\t// Fetch returns the body of URL and\n\t// a slice of URLs found on that page.\n\tFetch(url string) (body string, urls []string, err error)\n}\n\n// fakeFetcher is Fetcher that returns canned results.\ntype fakeFetcher map[string]*fakeResult\n\ntype fakeResult struct {\n\tbody string\n\turls []string\n}\n\nfunc (f fakeFetcher) Fetch(url string) (string, []string, error) {\n\tif res, ok := f[url]; ok {\n\t\treturn res.body, res.urls, nil\n\t}\n\treturn \"\", nil, fmt.Errorf(\"not found: %s\", url)\n}\n\n// fetcher is a populated fakeFetcher.\nvar fetcher = fakeFetcher{\n\t\"http://golang.org/\": &fakeResult{\n\t\t\"The Go Programming Language\",\n\t\t[]string{\n\t\t\t\"http://golang.org/pkg/\",\n\t\t\t\"http://golang.org/cmd/\",\n\t\t},\n\t},\n\t\"http://golang.org/pkg/\": &fakeResult{\n\t\t\"Packages\",\n\t\t[]string{\n\t\t\t\"http://golang.org/\",\n\t\t\t\"http://golang.org/cmd/\",\n\t\t\t\"http://golang.org/pkg/fmt/\",\n\t\t\t\"http://golang.org/pkg/os/\",\n\t\t},\n\t},\n\t\"http://golang.org/pkg/fmt/\": &fakeResult{\n\t\t\"Package fmt\",\n\t\t[]string{\n\t\t\t\"http://golang.org/\",\n\t\t\t\"http://golang.org/pkg/\",\n\t\t},\n\t},\n\t\"http://golang.org/pkg/os/\": &fakeResult{\n\t\t\"Package os\",\n\t\t[]string{\n\t\t\t\"http://golang.org/\",\n\t\t\t\"http://golang.org/pkg/\",\n\t\t},\n\t},\n}\n```\n\n### 代码解析\n#### 串行的方式爬取内容\n1. 在`CrawlSewrial`中首先判断当前这个`url`是否被爬取过\n2. 若未爬取`body, urls, err := fetcher.Feach(url)`,打印当前的`body`\n3. 遍历urls,继续爬取内容\n\n\n#### 使用Mutex并行爬取数据\n```go\nvar done sync.WaitGroup\nfor _, u := range urls {\n\tdone.Add(1)\n\tgo func(u string) {\n\t\tdefer done.Done()\n\t\tCrawlConcurrentMutex(u, fetcher, f)\n\t}(u) // Without the u argument there is a race\n}\ndone.Wait()\n```\n遍历`urls`,每读取一条就并行的执行,并将任务添加进入`var done sync.WaitGroup`中\n在循环外设置`done.Wait()`等待所有的线程执行完毕后继续执行主线程代码\n\n#### 使用Channel并行爬取数据\n```go\nfunc master(ch chan []string, fetcher Fetcher) {\n\tn := 1\n\tfetched := make(map[string]bool)\n\tfor urls := range ch {\n\t\tfor _, u := range urls {\n\t\t\tif _, ok := fetched[u]; ok == false {\n\t\t\t\tfetched[u] = true\n\t\t\t\tn += 1\n\t\t\t\tgo dofetch(u, ch, fetcher)\n\t\t\t}\n\t\t}\n\t\tn -= 1\n\t\tif n == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n}\n```\n将要爬取的`url`放入`channel`中,使用`master`处理,判断是否已经被访问过\n若否开辟一个线程去访问,并将新的`urls`放入`channel`中 `n+1`\n读完一个`ch`中的`urls`后`n-1`直到最后`n=0`退出循环,所有的`url`都已经访问\n\n\n\n\n\n\n\n\n\n\n","slug":"the-concurrency-on-go","published":1,"updated":"2018-04-30T13:49:56.137Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cjyohfve0000rsy0y4mf0paqq","content":"<p>之前在学习MIT6.824的实验中,遇到了大量考虑程序并发性的内容,而go语言的特色之处就是在对并发性的支持上,因此我们来总结一下go语言在并发性方面的编程,希望给自己加深一下理解</p>\n<h2 id=\"goroutinue\"><a href=\"#goroutinue\" class=\"headerlink\" title=\"goroutinue\"></a>goroutinue</h2><p>goroutine在我的理解中就是快速的开辟一个线程执行相应的函数<br><code>go f(x, y, z)</code><br>在新的线程中执行<br><code>f(x, y, z)</code><br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">package</span> main</div><div class=\"line\"><span class=\"keyword\">import</span> (</div><div class=\"line\">\t<span class=\"string\">\"fmt\"</span></div><div class=\"line\">\t<span class=\"string\">\"time\"</span></div><div class=\"line\">)</div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">say</span><span class=\"params\">(s <span class=\"keyword\">string</span>)</span></span> {</div><div class=\"line\">\t<span class=\"keyword\">for</span> i := <span class=\"number\">0</span>; i < <span class=\"number\">5</span>; i++ {</div><div class=\"line\">\t\ttime.Sleep(<span class=\"number\">100</span> * time.Millisecond)</div><div class=\"line\">\t\tfmt.Println(s)</div><div class=\"line\">\t}</div><div class=\"line\">}</div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">main</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\t<span class=\"keyword\">go</span> say(<span class=\"string\">\"world\"</span>)</div><div class=\"line\">\tsay(<span class=\"string\">\"hello\"</span>)</div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<p>执行的结果为<br><img src=\"/2017/10/25/the-concurrency-on-go/all_1.png\" alt=\"all_1.png\" title=\"\"></p>\n<h2 id=\"channel\"><a href=\"#channel\" class=\"headerlink\" title=\"channel\"></a>channel</h2><p>channel在我理解是一个信道,信道中传输的数据类型可以是任意的,在另一端准备好之前,发送端和接收端都会被阻塞,在接收端接收的顺序是<strong>先进先出</strong>,类似于队列。<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div></pre></td><td class=\"code\"><pre><div class=\"line\">ch <- v</div><div class=\"line\">x := <- ch</div></pre></td></tr></table></figure></p>\n<p>这一段代码的意思是在发送方将v送入,在接收端用x去接收,channel在使用前必须创建,<br><code>ch := make(chan int)</code><br>实验的代码如下所示</p>\n<figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">package</span> main</div><div class=\"line\"></div><div class=\"line\"><span class=\"keyword\">import</span> <span class=\"string\">\"fmt\"</span></div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">sum</span><span class=\"params\">(a []<span class=\"keyword\">int</span>, c <span class=\"keyword\">chan</span> <span class=\"keyword\">int</span>)</span></span> {</div><div class=\"line\">\tsum := <span class=\"number\">0</span></div><div class=\"line\">\t<span class=\"keyword\">for</span> _, v := <span class=\"keyword\">range</span> a {</div><div class=\"line\">\t\tsum += v</div><div class=\"line\">\t}</div><div class=\"line\">\tc <- sum <span class=\"comment\">// 将和送入 c</span></div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">main</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\ta := []<span class=\"keyword\">int</span>{<span class=\"number\">7</span>, <span class=\"number\">2</span>, <span class=\"number\">8</span>, <span class=\"number\">-9</span>, <span class=\"number\">4</span>, <span class=\"number\">0</span>}</div><div class=\"line\">\tc := <span class=\"built_in\">make</span>(<span class=\"keyword\">chan</span> <span class=\"keyword\">int</span>)</div><div class=\"line\">\t<span class=\"keyword\">go</span> sum(a[:<span class=\"built_in\">len</span>(a)/<span class=\"number\">2</span>], c)</div><div class=\"line\">\t<span class=\"keyword\">go</span> sum(a[<span class=\"built_in\">len</span>(a)/<span class=\"number\">2</span>:], c)</div><div class=\"line\">\tx := <-c</div><div class=\"line\">\ty := <-c</div><div class=\"line\">\tfmt.Println(x, y, x+y)</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<p>代码的执行结果如下所示:<br><code>17 -5 12</code><br>由于此处使用的channel不带有缓冲,每在channel中放入一个变量,在取走之前<code>c <- sum</code>之前的代码阻塞,直到channel中的变量被取走,程序才会继续执行</p>\n<h2 id=\"带有缓冲的channel\"><a href=\"#带有缓冲的channel\" class=\"headerlink\" title=\"带有缓冲的channel\"></a>带有缓冲的channel</h2><p>带有缓冲的channel在构建时,<code>make()</code>的第二个参数作为缓冲区的大小<br>向缓冲区填充变量的时候,只有当缓冲区满的时候发送数据程序会阻塞,而当缓冲区为空的时候,接收变量的程序会阻塞</p>\n<h2 id=\"range-和-close\"><a href=\"#range-和-close\" class=\"headerlink\" title=\"range 和 close\"></a>range 和 close</h2><p><code>range</code>可以源源不断的从channel中读取数据直到关闭<br><code>close</code>可以关闭channel表示再也没有值被发送了<br><code>v, ok = <- ch</code>可以用来测试channel是否被关闭,若关闭ok的返回值为<code>false</code><br>实验的代码如下所示:</p>\n<figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">package</span> main</div><div class=\"line\"><span class=\"keyword\">import</span> (</div><div class=\"line\">\t<span class=\"string\">\"fmt\"</span></div><div class=\"line\">)</div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">fibonacci</span><span class=\"params\">(n <span class=\"keyword\">int</span>, c <span class=\"keyword\">chan</span> <span class=\"keyword\">int</span>)</span></span> {</div><div class=\"line\">\tx, y := <span class=\"number\">0</span>, <span class=\"number\">1</span></div><div class=\"line\">\t<span class=\"keyword\">for</span> i := <span class=\"number\">0</span>; i < n; i++ {</div><div class=\"line\">\t\tc <- x</div><div class=\"line\">\t\tx, y = y, x+y</div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"built_in\">close</span>(c)</div><div class=\"line\">}</div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">main</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\tc := <span class=\"built_in\">make</span>(<span class=\"keyword\">chan</span> <span class=\"keyword\">int</span>, <span class=\"number\">10</span>)</div><div class=\"line\">\t<span class=\"keyword\">go</span> fibonacci(<span class=\"built_in\">cap</span>(c), c)</div><div class=\"line\">\t<span class=\"keyword\">for</span> i := <span class=\"keyword\">range</span> c {</div><div class=\"line\">\t\tfmt.Println(i)</div><div class=\"line\">\t}</div><div class=\"line\">\t_, ok := <-c</div><div class=\"line\">\tfmt.Print(ok)</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<p>实验中实现的是打印斐波那契数列中的前10个数字,当产生完10个数字并全部放入缓冲区后,程序阻塞,主程序中的<code>range</code>开始读取channel缓存,当读取完毕后,函数<code>fibonacci()</code>关闭了channel,于是<code>ok = false</code></p>\n<p>实验的结果如下所示:</p>\n<img src=\"/2017/10/25/the-concurrency-on-go/test4.png\" alt=\"test4.png\" title=\"\">\n<h2 id=\"select\"><a href=\"#select\" class=\"headerlink\" title=\"select\"></a>select</h2><p><code>select</code>有两种执行情况</p>\n<ol>\n<li>阻塞直到条件分支中的某个分支可以执行时,便会执行这个分支</li>\n<li>当有多个分支符合条件的时候,便会随机选择一个分支执行</li>\n</ol>\n<p>实验的代码如下所示:<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">package</span> main</div><div class=\"line\"></div><div class=\"line\"><span class=\"keyword\">import</span> <span class=\"string\">\"fmt\"</span></div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">fibonacci</span><span class=\"params\">(c, quit <span class=\"keyword\">chan</span> <span class=\"keyword\">int</span>)</span></span> {</div><div class=\"line\">\tx, y := <span class=\"number\">0</span>, <span class=\"number\">1</span></div><div class=\"line\">\t<span class=\"keyword\">for</span> {</div><div class=\"line\">\t\t<span class=\"keyword\">select</span> {</div><div class=\"line\">\t\t<span class=\"keyword\">case</span> c <- x:</div><div class=\"line\">\t\t\tx, y = y, x+y</div><div class=\"line\">\t\t<span class=\"keyword\">case</span> <-quit:</div><div class=\"line\">\t\t\tfmt.Println(<span class=\"string\">\"quit\"</span>)</div><div class=\"line\">\t\t\t<span class=\"keyword\">return</span></div><div class=\"line\">\t\t}</div><div class=\"line\">\t}</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">main</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\tc := <span class=\"built_in\">make</span>(<span class=\"keyword\">chan</span> <span class=\"keyword\">int</span>)</div><div class=\"line\">\tquit := <span class=\"built_in\">make</span>(<span class=\"keyword\">chan</span> <span class=\"keyword\">int</span>)</div><div class=\"line\">\t<span class=\"keyword\">go</span> <span class=\"function\"><span class=\"keyword\">func</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> i := <span class=\"number\">0</span>; i < <span class=\"number\">10</span>; i++ {</div><div class=\"line\">\t\t\tfmt.Println(<-c)</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tquit <- <span class=\"number\">0</span></div><div class=\"line\">\t}()</div><div class=\"line\">\tfibonacci(c, quit)</div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<p>实验的结果如下图所示:<br><img src=\"/2017/10/25/the-concurrency-on-go/test5.png\" alt=\"test5.png\" title=\"\"></p>\n<h3 id=\"结果解析\"><a href=\"#结果解析\" class=\"headerlink\" title=\"结果解析\"></a>结果解析</h3><ol>\n<li>序先执行至<code>fmt.Println(<-c)</code>时阻塞,接收程序缓冲区为空</li>\n<li>在<code>fibonacci()</code>函数中,<code>select</code>语句智能选择往channel c中放入数据,由于<code>quit</code>为空,一直阻塞</li>\n<li>当打印完成结果后,<code>quit <- 0</code>执行,<code>select</code>可以执行<code>case <- quit</code>之后的代码,打印<code>quit</code>并返回主程序</li>\n</ol>\n<h3 id=\"默认选择\"><a href=\"#默认选择\" class=\"headerlink\" title=\"默认选择\"></a>默认选择</h3><p>当<code>select</code>中的其他条件分支都没有准备好的时候<code>default</code>分支会被执行<br>通常用作非阻塞的发送或接收程序</p>\n<h2 id=\"sync-Mutex\"><a href=\"#sync-Mutex\" class=\"headerlink\" title=\"sync.Mutex\"></a>sync.Mutex</h2><p>当我们不需要在goroutine中通信时,我们可以使用互斥锁在使用<strong>共享</strong>资源时进行封锁,使用结束后进行解锁<br>go库中提供了<code>sync.Mutex</code>类型以及两个方法:<br><code>Lock()</code><br><code>Unlock()</code><br>参考的代码如下所示:</p>\n<figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(c *SafeCounter)</span> <span class=\"title\">Inc</span><span class=\"params\">(key <span class=\"keyword\">string</span>)</span></span> {</div><div class=\"line\">\tc.mux.Lock()</div><div class=\"line\">\t<span class=\"comment\">// Lock 之后同一时刻只有一个 goroutine 能访问 c.v</span></div><div class=\"line\">\tc.v[key]++</div><div class=\"line\">\tc.mux.Unlock()</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<p>在增加<code>c.v[key]</code>之前先对其进行封锁,使用结束后解锁,而在定义这个可以封锁的资源时,我们采用了结构体的方式,将这个互斥锁<strong>绑定</strong>在资源上<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">type</span> SafeCounter <span class=\"keyword\">struct</span> {</div><div class=\"line\">\tv <span class=\"keyword\">map</span>[<span class=\"keyword\">string</span>]<span class=\"keyword\">int</span></div><div class=\"line\">\tmux sync.Mutex</div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<h2 id=\"多种方式的crawer\"><a href=\"#多种方式的crawer\" class=\"headerlink\" title=\"多种方式的crawer\"></a>多种方式的crawer</h2><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div><div class=\"line\">29</div><div class=\"line\">30</div><div class=\"line\">31</div><div class=\"line\">32</div><div class=\"line\">33</div><div class=\"line\">34</div><div class=\"line\">35</div><div class=\"line\">36</div><div class=\"line\">37</div><div class=\"line\">38</div><div class=\"line\">39</div><div class=\"line\">40</div><div class=\"line\">41</div><div class=\"line\">42</div><div class=\"line\">43</div><div class=\"line\">44</div><div class=\"line\">45</div><div class=\"line\">46</div><div class=\"line\">47</div><div class=\"line\">48</div><div class=\"line\">49</div><div class=\"line\">50</div><div class=\"line\">51</div><div class=\"line\">52</div><div class=\"line\">53</div><div class=\"line\">54</div><div class=\"line\">55</div><div class=\"line\">56</div><div class=\"line\">57</div><div class=\"line\">58</div><div class=\"line\">59</div><div class=\"line\">60</div><div class=\"line\">61</div><div class=\"line\">62</div><div class=\"line\">63</div><div class=\"line\">64</div><div class=\"line\">65</div><div class=\"line\">66</div><div class=\"line\">67</div><div class=\"line\">68</div><div class=\"line\">69</div><div class=\"line\">70</div><div class=\"line\">71</div><div class=\"line\">72</div><div class=\"line\">73</div><div class=\"line\">74</div><div class=\"line\">75</div><div class=\"line\">76</div><div class=\"line\">77</div><div class=\"line\">78</div><div class=\"line\">79</div><div class=\"line\">80</div><div class=\"line\">81</div><div class=\"line\">82</div><div class=\"line\">83</div><div class=\"line\">84</div><div class=\"line\">85</div><div class=\"line\">86</div><div class=\"line\">87</div><div class=\"line\">88</div><div class=\"line\">89</div><div class=\"line\">90</div><div class=\"line\">91</div><div class=\"line\">92</div><div class=\"line\">93</div><div class=\"line\">94</div><div class=\"line\">95</div><div class=\"line\">96</div><div class=\"line\">97</div><div class=\"line\">98</div><div class=\"line\">99</div><div class=\"line\">100</div><div class=\"line\">101</div><div class=\"line\">102</div><div class=\"line\">103</div><div class=\"line\">104</div><div class=\"line\">105</div><div class=\"line\">106</div><div class=\"line\">107</div><div class=\"line\">108</div><div class=\"line\">109</div><div class=\"line\">110</div><div class=\"line\">111</div><div class=\"line\">112</div><div class=\"line\">113</div><div class=\"line\">114</div><div class=\"line\">115</div><div class=\"line\">116</div><div class=\"line\">117</div><div class=\"line\">118</div><div class=\"line\">119</div><div class=\"line\">120</div><div class=\"line\">121</div><div class=\"line\">122</div><div class=\"line\">123</div><div class=\"line\">124</div><div class=\"line\">125</div><div class=\"line\">126</div><div class=\"line\">127</div><div class=\"line\">128</div><div class=\"line\">129</div><div class=\"line\">130</div><div class=\"line\">131</div><div class=\"line\">132</div><div class=\"line\">133</div><div class=\"line\">134</div><div class=\"line\">135</div><div class=\"line\">136</div><div class=\"line\">137</div><div class=\"line\">138</div><div class=\"line\">139</div><div class=\"line\">140</div><div class=\"line\">141</div><div class=\"line\">142</div><div class=\"line\">143</div><div class=\"line\">144</div><div class=\"line\">145</div><div class=\"line\">146</div><div class=\"line\">147</div><div class=\"line\">148</div><div class=\"line\">149</div><div class=\"line\">150</div><div class=\"line\">151</div><div class=\"line\">152</div><div class=\"line\">153</div><div class=\"line\">154</div><div class=\"line\">155</div><div class=\"line\">156</div><div class=\"line\">157</div><div class=\"line\">158</div><div class=\"line\">159</div><div class=\"line\">160</div><div class=\"line\">161</div><div class=\"line\">162</div><div class=\"line\">163</div><div class=\"line\">164</div><div class=\"line\">165</div><div class=\"line\">166</div><div class=\"line\">167</div><div class=\"line\">168</div><div class=\"line\">169</div><div class=\"line\">170</div><div class=\"line\">171</div><div class=\"line\">172</div><div class=\"line\">173</div><div class=\"line\">174</div><div class=\"line\">175</div><div class=\"line\">176</div><div class=\"line\">177</div><div class=\"line\">178</div><div class=\"line\">179</div><div class=\"line\">180</div><div class=\"line\">181</div><div class=\"line\">182</div><div class=\"line\">183</div><div class=\"line\">184</div><div class=\"line\">185</div><div class=\"line\">186</div><div class=\"line\">187</div><div class=\"line\">188</div><div class=\"line\">189</div><div class=\"line\">190</div><div class=\"line\">191</div><div class=\"line\">192</div><div class=\"line\">193</div><div class=\"line\">194</div><div class=\"line\">195</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">package</span> main</div><div class=\"line\"></div><div class=\"line\"><span class=\"keyword\">import</span> (</div><div class=\"line\">\t<span class=\"string\">\"fmt\"</span></div><div class=\"line\">\t<span class=\"string\">\"sync\"</span></div><div class=\"line\">)</div><div class=\"line\"></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"><span class=\"comment\">// Several solutions to the crawler exercise from the Go tutorial (https://tour.golang.org/concurrency/10)</span></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"><span class=\"comment\">// Serial crawler</span></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">CrawlSerial</span><span class=\"params\">(url <span class=\"keyword\">string</span>, fetcher Fetcher, fetched <span class=\"keyword\">map</span>[<span class=\"keyword\">string</span>]<span class=\"keyword\">bool</span>)</span></span> {</div><div class=\"line\">\t<span class=\"keyword\">if</span> fetched[url] {</div><div class=\"line\">\t\t<span class=\"keyword\">return</span></div><div class=\"line\">\t}</div><div class=\"line\">\tfetched[url] = <span class=\"literal\">true</span></div><div class=\"line\">\tbody, urls, err := fetcher.Fetch(url)</div><div class=\"line\">\t<span class=\"keyword\">if</span> err != <span class=\"literal\">nil</span> {</div><div class=\"line\">\t\tfmt.Println(err)</div><div class=\"line\">\t\t<span class=\"keyword\">return</span></div><div class=\"line\">\t}</div><div class=\"line\">\tfmt.Printf(<span class=\"string\">\"found: %s %q\\n\"</span>, url, body)</div><div class=\"line\">\t<span class=\"keyword\">for</span> _, u := <span class=\"keyword\">range</span> urls {</div><div class=\"line\">\t\tCrawlSerial(u, fetcher, fetched)</div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"keyword\">return</span></div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"><span class=\"comment\">// Concurrent crawler with Mutex and WaitGroup</span></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"></div><div class=\"line\"><span class=\"keyword\">type</span> fetchState <span class=\"keyword\">struct</span> {</div><div class=\"line\">\tmu sync.Mutex</div><div class=\"line\">\tfetched <span class=\"keyword\">map</span>[<span class=\"keyword\">string</span>]<span class=\"keyword\">bool</span></div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(f *fetchState)</span> <span class=\"title\">CheckAndMark</span><span class=\"params\">(url <span class=\"keyword\">string</span>)</span> <span class=\"title\">bool</span></span> {</div><div class=\"line\">\t<span class=\"comment\">//defer f.mu.Unlock()</span></div><div class=\"line\"></div><div class=\"line\">\t<span class=\"comment\">//f.mu.Lock()</span></div><div class=\"line\">\t<span class=\"keyword\">if</span> f.fetched[url] {</div><div class=\"line\">\t\t<span class=\"keyword\">return</span> <span class=\"literal\">true</span></div><div class=\"line\">\t}</div><div class=\"line\">\tf.fetched[url] = <span class=\"literal\">true</span></div><div class=\"line\">\t<span class=\"keyword\">return</span> <span class=\"literal\">false</span></div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">mkFetchState</span><span class=\"params\">()</span> *<span class=\"title\">fetchState</span></span> {</div><div class=\"line\">\tf := &fetchState{}</div><div class=\"line\">\tf.fetched = <span class=\"built_in\">make</span>(<span class=\"keyword\">map</span>[<span class=\"keyword\">string</span>]<span class=\"keyword\">bool</span>)</div><div class=\"line\">\t<span class=\"keyword\">return</span> f</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">CrawlConcurrentMutex</span><span class=\"params\">(url <span class=\"keyword\">string</span>, fetcher Fetcher, f *fetchState)</span></span> {</div><div class=\"line\">\t<span class=\"keyword\">if</span> f.CheckAndMark(url) {</div><div class=\"line\">\t\t<span class=\"keyword\">return</span></div><div class=\"line\">\t}</div><div class=\"line\"></div><div class=\"line\">\tbody, urls, err := fetcher.Fetch(url)</div><div class=\"line\">\t<span class=\"keyword\">if</span> err != <span class=\"literal\">nil</span> {</div><div class=\"line\">\t\tfmt.Println(err)</div><div class=\"line\">\t\t<span class=\"keyword\">return</span></div><div class=\"line\">\t}</div><div class=\"line\">\tfmt.Printf(<span class=\"string\">\"found: %s %q\\n\"</span>, url, body)</div><div class=\"line\">\t<span class=\"keyword\">var</span> done sync.WaitGroup</div><div class=\"line\">\t<span class=\"keyword\">for</span> _, u := <span class=\"keyword\">range</span> urls {</div><div class=\"line\">\t\tdone.Add(<span class=\"number\">1</span>)</div><div class=\"line\">\t\t<span class=\"keyword\">go</span> <span class=\"function\"><span class=\"keyword\">func</span><span class=\"params\">(u <span class=\"keyword\">string</span>)</span></span> {</div><div class=\"line\">\t\t\t<span class=\"keyword\">defer</span> done.Done()</div><div class=\"line\">\t\t\tCrawlConcurrentMutex(u, fetcher, f)</div><div class=\"line\">\t\t}(u) <span class=\"comment\">// Without the u argument there is a race</span></div><div class=\"line\">\t}</div><div class=\"line\">\tdone.Wait()</div><div class=\"line\">\t<span class=\"keyword\">return</span></div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"><span class=\"comment\">// Concurrent crawler with channels</span></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">dofetch</span><span class=\"params\">(url1 <span class=\"keyword\">string</span>, ch <span class=\"keyword\">chan</span> []<span class=\"keyword\">string</span>, fetcher Fetcher)</span></span> {</div><div class=\"line\">\tbody, urls, err := fetcher.Fetch(url1)</div><div class=\"line\">\t<span class=\"keyword\">if</span> err != <span class=\"literal\">nil</span> {</div><div class=\"line\">\t\tfmt.Println(err)</div><div class=\"line\">\t\tch <- []<span class=\"keyword\">string</span>{}</div><div class=\"line\">\t} <span class=\"keyword\">else</span> {</div><div class=\"line\">\t\tfmt.Printf(<span class=\"string\">\"found: %s %q\\n\"</span>, url1, body)</div><div class=\"line\">\t\tch <- urls</div><div class=\"line\">\t}</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">master</span><span class=\"params\">(ch <span class=\"keyword\">chan</span> []<span class=\"keyword\">string</span>, fetcher Fetcher)</span></span> {</div><div class=\"line\">\tn := <span class=\"number\">1</span></div><div class=\"line\">\tfetched := <span class=\"built_in\">make</span>(<span class=\"keyword\">map</span>[<span class=\"keyword\">string</span>]<span class=\"keyword\">bool</span>)</div><div class=\"line\">\t<span class=\"keyword\">for</span> urls := <span class=\"keyword\">range</span> ch {</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> _, u := <span class=\"keyword\">range</span> urls {</div><div class=\"line\">\t\t\t<span class=\"keyword\">if</span> _, ok := fetched[u]; ok == <span class=\"literal\">false</span> {</div><div class=\"line\">\t\t\t\tfetched[u] = <span class=\"literal\">true</span></div><div class=\"line\">\t\t\t\tn += <span class=\"number\">1</span></div><div class=\"line\">\t\t\t\t<span class=\"keyword\">go</span> dofetch(u, ch, fetcher)</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tn -= <span class=\"number\">1</span></div><div class=\"line\">\t\t<span class=\"keyword\">if</span> n == <span class=\"number\">0</span> {</div><div class=\"line\">\t\t\t<span class=\"keyword\">break</span></div><div class=\"line\">\t\t}</div><div class=\"line\">\t}</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">CrawlConcurrentChannel</span><span class=\"params\">(url <span class=\"keyword\">string</span>, fetcher Fetcher)</span></span> {</div><div class=\"line\">\tch := <span class=\"built_in\">make</span>(<span class=\"keyword\">chan</span> []<span class=\"keyword\">string</span>)</div><div class=\"line\">\t<span class=\"keyword\">go</span> <span class=\"function\"><span class=\"keyword\">func</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\t\tch <- []<span class=\"keyword\">string</span>{url}</div><div class=\"line\">\t}()</div><div class=\"line\">\tmaster(ch, fetcher)</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"><span class=\"comment\">// main</span></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">main</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\tfmt.Printf(<span class=\"string\">\"=== Serial===\\n\"</span>)</div><div class=\"line\">\tCrawlSerial(<span class=\"string\">\"http://golang.org/\"</span>, fetcher, <span class=\"built_in\">make</span>(<span class=\"keyword\">map</span>[<span class=\"keyword\">string</span>]<span class=\"keyword\">bool</span>))</div><div class=\"line\"></div><div class=\"line\">\tfmt.Printf(<span class=\"string\">\"=== ConcurrentMutex ===\\n\"</span>)</div><div class=\"line\">\tCrawlConcurrentMutex(<span class=\"string\">\"http://golang.org/\"</span>, fetcher, mkFetchState())</div><div class=\"line\"></div><div class=\"line\">\tfmt.Printf(<span class=\"string\">\"=== ConcurrentChannel ===\\n\"</span>)</div><div class=\"line\">\tCrawlConcurrentChannel(<span class=\"string\">\"http://golang.org/\"</span>, fetcher)</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"><span class=\"comment\">// Fetcher</span></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"></div><div class=\"line\"><span class=\"keyword\">type</span> Fetcher <span class=\"keyword\">interface</span> {</div><div class=\"line\">\t<span class=\"comment\">// Fetch returns the body of URL and</span></div><div class=\"line\">\t<span class=\"comment\">// a slice of URLs found on that page.</span></div><div class=\"line\">\tFetch(url <span class=\"keyword\">string</span>) (body <span class=\"keyword\">string</span>, urls []<span class=\"keyword\">string</span>, err error)</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"comment\">// fakeFetcher is Fetcher that returns canned results.</span></div><div class=\"line\"><span class=\"keyword\">type</span> fakeFetcher <span class=\"keyword\">map</span>[<span class=\"keyword\">string</span>]*fakeResult</div><div class=\"line\"></div><div class=\"line\"><span class=\"keyword\">type</span> fakeResult <span class=\"keyword\">struct</span> {</div><div class=\"line\">\tbody <span class=\"keyword\">string</span></div><div class=\"line\">\turls []<span class=\"keyword\">string</span></div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(f fakeFetcher)</span> <span class=\"title\">Fetch</span><span class=\"params\">(url <span class=\"keyword\">string</span>)</span> <span class=\"params\">(<span class=\"keyword\">string</span>, []<span class=\"keyword\">string</span>, error)</span></span> {</div><div class=\"line\">\t<span class=\"keyword\">if</span> res, ok := f[url]; ok {</div><div class=\"line\">\t\t<span class=\"keyword\">return</span> res.body, res.urls, <span class=\"literal\">nil</span></div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"keyword\">return</span> <span class=\"string\">\"\"</span>, <span class=\"literal\">nil</span>, fmt.Errorf(<span class=\"string\">\"not found: %s\"</span>, url)</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"comment\">// fetcher is a populated fakeFetcher.</span></div><div class=\"line\"><span class=\"keyword\">var</span> fetcher = fakeFetcher{</div><div class=\"line\">\t<span class=\"string\">\"http://golang.org/\"</span>: &fakeResult{</div><div class=\"line\">\t\t<span class=\"string\">\"The Go Programming Language\"</span>,</div><div class=\"line\">\t\t[]<span class=\"keyword\">string</span>{</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/pkg/\"</span>,</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/cmd/\"</span>,</div><div class=\"line\">\t\t},</div><div class=\"line\">\t},</div><div class=\"line\">\t<span class=\"string\">\"http://golang.org/pkg/\"</span>: &fakeResult{</div><div class=\"line\">\t\t<span class=\"string\">\"Packages\"</span>,</div><div class=\"line\">\t\t[]<span class=\"keyword\">string</span>{</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/\"</span>,</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/cmd/\"</span>,</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/pkg/fmt/\"</span>,</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/pkg/os/\"</span>,</div><div class=\"line\">\t\t},</div><div class=\"line\">\t},</div><div class=\"line\">\t<span class=\"string\">\"http://golang.org/pkg/fmt/\"</span>: &fakeResult{</div><div class=\"line\">\t\t<span class=\"string\">\"Package fmt\"</span>,</div><div class=\"line\">\t\t[]<span class=\"keyword\">string</span>{</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/\"</span>,</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/pkg/\"</span>,</div><div class=\"line\">\t\t},</div><div class=\"line\">\t},</div><div class=\"line\">\t<span class=\"string\">\"http://golang.org/pkg/os/\"</span>: &fakeResult{</div><div class=\"line\">\t\t<span class=\"string\">\"Package os\"</span>,</div><div class=\"line\">\t\t[]<span class=\"keyword\">string</span>{</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/\"</span>,</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/pkg/\"</span>,</div><div class=\"line\">\t\t},</div><div class=\"line\">\t},</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h3 id=\"代码解析\"><a href=\"#代码解析\" class=\"headerlink\" title=\"代码解析\"></a>代码解析</h3><h4 id=\"串行的方式爬取内容\"><a href=\"#串行的方式爬取内容\" class=\"headerlink\" title=\"串行的方式爬取内容\"></a>串行的方式爬取内容</h4><ol>\n<li>在<code>CrawlSewrial</code>中首先判断当前这个<code>url</code>是否被爬取过</li>\n<li>若未爬取<code>body, urls, err := fetcher.Feach(url)</code>,打印当前的<code>body</code></li>\n<li>遍历urls,继续爬取内容</li>\n</ol>\n<h4 id=\"使用Mutex并行爬取数据\"><a href=\"#使用Mutex并行爬取数据\" class=\"headerlink\" title=\"使用Mutex并行爬取数据\"></a>使用Mutex并行爬取数据</h4><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">var</span> done sync.WaitGroup</div><div class=\"line\"><span class=\"keyword\">for</span> _, u := <span class=\"keyword\">range</span> urls {</div><div class=\"line\">\tdone.Add(<span class=\"number\">1</span>)</div><div class=\"line\">\t<span class=\"keyword\">go</span> <span class=\"function\"><span class=\"keyword\">func</span><span class=\"params\">(u <span class=\"keyword\">string</span>)</span></span> {</div><div class=\"line\">\t\t<span class=\"keyword\">defer</span> done.Done()</div><div class=\"line\">\t\tCrawlConcurrentMutex(u, fetcher, f)</div><div class=\"line\">\t}(u) <span class=\"comment\">// Without the u argument there is a race</span></div><div class=\"line\">}</div><div class=\"line\">done.Wait()</div></pre></td></tr></table></figure>\n<p>遍历<code>urls</code>,每读取一条就并行的执行,并将任务添加进入<code>var done sync.WaitGroup</code>中<br>在循环外设置<code>done.Wait()</code>等待所有的线程执行完毕后继续执行主线程代码</p>\n<h4 id=\"使用Channel并行爬取数据\"><a href=\"#使用Channel并行爬取数据\" class=\"headerlink\" title=\"使用Channel并行爬取数据\"></a>使用Channel并行爬取数据</h4><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">master</span><span class=\"params\">(ch <span class=\"keyword\">chan</span> []<span class=\"keyword\">string</span>, fetcher Fetcher)</span></span> {</div><div class=\"line\">\tn := <span class=\"number\">1</span></div><div class=\"line\">\tfetched := <span class=\"built_in\">make</span>(<span class=\"keyword\">map</span>[<span class=\"keyword\">string</span>]<span class=\"keyword\">bool</span>)</div><div class=\"line\">\t<span class=\"keyword\">for</span> urls := <span class=\"keyword\">range</span> ch {</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> _, u := <span class=\"keyword\">range</span> urls {</div><div class=\"line\">\t\t\t<span class=\"keyword\">if</span> _, ok := fetched[u]; ok == <span class=\"literal\">false</span> {</div><div class=\"line\">\t\t\t\tfetched[u] = <span class=\"literal\">true</span></div><div class=\"line\">\t\t\t\tn += <span class=\"number\">1</span></div><div class=\"line\">\t\t\t\t<span class=\"keyword\">go</span> dofetch(u, ch, fetcher)</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tn -= <span class=\"number\">1</span></div><div class=\"line\">\t\t<span class=\"keyword\">if</span> n == <span class=\"number\">0</span> {</div><div class=\"line\">\t\t\t<span class=\"keyword\">break</span></div><div class=\"line\">\t\t}</div><div class=\"line\">\t}</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<p>将要爬取的<code>url</code>放入<code>channel</code>中,使用<code>master</code>处理,判断是否已经被访问过<br>若否开辟一个线程去访问,并将新的<code>urls</code>放入<code>channel</code>中 <code>n+1</code><br>读完一个<code>ch</code>中的<code>urls</code>后<code>n-1</code>直到最后<code>n=0</code>退出循环,所有的<code>url</code>都已经访问</p>\n","site":{"data":{}},"excerpt":"","more":"<p>之前在学习MIT6.824的实验中,遇到了大量考虑程序并发性的内容,而go语言的特色之处就是在对并发性的支持上,因此我们来总结一下go语言在并发性方面的编程,希望给自己加深一下理解</p>\n<h2 id=\"goroutinue\"><a href=\"#goroutinue\" class=\"headerlink\" title=\"goroutinue\"></a>goroutinue</h2><p>goroutine在我的理解中就是快速的开辟一个线程执行相应的函数<br><code>go f(x, y, z)</code><br>在新的线程中执行<br><code>f(x, y, z)</code><br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">package</span> main</div><div class=\"line\"><span class=\"keyword\">import</span> (</div><div class=\"line\">\t<span class=\"string\">\"fmt\"</span></div><div class=\"line\">\t<span class=\"string\">\"time\"</span></div><div class=\"line\">)</div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">say</span><span class=\"params\">(s <span class=\"keyword\">string</span>)</span></span> {</div><div class=\"line\">\t<span class=\"keyword\">for</span> i := <span class=\"number\">0</span>; i < <span class=\"number\">5</span>; i++ {</div><div class=\"line\">\t\ttime.Sleep(<span class=\"number\">100</span> * time.Millisecond)</div><div class=\"line\">\t\tfmt.Println(s)</div><div class=\"line\">\t}</div><div class=\"line\">}</div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">main</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\t<span class=\"keyword\">go</span> say(<span class=\"string\">\"world\"</span>)</div><div class=\"line\">\tsay(<span class=\"string\">\"hello\"</span>)</div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<p>执行的结果为<br><img src=\"/2017/10/25/the-concurrency-on-go/all_1.png\" alt=\"all_1.png\" title=\"\"></p>\n<h2 id=\"channel\"><a href=\"#channel\" class=\"headerlink\" title=\"channel\"></a>channel</h2><p>channel在我理解是一个信道,信道中传输的数据类型可以是任意的,在另一端准备好之前,发送端和接收端都会被阻塞,在接收端接收的顺序是<strong>先进先出</strong>,类似于队列。<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div></pre></td><td class=\"code\"><pre><div class=\"line\">ch <- v</div><div class=\"line\">x := <- ch</div></pre></td></tr></table></figure></p>\n<p>这一段代码的意思是在发送方将v送入,在接收端用x去接收,channel在使用前必须创建,<br><code>ch := make(chan int)</code><br>实验的代码如下所示</p>\n<figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">package</span> main</div><div class=\"line\"></div><div class=\"line\"><span class=\"keyword\">import</span> <span class=\"string\">\"fmt\"</span></div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">sum</span><span class=\"params\">(a []<span class=\"keyword\">int</span>, c <span class=\"keyword\">chan</span> <span class=\"keyword\">int</span>)</span></span> {</div><div class=\"line\">\tsum := <span class=\"number\">0</span></div><div class=\"line\">\t<span class=\"keyword\">for</span> _, v := <span class=\"keyword\">range</span> a {</div><div class=\"line\">\t\tsum += v</div><div class=\"line\">\t}</div><div class=\"line\">\tc <- sum <span class=\"comment\">// 将和送入 c</span></div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">main</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\ta := []<span class=\"keyword\">int</span>{<span class=\"number\">7</span>, <span class=\"number\">2</span>, <span class=\"number\">8</span>, <span class=\"number\">-9</span>, <span class=\"number\">4</span>, <span class=\"number\">0</span>}</div><div class=\"line\">\tc := <span class=\"built_in\">make</span>(<span class=\"keyword\">chan</span> <span class=\"keyword\">int</span>)</div><div class=\"line\">\t<span class=\"keyword\">go</span> sum(a[:<span class=\"built_in\">len</span>(a)/<span class=\"number\">2</span>], c)</div><div class=\"line\">\t<span class=\"keyword\">go</span> sum(a[<span class=\"built_in\">len</span>(a)/<span class=\"number\">2</span>:], c)</div><div class=\"line\">\tx := <-c</div><div class=\"line\">\ty := <-c</div><div class=\"line\">\tfmt.Println(x, y, x+y)</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<p>代码的执行结果如下所示:<br><code>17 -5 12</code><br>由于此处使用的channel不带有缓冲,每在channel中放入一个变量,在取走之前<code>c <- sum</code>之前的代码阻塞,直到channel中的变量被取走,程序才会继续执行</p>\n<h2 id=\"带有缓冲的channel\"><a href=\"#带有缓冲的channel\" class=\"headerlink\" title=\"带有缓冲的channel\"></a>带有缓冲的channel</h2><p>带有缓冲的channel在构建时,<code>make()</code>的第二个参数作为缓冲区的大小<br>向缓冲区填充变量的时候,只有当缓冲区满的时候发送数据程序会阻塞,而当缓冲区为空的时候,接收变量的程序会阻塞</p>\n<h2 id=\"range-和-close\"><a href=\"#range-和-close\" class=\"headerlink\" title=\"range 和 close\"></a>range 和 close</h2><p><code>range</code>可以源源不断的从channel中读取数据直到关闭<br><code>close</code>可以关闭channel表示再也没有值被发送了<br><code>v, ok = <- ch</code>可以用来测试channel是否被关闭,若关闭ok的返回值为<code>false</code><br>实验的代码如下所示:</p>\n<figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">package</span> main</div><div class=\"line\"><span class=\"keyword\">import</span> (</div><div class=\"line\">\t<span class=\"string\">\"fmt\"</span></div><div class=\"line\">)</div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">fibonacci</span><span class=\"params\">(n <span class=\"keyword\">int</span>, c <span class=\"keyword\">chan</span> <span class=\"keyword\">int</span>)</span></span> {</div><div class=\"line\">\tx, y := <span class=\"number\">0</span>, <span class=\"number\">1</span></div><div class=\"line\">\t<span class=\"keyword\">for</span> i := <span class=\"number\">0</span>; i < n; i++ {</div><div class=\"line\">\t\tc <- x</div><div class=\"line\">\t\tx, y = y, x+y</div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"built_in\">close</span>(c)</div><div class=\"line\">}</div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">main</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\tc := <span class=\"built_in\">make</span>(<span class=\"keyword\">chan</span> <span class=\"keyword\">int</span>, <span class=\"number\">10</span>)</div><div class=\"line\">\t<span class=\"keyword\">go</span> fibonacci(<span class=\"built_in\">cap</span>(c), c)</div><div class=\"line\">\t<span class=\"keyword\">for</span> i := <span class=\"keyword\">range</span> c {</div><div class=\"line\">\t\tfmt.Println(i)</div><div class=\"line\">\t}</div><div class=\"line\">\t_, ok := <-c</div><div class=\"line\">\tfmt.Print(ok)</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<p>实验中实现的是打印斐波那契数列中的前10个数字,当产生完10个数字并全部放入缓冲区后,程序阻塞,主程序中的<code>range</code>开始读取channel缓存,当读取完毕后,函数<code>fibonacci()</code>关闭了channel,于是<code>ok = false</code></p>\n<p>实验的结果如下所示:</p>\n<img src=\"/2017/10/25/the-concurrency-on-go/test4.png\" alt=\"test4.png\" title=\"\">\n<h2 id=\"select\"><a href=\"#select\" class=\"headerlink\" title=\"select\"></a>select</h2><p><code>select</code>有两种执行情况</p>\n<ol>\n<li>阻塞直到条件分支中的某个分支可以执行时,便会执行这个分支</li>\n<li>当有多个分支符合条件的时候,便会随机选择一个分支执行</li>\n</ol>\n<p>实验的代码如下所示:<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">package</span> main</div><div class=\"line\"></div><div class=\"line\"><span class=\"keyword\">import</span> <span class=\"string\">\"fmt\"</span></div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">fibonacci</span><span class=\"params\">(c, quit <span class=\"keyword\">chan</span> <span class=\"keyword\">int</span>)</span></span> {</div><div class=\"line\">\tx, y := <span class=\"number\">0</span>, <span class=\"number\">1</span></div><div class=\"line\">\t<span class=\"keyword\">for</span> {</div><div class=\"line\">\t\t<span class=\"keyword\">select</span> {</div><div class=\"line\">\t\t<span class=\"keyword\">case</span> c <- x:</div><div class=\"line\">\t\t\tx, y = y, x+y</div><div class=\"line\">\t\t<span class=\"keyword\">case</span> <-quit:</div><div class=\"line\">\t\t\tfmt.Println(<span class=\"string\">\"quit\"</span>)</div><div class=\"line\">\t\t\t<span class=\"keyword\">return</span></div><div class=\"line\">\t\t}</div><div class=\"line\">\t}</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">main</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\tc := <span class=\"built_in\">make</span>(<span class=\"keyword\">chan</span> <span class=\"keyword\">int</span>)</div><div class=\"line\">\tquit := <span class=\"built_in\">make</span>(<span class=\"keyword\">chan</span> <span class=\"keyword\">int</span>)</div><div class=\"line\">\t<span class=\"keyword\">go</span> <span class=\"function\"><span class=\"keyword\">func</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> i := <span class=\"number\">0</span>; i < <span class=\"number\">10</span>; i++ {</div><div class=\"line\">\t\t\tfmt.Println(<-c)</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tquit <- <span class=\"number\">0</span></div><div class=\"line\">\t}()</div><div class=\"line\">\tfibonacci(c, quit)</div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<p>实验的结果如下图所示:<br><img src=\"/2017/10/25/the-concurrency-on-go/test5.png\" alt=\"test5.png\" title=\"\"></p>\n<h3 id=\"结果解析\"><a href=\"#结果解析\" class=\"headerlink\" title=\"结果解析\"></a>结果解析</h3><ol>\n<li>序先执行至<code>fmt.Println(<-c)</code>时阻塞,接收程序缓冲区为空</li>\n<li>在<code>fibonacci()</code>函数中,<code>select</code>语句智能选择往channel c中放入数据,由于<code>quit</code>为空,一直阻塞</li>\n<li>当打印完成结果后,<code>quit <- 0</code>执行,<code>select</code>可以执行<code>case <- quit</code>之后的代码,打印<code>quit</code>并返回主程序</li>\n</ol>\n<h3 id=\"默认选择\"><a href=\"#默认选择\" class=\"headerlink\" title=\"默认选择\"></a>默认选择</h3><p>当<code>select</code>中的其他条件分支都没有准备好的时候<code>default</code>分支会被执行<br>通常用作非阻塞的发送或接收程序</p>\n<h2 id=\"sync-Mutex\"><a href=\"#sync-Mutex\" class=\"headerlink\" title=\"sync.Mutex\"></a>sync.Mutex</h2><p>当我们不需要在goroutine中通信时,我们可以使用互斥锁在使用<strong>共享</strong>资源时进行封锁,使用结束后进行解锁<br>go库中提供了<code>sync.Mutex</code>类型以及两个方法:<br><code>Lock()</code><br><code>Unlock()</code><br>参考的代码如下所示:</p>\n<figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(c *SafeCounter)</span> <span class=\"title\">Inc</span><span class=\"params\">(key <span class=\"keyword\">string</span>)</span></span> {</div><div class=\"line\">\tc.mux.Lock()</div><div class=\"line\">\t<span class=\"comment\">// Lock 之后同一时刻只有一个 goroutine 能访问 c.v</span></div><div class=\"line\">\tc.v[key]++</div><div class=\"line\">\tc.mux.Unlock()</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<p>在增加<code>c.v[key]</code>之前先对其进行封锁,使用结束后解锁,而在定义这个可以封锁的资源时,我们采用了结构体的方式,将这个互斥锁<strong>绑定</strong>在资源上<br><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">type</span> SafeCounter <span class=\"keyword\">struct</span> {</div><div class=\"line\">\tv <span class=\"keyword\">map</span>[<span class=\"keyword\">string</span>]<span class=\"keyword\">int</span></div><div class=\"line\">\tmux sync.Mutex</div><div class=\"line\">}</div></pre></td></tr></table></figure></p>\n<h2 id=\"多种方式的crawer\"><a href=\"#多种方式的crawer\" class=\"headerlink\" title=\"多种方式的crawer\"></a>多种方式的crawer</h2><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div><div class=\"line\">18</div><div class=\"line\">19</div><div class=\"line\">20</div><div class=\"line\">21</div><div class=\"line\">22</div><div class=\"line\">23</div><div class=\"line\">24</div><div class=\"line\">25</div><div class=\"line\">26</div><div class=\"line\">27</div><div class=\"line\">28</div><div class=\"line\">29</div><div class=\"line\">30</div><div class=\"line\">31</div><div class=\"line\">32</div><div class=\"line\">33</div><div class=\"line\">34</div><div class=\"line\">35</div><div class=\"line\">36</div><div class=\"line\">37</div><div class=\"line\">38</div><div class=\"line\">39</div><div class=\"line\">40</div><div class=\"line\">41</div><div class=\"line\">42</div><div class=\"line\">43</div><div class=\"line\">44</div><div class=\"line\">45</div><div class=\"line\">46</div><div class=\"line\">47</div><div class=\"line\">48</div><div class=\"line\">49</div><div class=\"line\">50</div><div class=\"line\">51</div><div class=\"line\">52</div><div class=\"line\">53</div><div class=\"line\">54</div><div class=\"line\">55</div><div class=\"line\">56</div><div class=\"line\">57</div><div class=\"line\">58</div><div class=\"line\">59</div><div class=\"line\">60</div><div class=\"line\">61</div><div class=\"line\">62</div><div class=\"line\">63</div><div class=\"line\">64</div><div class=\"line\">65</div><div class=\"line\">66</div><div class=\"line\">67</div><div class=\"line\">68</div><div class=\"line\">69</div><div class=\"line\">70</div><div class=\"line\">71</div><div class=\"line\">72</div><div class=\"line\">73</div><div class=\"line\">74</div><div class=\"line\">75</div><div class=\"line\">76</div><div class=\"line\">77</div><div class=\"line\">78</div><div class=\"line\">79</div><div class=\"line\">80</div><div class=\"line\">81</div><div class=\"line\">82</div><div class=\"line\">83</div><div class=\"line\">84</div><div class=\"line\">85</div><div class=\"line\">86</div><div class=\"line\">87</div><div class=\"line\">88</div><div class=\"line\">89</div><div class=\"line\">90</div><div class=\"line\">91</div><div class=\"line\">92</div><div class=\"line\">93</div><div class=\"line\">94</div><div class=\"line\">95</div><div class=\"line\">96</div><div class=\"line\">97</div><div class=\"line\">98</div><div class=\"line\">99</div><div class=\"line\">100</div><div class=\"line\">101</div><div class=\"line\">102</div><div class=\"line\">103</div><div class=\"line\">104</div><div class=\"line\">105</div><div class=\"line\">106</div><div class=\"line\">107</div><div class=\"line\">108</div><div class=\"line\">109</div><div class=\"line\">110</div><div class=\"line\">111</div><div class=\"line\">112</div><div class=\"line\">113</div><div class=\"line\">114</div><div class=\"line\">115</div><div class=\"line\">116</div><div class=\"line\">117</div><div class=\"line\">118</div><div class=\"line\">119</div><div class=\"line\">120</div><div class=\"line\">121</div><div class=\"line\">122</div><div class=\"line\">123</div><div class=\"line\">124</div><div class=\"line\">125</div><div class=\"line\">126</div><div class=\"line\">127</div><div class=\"line\">128</div><div class=\"line\">129</div><div class=\"line\">130</div><div class=\"line\">131</div><div class=\"line\">132</div><div class=\"line\">133</div><div class=\"line\">134</div><div class=\"line\">135</div><div class=\"line\">136</div><div class=\"line\">137</div><div class=\"line\">138</div><div class=\"line\">139</div><div class=\"line\">140</div><div class=\"line\">141</div><div class=\"line\">142</div><div class=\"line\">143</div><div class=\"line\">144</div><div class=\"line\">145</div><div class=\"line\">146</div><div class=\"line\">147</div><div class=\"line\">148</div><div class=\"line\">149</div><div class=\"line\">150</div><div class=\"line\">151</div><div class=\"line\">152</div><div class=\"line\">153</div><div class=\"line\">154</div><div class=\"line\">155</div><div class=\"line\">156</div><div class=\"line\">157</div><div class=\"line\">158</div><div class=\"line\">159</div><div class=\"line\">160</div><div class=\"line\">161</div><div class=\"line\">162</div><div class=\"line\">163</div><div class=\"line\">164</div><div class=\"line\">165</div><div class=\"line\">166</div><div class=\"line\">167</div><div class=\"line\">168</div><div class=\"line\">169</div><div class=\"line\">170</div><div class=\"line\">171</div><div class=\"line\">172</div><div class=\"line\">173</div><div class=\"line\">174</div><div class=\"line\">175</div><div class=\"line\">176</div><div class=\"line\">177</div><div class=\"line\">178</div><div class=\"line\">179</div><div class=\"line\">180</div><div class=\"line\">181</div><div class=\"line\">182</div><div class=\"line\">183</div><div class=\"line\">184</div><div class=\"line\">185</div><div class=\"line\">186</div><div class=\"line\">187</div><div class=\"line\">188</div><div class=\"line\">189</div><div class=\"line\">190</div><div class=\"line\">191</div><div class=\"line\">192</div><div class=\"line\">193</div><div class=\"line\">194</div><div class=\"line\">195</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">package</span> main</div><div class=\"line\"></div><div class=\"line\"><span class=\"keyword\">import</span> (</div><div class=\"line\">\t<span class=\"string\">\"fmt\"</span></div><div class=\"line\">\t<span class=\"string\">\"sync\"</span></div><div class=\"line\">)</div><div class=\"line\"></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"><span class=\"comment\">// Several solutions to the crawler exercise from the Go tutorial (https://tour.golang.org/concurrency/10)</span></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"><span class=\"comment\">// Serial crawler</span></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">CrawlSerial</span><span class=\"params\">(url <span class=\"keyword\">string</span>, fetcher Fetcher, fetched <span class=\"keyword\">map</span>[<span class=\"keyword\">string</span>]<span class=\"keyword\">bool</span>)</span></span> {</div><div class=\"line\">\t<span class=\"keyword\">if</span> fetched[url] {</div><div class=\"line\">\t\t<span class=\"keyword\">return</span></div><div class=\"line\">\t}</div><div class=\"line\">\tfetched[url] = <span class=\"literal\">true</span></div><div class=\"line\">\tbody, urls, err := fetcher.Fetch(url)</div><div class=\"line\">\t<span class=\"keyword\">if</span> err != <span class=\"literal\">nil</span> {</div><div class=\"line\">\t\tfmt.Println(err)</div><div class=\"line\">\t\t<span class=\"keyword\">return</span></div><div class=\"line\">\t}</div><div class=\"line\">\tfmt.Printf(<span class=\"string\">\"found: %s %q\\n\"</span>, url, body)</div><div class=\"line\">\t<span class=\"keyword\">for</span> _, u := <span class=\"keyword\">range</span> urls {</div><div class=\"line\">\t\tCrawlSerial(u, fetcher, fetched)</div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"keyword\">return</span></div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"><span class=\"comment\">// Concurrent crawler with Mutex and WaitGroup</span></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"></div><div class=\"line\"><span class=\"keyword\">type</span> fetchState <span class=\"keyword\">struct</span> {</div><div class=\"line\">\tmu sync.Mutex</div><div class=\"line\">\tfetched <span class=\"keyword\">map</span>[<span class=\"keyword\">string</span>]<span class=\"keyword\">bool</span></div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(f *fetchState)</span> <span class=\"title\">CheckAndMark</span><span class=\"params\">(url <span class=\"keyword\">string</span>)</span> <span class=\"title\">bool</span></span> {</div><div class=\"line\">\t<span class=\"comment\">//defer f.mu.Unlock()</span></div><div class=\"line\"></div><div class=\"line\">\t<span class=\"comment\">//f.mu.Lock()</span></div><div class=\"line\">\t<span class=\"keyword\">if</span> f.fetched[url] {</div><div class=\"line\">\t\t<span class=\"keyword\">return</span> <span class=\"literal\">true</span></div><div class=\"line\">\t}</div><div class=\"line\">\tf.fetched[url] = <span class=\"literal\">true</span></div><div class=\"line\">\t<span class=\"keyword\">return</span> <span class=\"literal\">false</span></div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">mkFetchState</span><span class=\"params\">()</span> *<span class=\"title\">fetchState</span></span> {</div><div class=\"line\">\tf := &fetchState{}</div><div class=\"line\">\tf.fetched = <span class=\"built_in\">make</span>(<span class=\"keyword\">map</span>[<span class=\"keyword\">string</span>]<span class=\"keyword\">bool</span>)</div><div class=\"line\">\t<span class=\"keyword\">return</span> f</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">CrawlConcurrentMutex</span><span class=\"params\">(url <span class=\"keyword\">string</span>, fetcher Fetcher, f *fetchState)</span></span> {</div><div class=\"line\">\t<span class=\"keyword\">if</span> f.CheckAndMark(url) {</div><div class=\"line\">\t\t<span class=\"keyword\">return</span></div><div class=\"line\">\t}</div><div class=\"line\"></div><div class=\"line\">\tbody, urls, err := fetcher.Fetch(url)</div><div class=\"line\">\t<span class=\"keyword\">if</span> err != <span class=\"literal\">nil</span> {</div><div class=\"line\">\t\tfmt.Println(err)</div><div class=\"line\">\t\t<span class=\"keyword\">return</span></div><div class=\"line\">\t}</div><div class=\"line\">\tfmt.Printf(<span class=\"string\">\"found: %s %q\\n\"</span>, url, body)</div><div class=\"line\">\t<span class=\"keyword\">var</span> done sync.WaitGroup</div><div class=\"line\">\t<span class=\"keyword\">for</span> _, u := <span class=\"keyword\">range</span> urls {</div><div class=\"line\">\t\tdone.Add(<span class=\"number\">1</span>)</div><div class=\"line\">\t\t<span class=\"keyword\">go</span> <span class=\"function\"><span class=\"keyword\">func</span><span class=\"params\">(u <span class=\"keyword\">string</span>)</span></span> {</div><div class=\"line\">\t\t\t<span class=\"keyword\">defer</span> done.Done()</div><div class=\"line\">\t\t\tCrawlConcurrentMutex(u, fetcher, f)</div><div class=\"line\">\t\t}(u) <span class=\"comment\">// Without the u argument there is a race</span></div><div class=\"line\">\t}</div><div class=\"line\">\tdone.Wait()</div><div class=\"line\">\t<span class=\"keyword\">return</span></div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"><span class=\"comment\">// Concurrent crawler with channels</span></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">dofetch</span><span class=\"params\">(url1 <span class=\"keyword\">string</span>, ch <span class=\"keyword\">chan</span> []<span class=\"keyword\">string</span>, fetcher Fetcher)</span></span> {</div><div class=\"line\">\tbody, urls, err := fetcher.Fetch(url1)</div><div class=\"line\">\t<span class=\"keyword\">if</span> err != <span class=\"literal\">nil</span> {</div><div class=\"line\">\t\tfmt.Println(err)</div><div class=\"line\">\t\tch <- []<span class=\"keyword\">string</span>{}</div><div class=\"line\">\t} <span class=\"keyword\">else</span> {</div><div class=\"line\">\t\tfmt.Printf(<span class=\"string\">\"found: %s %q\\n\"</span>, url1, body)</div><div class=\"line\">\t\tch <- urls</div><div class=\"line\">\t}</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">master</span><span class=\"params\">(ch <span class=\"keyword\">chan</span> []<span class=\"keyword\">string</span>, fetcher Fetcher)</span></span> {</div><div class=\"line\">\tn := <span class=\"number\">1</span></div><div class=\"line\">\tfetched := <span class=\"built_in\">make</span>(<span class=\"keyword\">map</span>[<span class=\"keyword\">string</span>]<span class=\"keyword\">bool</span>)</div><div class=\"line\">\t<span class=\"keyword\">for</span> urls := <span class=\"keyword\">range</span> ch {</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> _, u := <span class=\"keyword\">range</span> urls {</div><div class=\"line\">\t\t\t<span class=\"keyword\">if</span> _, ok := fetched[u]; ok == <span class=\"literal\">false</span> {</div><div class=\"line\">\t\t\t\tfetched[u] = <span class=\"literal\">true</span></div><div class=\"line\">\t\t\t\tn += <span class=\"number\">1</span></div><div class=\"line\">\t\t\t\t<span class=\"keyword\">go</span> dofetch(u, ch, fetcher)</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tn -= <span class=\"number\">1</span></div><div class=\"line\">\t\t<span class=\"keyword\">if</span> n == <span class=\"number\">0</span> {</div><div class=\"line\">\t\t\t<span class=\"keyword\">break</span></div><div class=\"line\">\t\t}</div><div class=\"line\">\t}</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">CrawlConcurrentChannel</span><span class=\"params\">(url <span class=\"keyword\">string</span>, fetcher Fetcher)</span></span> {</div><div class=\"line\">\tch := <span class=\"built_in\">make</span>(<span class=\"keyword\">chan</span> []<span class=\"keyword\">string</span>)</div><div class=\"line\">\t<span class=\"keyword\">go</span> <span class=\"function\"><span class=\"keyword\">func</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\t\tch <- []<span class=\"keyword\">string</span>{url}</div><div class=\"line\">\t}()</div><div class=\"line\">\tmaster(ch, fetcher)</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"><span class=\"comment\">// main</span></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">main</span><span class=\"params\">()</span></span> {</div><div class=\"line\">\tfmt.Printf(<span class=\"string\">\"=== Serial===\\n\"</span>)</div><div class=\"line\">\tCrawlSerial(<span class=\"string\">\"http://golang.org/\"</span>, fetcher, <span class=\"built_in\">make</span>(<span class=\"keyword\">map</span>[<span class=\"keyword\">string</span>]<span class=\"keyword\">bool</span>))</div><div class=\"line\"></div><div class=\"line\">\tfmt.Printf(<span class=\"string\">\"=== ConcurrentMutex ===\\n\"</span>)</div><div class=\"line\">\tCrawlConcurrentMutex(<span class=\"string\">\"http://golang.org/\"</span>, fetcher, mkFetchState())</div><div class=\"line\"></div><div class=\"line\">\tfmt.Printf(<span class=\"string\">\"=== ConcurrentChannel ===\\n\"</span>)</div><div class=\"line\">\tCrawlConcurrentChannel(<span class=\"string\">\"http://golang.org/\"</span>, fetcher)</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"><span class=\"comment\">// Fetcher</span></div><div class=\"line\"><span class=\"comment\">//</span></div><div class=\"line\"></div><div class=\"line\"><span class=\"keyword\">type</span> Fetcher <span class=\"keyword\">interface</span> {</div><div class=\"line\">\t<span class=\"comment\">// Fetch returns the body of URL and</span></div><div class=\"line\">\t<span class=\"comment\">// a slice of URLs found on that page.</span></div><div class=\"line\">\tFetch(url <span class=\"keyword\">string</span>) (body <span class=\"keyword\">string</span>, urls []<span class=\"keyword\">string</span>, err error)</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"comment\">// fakeFetcher is Fetcher that returns canned results.</span></div><div class=\"line\"><span class=\"keyword\">type</span> fakeFetcher <span class=\"keyword\">map</span>[<span class=\"keyword\">string</span>]*fakeResult</div><div class=\"line\"></div><div class=\"line\"><span class=\"keyword\">type</span> fakeResult <span class=\"keyword\">struct</span> {</div><div class=\"line\">\tbody <span class=\"keyword\">string</span></div><div class=\"line\">\turls []<span class=\"keyword\">string</span></div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"params\">(f fakeFetcher)</span> <span class=\"title\">Fetch</span><span class=\"params\">(url <span class=\"keyword\">string</span>)</span> <span class=\"params\">(<span class=\"keyword\">string</span>, []<span class=\"keyword\">string</span>, error)</span></span> {</div><div class=\"line\">\t<span class=\"keyword\">if</span> res, ok := f[url]; ok {</div><div class=\"line\">\t\t<span class=\"keyword\">return</span> res.body, res.urls, <span class=\"literal\">nil</span></div><div class=\"line\">\t}</div><div class=\"line\">\t<span class=\"keyword\">return</span> <span class=\"string\">\"\"</span>, <span class=\"literal\">nil</span>, fmt.Errorf(<span class=\"string\">\"not found: %s\"</span>, url)</div><div class=\"line\">}</div><div class=\"line\"></div><div class=\"line\"><span class=\"comment\">// fetcher is a populated fakeFetcher.</span></div><div class=\"line\"><span class=\"keyword\">var</span> fetcher = fakeFetcher{</div><div class=\"line\">\t<span class=\"string\">\"http://golang.org/\"</span>: &fakeResult{</div><div class=\"line\">\t\t<span class=\"string\">\"The Go Programming Language\"</span>,</div><div class=\"line\">\t\t[]<span class=\"keyword\">string</span>{</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/pkg/\"</span>,</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/cmd/\"</span>,</div><div class=\"line\">\t\t},</div><div class=\"line\">\t},</div><div class=\"line\">\t<span class=\"string\">\"http://golang.org/pkg/\"</span>: &fakeResult{</div><div class=\"line\">\t\t<span class=\"string\">\"Packages\"</span>,</div><div class=\"line\">\t\t[]<span class=\"keyword\">string</span>{</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/\"</span>,</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/cmd/\"</span>,</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/pkg/fmt/\"</span>,</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/pkg/os/\"</span>,</div><div class=\"line\">\t\t},</div><div class=\"line\">\t},</div><div class=\"line\">\t<span class=\"string\">\"http://golang.org/pkg/fmt/\"</span>: &fakeResult{</div><div class=\"line\">\t\t<span class=\"string\">\"Package fmt\"</span>,</div><div class=\"line\">\t\t[]<span class=\"keyword\">string</span>{</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/\"</span>,</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/pkg/\"</span>,</div><div class=\"line\">\t\t},</div><div class=\"line\">\t},</div><div class=\"line\">\t<span class=\"string\">\"http://golang.org/pkg/os/\"</span>: &fakeResult{</div><div class=\"line\">\t\t<span class=\"string\">\"Package os\"</span>,</div><div class=\"line\">\t\t[]<span class=\"keyword\">string</span>{</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/\"</span>,</div><div class=\"line\">\t\t\t<span class=\"string\">\"http://golang.org/pkg/\"</span>,</div><div class=\"line\">\t\t},</div><div class=\"line\">\t},</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<h3 id=\"代码解析\"><a href=\"#代码解析\" class=\"headerlink\" title=\"代码解析\"></a>代码解析</h3><h4 id=\"串行的方式爬取内容\"><a href=\"#串行的方式爬取内容\" class=\"headerlink\" title=\"串行的方式爬取内容\"></a>串行的方式爬取内容</h4><ol>\n<li>在<code>CrawlSewrial</code>中首先判断当前这个<code>url</code>是否被爬取过</li>\n<li>若未爬取<code>body, urls, err := fetcher.Feach(url)</code>,打印当前的<code>body</code></li>\n<li>遍历urls,继续爬取内容</li>\n</ol>\n<h4 id=\"使用Mutex并行爬取数据\"><a href=\"#使用Mutex并行爬取数据\" class=\"headerlink\" title=\"使用Mutex并行爬取数据\"></a>使用Mutex并行爬取数据</h4><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"keyword\">var</span> done sync.WaitGroup</div><div class=\"line\"><span class=\"keyword\">for</span> _, u := <span class=\"keyword\">range</span> urls {</div><div class=\"line\">\tdone.Add(<span class=\"number\">1</span>)</div><div class=\"line\">\t<span class=\"keyword\">go</span> <span class=\"function\"><span class=\"keyword\">func</span><span class=\"params\">(u <span class=\"keyword\">string</span>)</span></span> {</div><div class=\"line\">\t\t<span class=\"keyword\">defer</span> done.Done()</div><div class=\"line\">\t\tCrawlConcurrentMutex(u, fetcher, f)</div><div class=\"line\">\t}(u) <span class=\"comment\">// Without the u argument there is a race</span></div><div class=\"line\">}</div><div class=\"line\">done.Wait()</div></pre></td></tr></table></figure>\n<p>遍历<code>urls</code>,每读取一条就并行的执行,并将任务添加进入<code>var done sync.WaitGroup</code>中<br>在循环外设置<code>done.Wait()</code>等待所有的线程执行完毕后继续执行主线程代码</p>\n<h4 id=\"使用Channel并行爬取数据\"><a href=\"#使用Channel并行爬取数据\" class=\"headerlink\" title=\"使用Channel并行爬取数据\"></a>使用Channel并行爬取数据</h4><figure class=\"highlight go\"><table><tr><td class=\"gutter\"><pre><div class=\"line\">1</div><div class=\"line\">2</div><div class=\"line\">3</div><div class=\"line\">4</div><div class=\"line\">5</div><div class=\"line\">6</div><div class=\"line\">7</div><div class=\"line\">8</div><div class=\"line\">9</div><div class=\"line\">10</div><div class=\"line\">11</div><div class=\"line\">12</div><div class=\"line\">13</div><div class=\"line\">14</div><div class=\"line\">15</div><div class=\"line\">16</div><div class=\"line\">17</div></pre></td><td class=\"code\"><pre><div class=\"line\"><span class=\"function\"><span class=\"keyword\">func</span> <span class=\"title\">master</span><span class=\"params\">(ch <span class=\"keyword\">chan</span> []<span class=\"keyword\">string</span>, fetcher Fetcher)</span></span> {</div><div class=\"line\">\tn := <span class=\"number\">1</span></div><div class=\"line\">\tfetched := <span class=\"built_in\">make</span>(<span class=\"keyword\">map</span>[<span class=\"keyword\">string</span>]<span class=\"keyword\">bool</span>)</div><div class=\"line\">\t<span class=\"keyword\">for</span> urls := <span class=\"keyword\">range</span> ch {</div><div class=\"line\">\t\t<span class=\"keyword\">for</span> _, u := <span class=\"keyword\">range</span> urls {</div><div class=\"line\">\t\t\t<span class=\"keyword\">if</span> _, ok := fetched[u]; ok == <span class=\"literal\">false</span> {</div><div class=\"line\">\t\t\t\tfetched[u] = <span class=\"literal\">true</span></div><div class=\"line\">\t\t\t\tn += <span class=\"number\">1</span></div><div class=\"line\">\t\t\t\t<span class=\"keyword\">go</span> dofetch(u, ch, fetcher)</div><div class=\"line\">\t\t\t}</div><div class=\"line\">\t\t}</div><div class=\"line\">\t\tn -= <span class=\"number\">1</span></div><div class=\"line\">\t\t<span class=\"keyword\">if</span> n == <span class=\"number\">0</span> {</div><div class=\"line\">\t\t\t<span class=\"keyword\">break</span></div><div class=\"line\">\t\t}</div><div class=\"line\">\t}</div><div class=\"line\">}</div></pre></td></tr></table></figure>\n<p>将要爬取的<code>url</code>放入<code>channel</code>中,使用<code>master</code>处理,判断是否已经被访问过<br>若否开辟一个线程去访问,并将新的<code>urls</code>放入<code>channel</code>中 <code>n+1</code><br>读完一个<code>ch</code>中的<code>urls</code>后<code>n-1</code>直到最后<code>n=0</code>退出循环,所有的<code>url</code>都已经访问</p>\n"}],"PostAsset":[{"_id":"source/_posts/ZooKeeper/service.png","post":"cjyohfvd00003sy0yyfpxwp4f","slug":"service.png","modified":1,"renderable":1},{"_id":"source/_posts/ZooKeeper/zknamespace.jpg","post":"cjyohfvd00003sy0yyfpxwp4f","slug":"zknamespace.jpg","modified":1,"renderable":1},{"_id":"source/_posts/MapReduce/allcondition.png","post":"cjyohfvcw0001sy0yqj958sne","slug":"allcondition.png","modified":1,"renderable":1},{"_id":"source/_posts/MapReduce/excution.png","post":"cjyohfvcw0001sy0yqj958sne","slug":"excution.png","modified":1,"renderable":1},{"_id":"source/_posts/MapReduce/transfer.png","post":"cjyohfvcw0001sy0yqj958sne","slug":"transfer.png","modified":1,"renderable":1},{"_id":"source/_posts/Raft-PartA/election.png","post":"cjyohfve0000osy0ygzy3b03c","slug":"election.png","modified":1,"renderable":1},{"_id":"source/_posts/Raft-PartA/raft.png","post":"cjyohfve0000osy0ygzy3b03c","slug":"raft.png","modified":1,"renderable":1},{"_id":"source/_posts/the-concurrency-on-go/all_1.png","post":"cjyohfve0000rsy0y4mf0paqq","slug":"all_1.png","modified":1,"renderable":1},{"_id":"source/_posts/the-concurrency-on-go/no_sleep_1.png","post":"cjyohfve0000rsy0y4mf0paqq","slug":"no_sleep_1.png","modified":1,"renderable":1},{"_id":"source/_posts/the-concurrency-on-go/test4.png","post":"cjyohfve0000rsy0y4mf0paqq","slug":"test4.png","modified":1,"renderable":1},{"_id":"source/_posts/the-concurrency-on-go/test5.png","post":"cjyohfve0000rsy0y4mf0paqq","slug":"test5.png","modified":1,"renderable":1}],"PostCategory":[{"post_id":"cjyohfvcw0001sy0yqj958sne","category_id":"cjyohfvd30004sy0yntd1yvpc","_id":"cjyohfvdc000hsy0yuj0ssfjr"},{"post_id":"cjyohfvcw0001sy0yqj958sne","category_id":"cjyohfvdb000dsy0ye1hbyfmr","_id":"cjyohfvdc000isy0ysooh85yq"},{"post_id":"cjyohfvd00003sy0yyfpxwp4f","category_id":"cjyohfvd30004sy0yntd1yvpc","_id":"cjyohfvdd000jsy0y0tsal5cs"},{"post_id":"cjyohfvd00003sy0yyfpxwp4f","category_id":"cjyohfvdb000dsy0ye1hbyfmr","_id":"cjyohfvde000ksy0yxua6atqp"},{"post_id":"cjyohfvd40006sy0yyxsxrl58","category_id":"cjyohfvd30004sy0yntd1yvpc","_id":"cjyohfvde000lsy0yj787xir1"},{"post_id":"cjyohfvd40006sy0yyxsxrl58","category_id":"cjyohfvdc000gsy0y2kr2pv78","_id":"cjyohfvde000msy0yel08deia"},{"post_id":"cjyohfvdz000nsy0yo34ehy88","category_id":"cjyohfve0000psy0ys3e6rq9n","_id":"cjyohfve2000vsy0ywkqzqhyb"},{"post_id":"cjyohfve0000osy0ygzy3b03c","category_id":"cjyohfvd30004sy0yntd1yvpc","_id":"cjyohfve2000ysy0ysidvuxdb"},{"post_id":"cjyohfve0000osy0ygzy3b03c","category_id":"cjyohfve1000ssy0yjqd52juo","_id":"cjyohfve30010sy0yr75vlmus"},{"post_id":"cjyohfve0000rsy0y4mf0paqq","category_id":"cjyohfve2000wsy0ygx6is5uk","_id":"cjyohfve50013sy0ypymnt9pd"}],"PostTag":[{"post_id":"cjyohfvcw0001sy0yqj958sne","tag_id":"cjyohfvd40005sy0y2pbow58b","_id":"cjyohfvd90009sy0y532zptfb"},{"post_id":"cjyohfvd00003sy0yyfpxwp4f","tag_id":"cjyohfvd40005sy0y2pbow58b","_id":"cjyohfvdb000csy0y34h3fstn"},{"post_id":"cjyohfvd40006sy0yyxsxrl58","tag_id":"cjyohfvda000bsy0yxw2kqkpl","_id":"cjyohfvdb000esy0y070rdlxp"},{"post_id":"cjyohfvdz000nsy0yo34ehy88","tag_id":"cjyohfve0000qsy0yp4ptgdbj","_id":"cjyohfve1000usy0y0mtc6qf6"},{"post_id":"cjyohfve0000osy0ygzy3b03c","tag_id":"cjyohfve1000tsy0y9nfle2c2","_id":"cjyohfve30011sy0yzpqm9mw9"},{"post_id":"cjyohfve0000osy0ygzy3b03c","tag_id":"cjyohfve2000xsy0y4jrylyt2","_id":"cjyohfve30012sy0yfcq4fp44"},{"post_id":"cjyohfve0000rsy0y4mf0paqq","tag_id":"cjyohfve2000zsy0yh3k0v67f","_id":"cjyohfve50014sy0y13chfe4v"}],"Tag":[{"name":"mapreduce","_id":"cjyohfvd40005sy0y2pbow58b"},{"name":"秋招","_id":"cjyohfvda000bsy0yxw2kqkpl"},{"name":"LeetCode","_id":"cjyohfve0000qsy0yp4ptgdbj"},{"name":"raft","_id":"cjyohfve1000tsy0y9nfle2c2"},{"name":"golang","_id":"cjyohfve2000xsy0y4jrylyt2"},{"name":"concurrency","_id":"cjyohfve2000zsy0yh3k0v67f"}]}}