From e5162ed71b4f526a889df58887e5cd9016556187 Mon Sep 17 00:00:00 2001 From: zombieyang Date: Fri, 29 Nov 2019 13:01:34 +0800 Subject: [PATCH] chapter5 --- .gitignore | 2 - chapter5/backend/article.js | 9 + chapter5/backend/comment-list.js | 21 + chapter5/backend/comment-praise.js | 32 + chapter5/backend/detail.js | 28 + chapter5/backend/lib/geeknode-rpc-server.js | 36 + chapter5/backend/lib/rpc-server.js | 131 + chapter5/backend/mockdata/column.js | 144 + chapter5/backend/mockdata/comment.js | 32 + chapter5/backend/proto/detail.proto | 28 + chapter5/backend/run.js | 5 + chapter5/business/play/data.js | 145 + chapter5/business/play/template.tpl | 2721 +++++++++++++++++++ chapter5/package.json | 22 + chapter5/readme.md | 8 +- chapter5/server/create-template.js | 17 + chapter5/server/request-factory.js | 46 + chapter5/server/requestors/geek-rpc.js | 62 + chapter5/server/requestors/http.js | 14 + chapter5/server/run.js | 54 + chapter5/server/test.js | 29 + chapter5/workspace/build.js | 7 + chapter5/workspace/src/page.data.js | 35 + chapter5/workspace/src/play.template.html | 2721 +++++++++++++++++++ chapter5/workspace/src/proto/detail.proto | 28 + chapter5/workspace/uploader.js | 43 + lottery/index.js | 39 + 27 files changed, 6454 insertions(+), 5 deletions(-) create mode 100644 chapter5/backend/article.js create mode 100644 chapter5/backend/comment-list.js create mode 100644 chapter5/backend/comment-praise.js create mode 100644 chapter5/backend/detail.js create mode 100644 chapter5/backend/lib/geeknode-rpc-server.js create mode 100644 chapter5/backend/lib/rpc-server.js create mode 100644 chapter5/backend/mockdata/column.js create mode 100644 chapter5/backend/mockdata/comment.js create mode 100644 chapter5/backend/proto/detail.proto create mode 100644 chapter5/backend/run.js create mode 100644 chapter5/business/play/data.js create mode 100644 chapter5/business/play/template.tpl create mode 100644 chapter5/package.json create mode 100644 chapter5/server/create-template.js create mode 100644 chapter5/server/request-factory.js create mode 100644 chapter5/server/requestors/geek-rpc.js create mode 100644 chapter5/server/requestors/http.js create mode 100644 chapter5/server/run.js create mode 100644 chapter5/server/test.js create mode 100644 chapter5/workspace/build.js create mode 100644 chapter5/workspace/src/page.data.js create mode 100755 chapter5/workspace/src/play.template.html create mode 100644 chapter5/workspace/src/proto/detail.proto create mode 100644 chapter5/workspace/uploader.js create mode 100644 lottery/index.js diff --git a/.gitignore b/.gitignore index c6f72b1..c99a74e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ .DS_Store node_modules -etc -lottery chapter5-bak \ No newline at end of file diff --git a/chapter5/backend/article.js b/chapter5/backend/article.js new file mode 100644 index 0000000..cbf2f99 --- /dev/null +++ b/chapter5/backend/article.js @@ -0,0 +1,9 @@ +const http = require('http'); + +http.createServer((req, res) => { + res.writeHead(200, { 'content-type': 'application/json' }); + res.end(JSON.stringify( + { "error": [], "extra": [], "data": { "list": [{ "article_subtitle": "", "video_time": "00:06:08", "id": 136797, "had_viewed": false, "article_title": "01 | 课程介绍", "article_cover": "", "video_media_map": { "sd": { "size": 59401420 }, "ld": { "size": 35017068 }, "hd": { "size": 107900344 } }, "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/static001.geekbang.org\/resource\/image\/cd\/07\/cd13da291b3608dbcec4790db4c2ce07.jpg", "include_audio": false, "video_size": 467254906, "video_time_arr": { "m": "06", "s": "08", "h": "00" }, "column_sku": 100036001, "video_id": "637f9da548c34b0596121f981122cd35", "article_could_preview": true, "is_required": true, "chapter_id": "906", "score": 1568790120726, "article_ctime": 1568790120 }, { "article_subtitle": "", "video_time": "00:01:58", "id": 136798, "had_viewed": false, "article_title": "02 | 内容综述", "article_cover": "", "video_media_map": { "sd": { "size": 17226816 }, "ld": { "size": 9881844 }, "hd": { "size": 29884480 } }, "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/67c56bd96d5747508d5d5e984c94fc13\/snapshots\/abce20e443aa4c6d845bf383fe42ac64-00005.jpg", "include_audio": false, "video_size": 30358230, "video_time_arr": { "m": "01", "s": "58", "h": "00" }, "column_sku": 100036001, "video_id": "67c56bd96d5747508d5d5e984c94fc13", "article_could_preview": true, "is_required": true, "chapter_id": "906", "score": 1568790180578, "article_ctime": 1568790180 }, { "article_subtitle": "", "video_time": "00:03:33", "id": 136799, "had_viewed": false, "article_title": "03 | Node.js是什么?", "article_cover": "", "video_media_map": { "sd": { "size": 13994156 }, "ld": { "size": 7885660 }, "hd": { "size": 19522484 } }, "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/c7f166e016dc4bf89e95967b65a2f447\/snapshots\/fbfe7eb2289644efbfba2931d8587ead-00005.jpg", "include_audio": false, "video_size": 91213276, "video_time_arr": { "m": "03", "s": "33", "h": "00" }, "column_sku": 100036001, "video_id": "c7f166e016dc4bf89e95967b65a2f447", "article_could_preview": true, "is_required": true, "chapter_id": "906", "score": 1568790240208, "article_ctime": 1568790240 }, { "article_subtitle": "", "video_time": "00:08:43", "id": 136800, "had_viewed": false, "article_title": "04 | Node.js可以用来做什么?", "article_cover": "", "video_media_map": { "sd": { "size": 45658244 }, "ld": { "size": 26383544 }, "hd": { "size": 66436192 } }, "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/8eeb38fe232f4a7fad8a430849079ed9\/snapshots\/d06081e6d61746799b169bd96d59f0c4-00005.jpg", "include_audio": false, "video_size": 102157917, "video_time_arr": { "m": "08", "s": "43", "h": "00" }, "column_sku": 100036001, "video_id": "8eeb38fe232f4a7fad8a430849079ed9", "article_could_preview": true, "is_required": true, "chapter_id": "906", "score": 1568790300786, "article_ctime": 1568790300 }, { "article_subtitle": "", "video_time": "00:03:33", "id": 136801, "had_viewed": false, "article_title": "05 | 课程实战项目介绍", "article_cover": "", "video_media_map": { "sd": { "size": 17641168 }, "ld": { "size": 10137900 }, "hd": { "size": 25225276 } }, "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/c09e89fbfa6544eda6e0ee01901acce2\/snapshots\/08b1896aadfd474296cddc55c138a869-00005.jpg", "include_audio": false, "video_size": 43289961, "video_time_arr": { "m": "03", "s": "33", "h": "00" }, "column_sku": 100036001, "video_id": "c09e89fbfa6544eda6e0ee01901acce2", "article_could_preview": true, "is_required": true, "chapter_id": "906", "score": 1568790360844, "article_ctime": 1568790360 }, { "article_subtitle": "", "video_time": "00:02:33", "id": 136803, "had_viewed": false, "article_title": "06 | 什么是技术预研?", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/ef45703fb28f47e79e9cb5d2ae7ad209\/snapshots\/69c1ffff2ba84dd18f9eeb0a4e5a7776-00005.jpg", "include_audio": false, "video_size": 65344127, "video_time_arr": { "m": "02", "s": "33", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1568790420783, "article_ctime": 1568790420 }, { "article_subtitle": "", "video_time": "00:06:17", "id": 136804, "had_viewed": false, "article_title": "07 | Node.js开发环境安装", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/e39671f3abf046008bfc1e9aad5e55ba\/snapshots\/939dbeb0b0124151b5fa40c84196d7c8-00005.jpg", "include_audio": false, "video_size": 96303628, "video_time_arr": { "m": "06", "s": "17", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1568790480309, "article_ctime": 1568790480 }, { "article_subtitle": "", "video_time": "00:08:55", "id": 136806, "had_viewed": false, "article_title": "08 | 第一个Node.js程序:石头剪刀布游戏", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/01132b13b69b4e5ca2b33bdbb50ab00e\/snapshots\/e134ec560c294a5d8294ba28fc0025fe-00005.jpg", "include_audio": false, "video_size": 266574059, "video_time_arr": { "m": "08", "s": "55", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1568790540055, "article_ctime": 1568790540 }, { "article_subtitle": "", "video_time": "00:17:39", "id": 136807, "had_viewed": false, "article_title": "09 | 模块:CommonJS规范", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/e9a59d6f4780451095ca486a1e99ac99\/snapshots\/7ea59f36ff644b92bc27605462e48c05-00005.jpg", "include_audio": false, "video_size": 203642850, "video_time_arr": { "m": "17", "s": "39", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1569396600508, "article_ctime": 1569396600 }, { "article_subtitle": "", "video_time": "00:06:01", "id": 136808, "had_viewed": false, "article_title": "10 | 模块:使用模块规范改造石头剪刀布游戏", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/c1834aa7432f48e294214351cf06ae5c\/snapshots\/31778dd5f63040b6980f859395504f13-00005.jpg", "include_audio": false, "video_size": 92360500, "video_time_arr": { "m": "06", "s": "01", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1569397440762, "article_ctime": 1569397440 }, { "article_subtitle": "", "video_time": "00:11:24", "id": 136809, "had_viewed": false, "article_title": "11 | 模块:npm", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/160ef55961134b088450c6852c139077\/snapshots\/dfa3738e28b94919a99741cecfef740e-00005.jpg", "include_audio": false, "video_size": 132219800, "video_time_arr": { "m": "11", "s": "24", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1569397500367, "article_ctime": 1569397500 }, { "article_subtitle": "", "video_time": "00:19:19", "id": 141994, "had_viewed": false, "article_title": "12 | 模块:Node.js内置模块", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/c3cab6d6485d441bb1fc9ab804352142\/snapshots\/faed4eff89144ae9981c4262ab56398d-00005.jpg", "include_audio": false, "video_size": 232404694, "video_time_arr": { "m": "19", "s": "19", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1569562920376, "article_ctime": 1569562920 }, { "article_subtitle": "", "video_time": "00:16:26", "id": 143517, "had_viewed": false, "article_title": "13 | 异步:非阻塞I\/O", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/73cadfb4fc464512a45c3a6dd6ab4508\/snapshots\/ef795bdf01e54f30a9d9dc364c47b575-00005.jpg", "include_audio": false, "video_size": 202416642, "video_time_arr": { "m": "16", "s": "26", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1569981600572, "article_ctime": 1569981600 }, { "article_subtitle": "", "video_time": "00:14:41", "id": 143557, "had_viewed": false, "article_title": "14 | 异步:异步编程之callback", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/66488ab036cd4b8a944f5f9a8403e702\/snapshots\/a034d4bbcbf34e9ca336e7dd77570175-00005.jpg", "include_audio": false, "video_size": 182094457, "video_time_arr": { "m": "14", "s": "41", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1569981660316, "article_ctime": 1569981660 }, { "article_subtitle": "", "video_time": "00:10:17", "id": 143564, "had_viewed": false, "article_title": "15 | 异步:事件循环", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/aff468023a724d0d8649c5a781834388\/snapshots\/bd3e3dc9b9e74ab79aadf1f4e412b60a-00005.jpg", "include_audio": false, "video_size": 132294052, "video_time_arr": { "m": "10", "s": "17", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1569981720924, "article_ctime": 1569981720 }, { "article_subtitle": "", "video_time": "00:25:12", "id": 143644, "had_viewed": false, "article_title": "16 | 异步:异步编程之Promise", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/807bd87a4b83489397eccb075a26bea2\/snapshots\/81c497741edb460494975e9c1c828020-00005.jpg", "include_audio": false, "video_size": 312514203, "video_time_arr": { "m": "25", "s": "12", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1570620300795, "article_ctime": 1570620300 }, { "article_subtitle": "", "video_time": "00:14:21", "id": 146470, "had_viewed": false, "article_title": "17 | 异步:异步编程之async\/await", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/8bedc86bf3db4b1bb855acb03bb21424\/snapshots\/3de8a1ec5bf04f19b679134f2fa2342a-00005.jpg", "include_audio": false, "video_size": 199075760, "video_time_arr": { "m": "14", "s": "21", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1570620360512, "article_ctime": 1570620360 }, { "article_subtitle": "", "video_time": "00:06:21", "id": 146569, "had_viewed": false, "article_title": "18 | HTTP:什么是HTTP服务器?", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/029b2e5a45314d0d82976d0f58d51d67\/snapshots\/fc398495d6244675b1569373622108e2-00005.jpg", "include_audio": false, "video_size": 223176797, "video_time_arr": { "m": "06", "s": "21", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1570620420887, "article_ctime": 1570620420 }, { "article_subtitle": "", "video_time": "00:07:41", "id": 146582, "had_viewed": false, "article_title": "19 | HTTP:简单实现一个HTTP服务器", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/de096bdee74149ddaecc6bd32a176222\/snapshots\/e5ed6a8c25084bf5b31f23f959d5b246-00005.jpg", "include_audio": false, "video_size": 144923147, "video_time_arr": { "m": "07", "s": "41", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1570620480079, "article_ctime": 1570620480 }, { "article_subtitle": "", "video_time": "00:10:19", "id": 151876, "had_viewed": false, "article_title": "20 | HTTP:实现网页版石头剪刀布", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/92e873ce3bb6438e8637c3e734b44a48\/snapshots\/064652ab126e4c0cbfb5ffab31d71d0f-00005.jpg", "include_audio": false, "video_size": 119614853, "video_time_arr": { "m": "10", "s": "19", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1571238180352, "article_ctime": 1571238180 }, { "article_subtitle": "", "video_time": "00:20:31", "id": 151880, "had_viewed": false, "article_title": "21 | HTTP:用express优化石头剪刀布游戏", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/44e81681034b4f14803e2cc6959d9fb5\/snapshots\/dd6cdf38bf82448780e35ad4f6ef3d6d-00005.jpg", "include_audio": false, "video_size": 237672645, "video_time_arr": { "m": "20", "s": "31", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1571238240606, "article_ctime": 1571238240 }, { "article_subtitle": "", "video_time": "00:16:37", "id": 151914, "had_viewed": false, "article_title": "22 | HTTP:用koa优化石头剪刀布游戏", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/400f678c82c44f9d97754d228d0e2a2d\/snapshots\/a9cd7155e52b4b71825b654d89db8f74-00005.jpg", "include_audio": false, "video_size": 192278192, "video_time_arr": { "m": "16", "s": "37", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1571238300923, "article_ctime": 1571238300 }, { "article_subtitle": "", "video_time": "00:11:51", "id": 151911, "had_viewed": false, "article_title": "23 | RPC 调用:什么是RPC调用?", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/aa0bebe9f4684bf19683f426c0c98bce\/snapshots\/118bc827db8b424cac47d3d18ee03d58-00005.jpg", "include_audio": false, "video_size": 136798483, "video_time_arr": { "m": "11", "s": "51", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1571238360172, "article_ctime": 1571238360 }, { "article_subtitle": "", "video_time": "00:16:29", "id": 151912, "had_viewed": false, "article_title": "24 | RPC调用:Node.js Buffer编解码二进制数据包", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/07b1c3eea5d74d8398d0c37ef3ab776f\/snapshots\/b980907c55b54ef8b185e299290cda20-00005.jpg", "include_audio": false, "video_size": 190747741, "video_time_arr": { "m": "16", "s": "29", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1571238420519, "article_ctime": 1571238420 }, { "article_subtitle": "", "video_time": "00:23:00", "id": 152724, "had_viewed": false, "article_title": "25 | RPC 调用:Node.js net建立多路复用的RPC通道", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/413d309e516b4d9d8da140d701ed78c2\/snapshots\/98c16ab7d72f47d89170a52c6c2e4803-00005.jpg", "include_audio": false, "video_size": 266144287, "video_time_arr": { "m": "23", "s": "00", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "907", "score": 1571392260043, "article_ctime": 1571392260 }, { "article_subtitle": "", "video_time": "00:12:27", "id": 155083, "had_viewed": false, "article_title": "26 | 项目启动:整体需求分析", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/c7ee9a984f304efcb28dc194a694ec3c\/snapshots\/3e68c7a674ba4cbabfbe8105a08f81c8-00005.jpg", "include_audio": false, "video_size": 144148646, "video_time_arr": { "m": "12", "s": "27", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1015", "score": 1571839080634, "article_ctime": 1571839080 }, { "article_subtitle": "", "video_time": "00:06:46", "id": 155084, "had_viewed": false, "article_title": "27 | 项目启动:极客时间App下载页开发", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/3a320e08010d416aa9503070ec6878e4\/snapshots\/2370d1fe3e144d76b2d3eddba43f73b9-00005.jpg", "include_audio": false, "video_size": 78219204, "video_time_arr": { "m": "06", "s": "46", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1015", "score": 1571839140849, "article_ctime": 1571839140 }, { "article_subtitle": "", "video_time": "00:06:15", "id": 155085, "had_viewed": false, "article_title": "28 | 课程详情页:极客时间详情页需求解构", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/58b3c104132648dfb3fc3e553a80ec47\/snapshots\/aacb4a3725024509a47db3b7062e19ce-00005.jpg", "include_audio": false, "video_size": 72514266, "video_time_arr": { "m": "06", "s": "15", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1015", "score": 1571839200216, "article_ctime": 1571839200 }, { "article_subtitle": "", "video_time": "00:11:56", "id": 155196, "had_viewed": false, "article_title": "29 | 课程详情页:将ES6模版字符串改造成模板引擎", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/38cbaafe2269429e8d8639664e4c00b9\/snapshots\/2a65c92089e94686835b85deb2e09212-00005.jpg", "include_audio": false, "video_size": 137946799, "video_time_arr": { "m": "11", "s": "56", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1015", "score": 1571888280346, "article_ctime": 1571888280 }, { "article_subtitle": "", "video_time": "00:14:09", "id": 155198, "had_viewed": false, "article_title": "30 | 课程详情页:极客时间详情页需求实现", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/9c5bb59daf7640bb997fec9209a93572\/snapshots\/5c070744ba1e4186afbe5a8fb7760067-00005.jpg", "include_audio": false, "video_size": 163924407, "video_time_arr": { "m": "14", "s": "09", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1015", "score": 1571888340006, "article_ctime": 1571888340 }, { "article_subtitle": "", "video_time": "00:06:22", "id": 158831, "had_viewed": false, "article_title": "31 | 课程播放页:极客时间播放页需求解构", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/12df367aad9c4589aba04b2fb4e1c240\/snapshots\/14ce985fdc31411da3a4c93805b8fcca-00005.jpg", "include_audio": false, "video_size": 73748572, "video_time_arr": { "m": "06", "s": "22", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1015", "score": 1572414120983, "article_ctime": 1572414120 }, { "article_subtitle": "", "video_time": "00:10:46", "id": 158832, "had_viewed": false, "article_title": "32 | 课程播放页:GraphQL API服务", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/baa40668330747c2863a96c080834388\/snapshots\/78e1c81dd89a4598b00d1fa54eac9747-00005.jpg", "include_audio": false, "video_size": 124676664, "video_time_arr": { "m": "10", "s": "46", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1015", "score": 1572414180488, "article_ctime": 1572414180 }, { "article_subtitle": "", "video_time": "00:09:47", "id": 158833, "had_viewed": false, "article_title": "33 | 课程播放页:极客时间播放页需求实现", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/e0af3fdbbb454e4bad25478f2cbdcd54\/snapshots\/5c2e779c4a664c63a6fa0d6ae894e317-00005.jpg", "include_audio": false, "video_size": 113041649, "video_time_arr": { "m": "09", "s": "47", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1015", "score": 1572414240742, "article_ctime": 1572414240 }, { "article_subtitle": "", "video_time": "00:05:32", "id": 158834, "had_viewed": false, "article_title": "34 | 课程列表页:极客时间列表页需求解构", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/1f604e2dba0a4dcf90b2e760fdbda54d\/snapshots\/3861459228344063a3d2fbe2e6881f75-00005.jpg", "include_audio": false, "video_size": 64065484, "video_time_arr": { "m": "05", "s": "32", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1015", "score": 1572414300842, "article_ctime": 1572414300 }, { "article_subtitle": "", "video_time": "00:13:18", "id": 158835, "had_viewed": false, "article_title": "35 | 课程列表页:用 Vue\/React 进行服务端渲染", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/9efd8a655afb438a8e1b16afd02662bf\/snapshots\/6a9ab19190814ddcbe3cbbba380a36d5-00005.jpg", "include_audio": false, "video_size": 154078021, "video_time_arr": { "m": "13", "s": "18", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1015", "score": 1572414360279, "article_ctime": 1572414360 }, { "article_subtitle": "", "video_time": "00:21:49", "id": 159407, "had_viewed": false, "article_title": "36 | 课程列表页:极客时间列表页需求实现", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/97dcd9984538458490a86a5d9b235bbd\/snapshots\/3833d3d7c1714707b01afb1205e8c0cf-00005.jpg", "include_audio": false, "video_size": 251426921, "video_time_arr": { "m": "21", "s": "49", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1015", "score": 1572498660245, "article_ctime": 1572498660 }, { "article_subtitle": "", "video_time": "00:11:39", "id": 162487, "had_viewed": false, "article_title": "37 | 性能工具:HTTP服务的性能测试", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/de02e9414ebc4bc6ad091ab1e4a83642\/snapshots\/629cfda50f7b4281845c623a2c44e0a7-00005.jpg", "include_audio": false, "video_size": 134702674, "video_time_arr": { "m": "11", "s": "39", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1040", "score": 1573031880091, "article_ctime": 1573031880 }, { "article_subtitle": "", "video_time": "00:11:53", "id": 162490, "had_viewed": false, "article_title": "38 | 性能工具:Node.js性能分析工具", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/6d504651ff0a43c8b2e95c36d4c49320\/snapshots\/34dff6c4d1ac45fe9b835f089153fad2-00005.jpg", "include_audio": false, "video_size": 137378693, "video_time_arr": { "m": "11", "s": "53", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1040", "score": 1573031940766, "article_ctime": 1573031940 }, { "article_subtitle": "", "video_time": "00:08:07", "id": 162491, "had_viewed": false, "article_title": "39 | 代码优化:JavaScript代码性能优化", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/caa201f749834d18815319772da3197a\/snapshots\/208b722e4c744dda887d91884286c80f-00005.jpg", "include_audio": false, "video_size": 93909373, "video_time_arr": { "m": "08", "s": "07", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1040", "score": 1573032000415, "article_ctime": 1573032000 }, { "article_subtitle": "", "video_time": "00:12:16", "id": 162497, "had_viewed": false, "article_title": "40 | 代码优化:内存管理优化", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/c6bb4892548f45bab12e5690997f696e\/snapshots\/c47bb3aee990421fabef7938f9ec6e76-00005.jpg", "include_audio": false, "video_size": 142132328, "video_time_arr": { "m": "12", "s": "16", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1040", "score": 1573032060507, "article_ctime": 1573032060 }, { "article_subtitle": "", "video_time": "00:19:07", "id": 162499, "had_viewed": false, "article_title": "41 | 代码优化:Node.js C++插件", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/dc8057942932402593daae2f76b46a6f\/snapshots\/2bc901f0f2c34c61a491ddc8a3ff5c8c-00005.jpg", "include_audio": false, "video_size": 221184867, "video_time_arr": { "m": "19", "s": "07", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1040", "score": 1573032120464, "article_ctime": 1573032120 }, { "article_subtitle": "", "video_time": "00:11:59", "id": 165460, "had_viewed": false, "article_title": "42 | 多进程优化:Node.js子进程与线程", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/3691e67d123c43a5bd55aa1a86339921\/snapshots\/b99cbd449550402781982a460514a2ee-00005.jpg", "include_audio": false, "video_size": 138497157, "video_time_arr": { "m": "11", "s": "59", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1040", "score": 1573622580022, "article_ctime": 1573622580 }, { "article_subtitle": "", "video_time": "00:18:31", "id": 165461, "had_viewed": false, "article_title": "43 | 多进程优化:Node.js cluster模块实战与源码解读", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/416373c83ce04cebb9351cb081f7885e\/snapshots\/3eefb747feb1406ab4283e9f24532a35-00005.jpg", "include_audio": false, "video_size": 213962911, "video_time_arr": { "m": "18", "s": "31", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1040", "score": 1573622640199, "article_ctime": 1573622640 }, { "article_subtitle": "", "video_time": "00:14:27", "id": 165463, "had_viewed": false, "article_title": "44 | 多进程优化:进程守护与管理", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/c69b413e4cae4193bc010be414e8833d\/snapshots\/ac3c917e6a684d98ae705b50fcb7c483-00005.jpg", "include_audio": false, "video_size": 166915181, "video_time_arr": { "m": "14", "s": "27", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1040", "score": 1573622700326, "article_ctime": 1573622700 }, { "article_subtitle": "", "video_time": "00:13:21", "id": 165465, "had_viewed": false, "article_title": "45 | 架构优化:动静分离", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/22c5097d283b4eefbacd588ddc61ebab\/snapshots\/74b507dc445a43dd92b5c833fe3df074-00005.jpg", "include_audio": false, "video_size": 154041005, "video_time_arr": { "m": "13", "s": "21", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1040", "score": 1573622760261, "article_ctime": 1573622760 }, { "article_subtitle": "", "video_time": "00:14:35", "id": 165466, "had_viewed": false, "article_title": "46 | 架构优化:反向代理与缓存服务", "article_cover": "", "video_media_map": [], "had_freelyread": false, "is_video_preview": true, "article_summary": "", "column_had_sub": false, "video_cover": "https:\/\/media001.geekbang.org\/a2f5d9efceef41f3beac456ff678a104\/snapshots\/89ce7b8540d546c7b70e3a512c5683e6-00005.jpg", "include_audio": false, "video_size": 168784004, "video_time_arr": { "m": "14", "s": "35", "h": "00" }, "column_sku": 100036001, "video_id": "", "article_could_preview": false, "is_required": true, "chapter_id": "1040", "score": 1573622820812, "article_ctime": 1573622820 }], "page": { "count": 46, "more": false } }, "code": 0 })); +}).listen(4003, () => { + console.log('article http server listened: 4003') +}); \ No newline at end of file diff --git a/chapter5/backend/comment-list.js b/chapter5/backend/comment-list.js new file mode 100644 index 0000000..d16ed24 --- /dev/null +++ b/chapter5/backend/comment-list.js @@ -0,0 +1,21 @@ +const fs = require('fs') +const protobuf = require('protocol-buffers'); +const commentSchemas = protobuf( + fs.readFileSync(`${__dirname}/../proto/comment.proto`) +); + +// 假数据 +const commentData = require('./mockdata/comment'); + +/** + * 服务端的编解包逻辑 + */ +const server = require('./lib/geeknode-rpc-server')(commentSchemas.CommentListRequest, commentSchemas.CommentListResponse); + +server + .createServer((request, response) => { + response.end({ comments: commentData }); + }) + .listen(4001, ()=> { + console.log('commentlist rpc server listened: 4001') + }); \ No newline at end of file diff --git a/chapter5/backend/comment-praise.js b/chapter5/backend/comment-praise.js new file mode 100644 index 0000000..64d495a --- /dev/null +++ b/chapter5/backend/comment-praise.js @@ -0,0 +1,32 @@ +const fs = require('fs') +const protobuf = require('protocol-buffers'); +const commentSchemas = protobuf( + fs.readFileSync(`${__dirname}/../proto/comment.proto`) +); + +// 假数据 +const commentData = require('./mockdata/comment'); + +/** + * 服务端的编解包逻辑 + */ +const server = require('./lib/geeknode-rpc-server')(commentSchemas.PraiseRequest, commentSchemas.PraiseResponse); + +server + .createServer((request, response) => { + const commentid = request.body.commentid; + const comment = commentData.filter(comment => comment.id == commentid)[0]; + let praiseNum = 0; + + if (comment) { + comment.praiseNum++; + praiseNum = comment.praiseNum; + } + response.end({ + commentid, + praiseNum + }); + }) + .listen(4002, ()=> { + console.log('praise rpc server listened: 4002') + }); \ No newline at end of file diff --git a/chapter5/backend/detail.js b/chapter5/backend/detail.js new file mode 100644 index 0000000..4668ed4 --- /dev/null +++ b/chapter5/backend/detail.js @@ -0,0 +1,28 @@ +const fs = require('fs') +const protobuf = require('protocol-buffers'); +const schemas = protobuf( + fs.readFileSync(`${__dirname}/proto/detail.proto`) +); + +// 假数据 +const columnData = require('./mockdata/column') + +/** + * 服务端的编解包逻辑 + */ +const server = require('./lib/geeknode-rpc-server')(schemas.ColumnRequest, schemas.ColumnResponse); + +server + .createServer((request, response) => { + // 因为都是假数据,这里就没有使用栏目id。真实项目会拿这个columnid去请求数据库 + const columnid = request.body; + + // 直接返回假数据 + response.end({ + column: columnData[0], + recommendColumns: [columnData[1], columnData[2]] + }); + }) + .listen(4000, ()=> { + console.log('detail server listened: 4000') + }); \ No newline at end of file diff --git a/chapter5/backend/lib/geeknode-rpc-server.js b/chapter5/backend/lib/geeknode-rpc-server.js new file mode 100644 index 0000000..45d3b1a --- /dev/null +++ b/chapter5/backend/lib/geeknode-rpc-server.js @@ -0,0 +1,36 @@ +const RPC = require('./rpc-server'); + +/** + * 因为所有服务用的包头格式都一样,不一样的只有protobuf协议,所以这里可以将这段逻辑封成一个模块 + * + * 日常做项目的时候一定要注意把重复代码做封装 + */ +module.exports = function (protobufRequestSchema, protobufResponseSchema) { + return new RPC({ + // 解码请求包 + decodeRequest(buffer) { + const seq = buffer.readUInt32BE(); + + return { + seq: seq, + result: protobufRequestSchema.decode(buffer.slice(8)) + } + }, + // 判断请求包是不是接收完成 + isCompleteRequest(buffer) { + const bodyLength = buffer.readUInt32BE(4); + + return 8 + bodyLength + }, + // 编码返回包 + encodeResponse(data, seq) { + const body = protobufResponseSchema.encode(data); + + const head = Buffer.alloc(8); + head.writeUInt32BE(seq); + head.writeUInt32BE(body.length, 4); + + return Buffer.concat([head, body]); + } + }) +} \ No newline at end of file diff --git a/chapter5/backend/lib/rpc-server.js b/chapter5/backend/lib/rpc-server.js new file mode 100644 index 0000000..afa83c6 --- /dev/null +++ b/chapter5/backend/lib/rpc-server.js @@ -0,0 +1,131 @@ +// 'use strict'; +// const debug = require("debug")('easysock-server'); +const net = require("net"); + +module.exports = class RPC { + constructor({ encodeResponse, decodeRequest, isCompleteRequest }) { + this.encodeResponse = encodeResponse; + this.decodeRequest = decodeRequest; + this.isCompleteRequest = isCompleteRequest; + } + + createServer(callback) { + let buffer = null; + + const tcpServer = net.createServer((socket) => { + + socket.on('data', (data) => { + buffer = (buffer && buffer.length > 0) ? + Buffer.concat([buffer, data]) : // 有遗留数据才做拼接操作 + data; + + let checkLength = null; + while (buffer && (checkLength = this.isCompleteRequest(buffer))) { + let requestBuffer = null; + if (checkLength == buffer.length) { + requestBuffer = buffer; + buffer = null; + + } else { + requestBuffer = buffer.slice(0, checkLength); + buffer = buffer.slice(checkLength); + } + + const request = this.decodeRequest(requestBuffer); + callback( + { // request + body: request.result, + socket + }, + { // response + end: (data) => { + const buffer = this.encodeResponse(data, request.seq) + socket.write(buffer); + } + } + ); + } + }) + }); + + return { + listen() { + tcpServer.listen.apply(tcpServer, arguments) + } + } + } +} + +// /** +// * @param config +// * encode +// * decode +// * check +// * @param handleResponse:async fn +// * +// * +// * @returns {bindsocket(socket)} +// */ +// let exportee = module.exports = function (config, handleResponse) { +// let {encode, decode, check} = config; +// if (!check && config.isReceiveComplete) check = config.isReceiveComplete; + +// // 缓冲区 +// let handleData = async function (buffer) { +// let param = null; +// let result = null; + +// try { +// param = decode(buffer); +// result = await handleResponse(param.error, param.result); +// return encode(result, param.seq); +// } catch (e) { +// return debug(e); +// } +// }; + +// return function bindsocket(socket) { +// debug('socket connected', socket.connection); + +// socket.on('data', data=> { +// debug('socket ondata'); + +// debug('remain', buffer && buffer.length) +// }); + +// socket.on('end', e=> { +// debug('socket end'); +// }); + +// return socket; +// }; +// }; + +// const net = require("net"); +// /** +// * 直接创建一个server +// * @param config 包含encode、decode、check +// * @param handlerResponse 一个async function,参数为收到的请求结构体,返回是回包的结构体 +// */ +// exportee.server = function (config, handlerResponse) { +// let handleSocket = exportee(config, handlerResponse); +// let socketList = []; + +// return Object.assign( +// net.createServer(function (socket) { +// handleSocket(socket); +// socketList.push(socket); +// }), + +// { +// closeAllSocket: function () { +// socketList.forEach(socket=> { +// if (!socket.destroyed) { +// socket.destroy(); + +// } +// }) +// } +// } +// ) +// }; \ No newline at end of file diff --git a/chapter5/backend/mockdata/column.js b/chapter5/backend/mockdata/column.js new file mode 100644 index 0000000..4061208 --- /dev/null +++ b/chapter5/backend/mockdata/column.js @@ -0,0 +1,144 @@ +module.exports = [{ + id: 232, + type: 2, + column_cover: "https://static001.geekbang.org/resource/image/42/78/42db8ef7b28bcdc26410141dd97b8178.jpg", + column_title: "Node.js开发实战", + column_subtitle: "带你开发一个完整的Node.js项目", + author_name: "杨浩", + author_intro: "腾讯高级工程师", + column_intro: `

课程背景

Node.js 拥有广大的 JavaScript 程序员基础并且完全开源,所以它拥有一个强大的开发者社区。依靠繁荣的社区力量,现在已经发展出成熟的技术体系与庞大的生态。它被广泛地用在 Web 服务、开发工作流、客户端应用等诸多领域。其中,在 Web 服务开发这个领域,业界对 Node.js 的接受程度最高。

对于很多前端开发者来说,掌握 Node.js 的基础知识并不难,难点在于如何按照后端工程师的思维,基于 Node.js 来一步步构建项目,其中涉及诸如 RPC 调用、系统运维以及进程管理等前端工程师较少涉及到的领域。

因此,本课程站在一个前端工程师的角度,讲解如何基于 Node.js 开发一个完整的项目,从一开始的技术预研再到实际开发、性能优化以及最终的框架架构搭建和工程化建设,带你完整体验一遍前端工程师使用 Node.js进行项目开发会碰到的各种常见场景和技术难点,学完课程之后,你将能够熟练运用 Node.js 进行大型项目的设计和开发。

讲师简介

杨浩,腾讯高级工程师。之前曾在腾讯视频负责 Web 端的相关工作,设计并完成了腾讯视频整站大部分页面的 Node.js 化。

腾讯视频是从 2015 年开始使用 Node.js 对整站进行改造的,杨浩与同事一起从零开始一步一步把整个 Node.js 的开发运维步骤打通,搭建了一个运行于后台服务和浏览器前端之间的 Node.js 中间层用作 SSR(Server Side Rendering),以提高搜索引擎抓取的效果以及首屏展现的速度。

在 2018 年由 InfoQ 举办的 ArchSummit 全球架构师峰会深圳站上,杨浩也对在腾讯视频进行 Node.js 改造这一经历做了公开分享:

https://time.geekbang.org/dailylesson/detail/100016617

课程收获

  1. Node.js 开发必备基础知识;
  2. 使用 Node.js 构建 BFF 层;
  3. 一个完整项目的开发重构实战;
  4. 性能优化和工程化建设核心方法。

更新频率

  1. 9月18日上线 1-8 讲,9月25日开始固定为每周三更新。
  2. 全部课程预计将于 12 月 11 日前更新完毕。

如何在电脑端观看视频

  1. 用浏览器访问 https://time.geekbang.org ,登录极客时间账号;
  2. 然后在“讲堂”板块选择“视频课程”标签,点击相应的视频课程即可观看。

订阅须知

  1. 本课程为视频课程,共55讲,订阅成功后即可通过“极客时间”App端、小程序端、Web端永久观看;
  2. 由于视频内容为虚拟商品,一经订阅,概不退款;
  3. 企业批量购买请点击“企业充值”了解详情,可支持员工选课,企业支付;
  4. 在课程学习过程中,如有任何问题,请邮件联系 service@geekbang.org。
  5. 戳此申请学生认证,订阅课程一律 5 折。
`, + column_unit: "55讲", + sub_count: 2915, + update_frequency: "约550分钟", + column_price: 12900, + column_price_market: 12900, + articles: [{ + id: 0, + is_video_preview: true, + article_title: '第一课' + }, { + id: 1, + is_video_preview: false, + article_title: '第二课' + }, { + id: 2, + is_video_preview: false, + article_title: '第三课' + }, { + id: 3, + is_video_preview: false, + article_title: '第四课' + }] +}, { + id: 278, + type: 2, + column_cover: "https://static001.geekbang.org/resource/image/a0/19/a0ad97e899aa22023951bde5813e0819.jpg", + column_title: "移动端自动化测试实战", + column_subtitle: "一步一步带你成为测试高手", + author_name: "思寒", + author_intro: "TesterHome 社区测试专家、霍格沃兹测试学院创始人", + column_intro: `

课程背景

随着行业的竞争加剧,互联网产品迭代的速度越来越快,QA 与测试工程师都需要在越来越短的测试周期内充分保证质量。 App 测试则面临着更多的挑战,比如多端发布、多版本发布、多机型发布等等,这导致了手工测试很难完全胜任,自动化测试、持续测试就成为了团队的必要需求。作为传统的测试工程师与研发工程师,需要适应行业的变化,并积极做出应对。

学习自动化测试有两个难点,除了其本身拥有一定的技术门槛之外,实战经验才是学习自动化测试的最大难点。部分初中级测试工程师往往在掌握了基本的编程与自动化测试框架 API 后,会认为自己已经掌握了自动化测试,便开始投入到实际开发中,却又掉进各种各样的技术债和经验债里,痛苦不堪。

因此,整个课程将分为基础篇、框架篇和实战篇三个阶段,十个章节,由浅入深地带大家掌握 app 自动化测试技能。通过学习这门课程,你将能够掌握正确的自动化测试方法、理解自动化测试的技术体系,可以解决工作中遇到的自动化测试难题,并能把这项技术熟练应用在自己公司的测试体系中。

讲师简介

思寒,TesterHome 社区测试专家,霍格沃兹测试学院创始人。

先后任职于阿里巴巴、百度、雪球等公司,承担测试工程师、测试开发工程师、测试架构师等工作。

2013年,移动互联网开始迅猛发展,催生了对 app 测试的需求,思寒老师与其团队决定物色一个优秀的移动测试框架,在进行大量调研之后,Appium 以其优秀的特性脱颖而出。思寒老师也开始投身于这个开源框架的建设中,积极参与 Appium 框架的中文翻译、向 Appium 提交测试用例与 pull request,同时也开始在历任的公司中将 Appium 用于公司内的移动测试,积累了非常多的落地经验。

课程收获

  1. 系统掌握 Appium 自动化测试框架;
  2. 全面提升移动端自动化测试实战能力;
  3. 掌握自动遍历测试及兼容性测试技巧;
  4. 构建多设备管理平台与持续集成体系。

更新频率

  1. 10月23日上线 1-9 讲,10月30日开始固定为每周三更新。
  2. 全部课程预计将于 2020 年 1 月 22 日前更新完毕。

如何在电脑端观看视频

  1. 用浏览器访问 https://time.geekbang.org ,登录极客时间账号;
  2. 然后在“讲堂”板块选择“视频课程”标签,点击相应的视频课程即可观看。

订阅须知

  1. 本课程为视频课程,共58讲,订阅成功后即可通过“极客时间”App端、小程序端、Web端永久观看;
  2. 由于视频内容为虚拟商品,一经订阅,概不退款;
  3. 企业批量购买请点击“企业充值”了解详情,可支持员工选课,企业支付;
  4. 在课程学习过程中,如有任何问题,请邮件联系 service@geekbang.org。
  5. 戳此申请学生认证,订阅课程一律 5 折。
`, + column_unit: "55讲", + sub_count: 1471, + update_frequency: "约550分钟", + column_price: 9900, + column_price_market: 9900, + articles: [{ + id: 0, + is_video_preview: true, + article_title: '第一课' + }, { + id: 1, + is_video_preview: false, + article_title: '第二课' + }, { + id: 2, + is_video_preview: false, + article_title: '第三课' + }, { + id: 3, + is_video_preview: false, + article_title: '第四课' + }] +}, { + id: 211, + type: 1, + column_cover: "https://static001.geekbang.org/resource/image/b6/1a/b683240befccbdcaa86da8f382d3a11a.jpg", + column_subtitle: "Facebook研发效率工作法", + column_title: "研发效率破局之道", + author_intro: "前Facebook内部工具团队Tech Lead", + author_name: "葛俊", + column_intro: `

如果你问中国和美国互联网公司都有什么差别,很多人会回答:低效加班文化。最近爆出的996大讨论,正反映出国内很多公司拼工时的做法,以及程序员群体对这种做法的反感情绪。

“拼工时”说到底是为提高产出,但国内互联网产业已经步入从野蛮生长到精耕细作的过渡期,人力成本逐渐提高,通过糙快猛打拼和996加班去抢占市场获得机会的成功案例越来越少。至此,只有提高效能才是出路。

事实上,越来越多的公司意识到研发效能的重要性,很多百人研发规模的公司开始组建专门的效能团队,着手提高公司的整体效能。

然而,因为软件开发的灵活性,导致研发效能的提高需要关注的点太多、可以使用的方法也很多,结果就是不知道从何处着手,或者是花了精力、加大了投入却看不到效果,甚至产出抵不上投入。

在这个专栏中,葛俊将基于硅谷和国内多年的从业经验,从研发流程、工程方法、个人效能、管理和文化这四个方向入手,系统介绍研发效能的理论和实践,探讨协同、开发、测试、运维等关键研发步骤中高效的工程方法。

葛俊,前 Facebook内部工具团队Tech Lead,开源项目Phabricator的主要作者之一。在互联网行业奋斗的15年里,他曾任职于微软、Facebook、华为,以及硅谷和国内的两家创业公司。

在此期间,他在研发效能团队工作过,也在使用效能流程和工具的产品团队工作过,也有过主导推进研发效能的丰富经历。可以说,他目睹了硅谷以及国内的大型企业、创业公司推进研发效能的成功经验与失败经历,同时总结了一套适用的高效能引入方法,希望在这个专栏里和你一起学习、进步。

专栏模块

专栏共35讲,分为5个模块。

1. 研发效能综述

讲解研发效能的定义、模型,并着重介绍什么是度量,以及度量的正确使用方法。借此,希望你能够梳理出研发效能的主脉络,构建出一幅清晰的知识图谱。

2. 研发流程

与你分享敏捷、持续集成、持续交付、DevOps、团队协同等话题。通过这个模块,希望帮助你深入理解研发过程中的关键流程,以及流程优化的基本原则,从而能够针对实际情况找到最合适自己的工程实践,让软件开发的整个流程更加顺畅、高效。

3. 工程方法

与你讨论研发流程(包括开发、测试、运维等)中各关键步骤的高效工程方法,并分析软件开发的趋势和热点,比如智能化、云原生等。通过这个模块,希望能够加深你对这些工程方法的了解,帮助你找到针对具体环节提高效能的方法,以及学会如何正确地使用这些方法。

4. 个人效能

聚集探讨如何提高个人效能,具体涉及深度工作、Git、命令行、VIM、工具环境集成等内容,旨在帮助你提高技术专精程度,持续成长。每个开发人员都应该提高自己的效能,只有这样才能持续学习持续提高,避免被业务拖着跑的现象。

5. 管理和文化

系统分析硅谷尤其是Facebook的工程师文化,并根据国内公司的具体落地经验,给出推荐的文化引入和建设方法。

限时福利

  1. 订阅后,分享专属海报,每邀请一位好友订阅有奖励。
  2. 戳此添加社群管理员,进入技术交流&福利群
  3. 戳此申请学生认证,订阅课程一律 5 折。

订阅须知

  1. 本专栏为订阅专栏,更新时间为2019年8月23日至2019年11月13日。订阅成功后,即可通过“极客时间”App端、小程序端、Web端永久阅读。
  2. 本专栏更新时间为每周一、三、五,形式为图文 + 音频,共计35期,定价99元。
  3. 企业批量购买请点击“企业充值”了解详情,可支持员工选课,企业支付。
  4. 本专栏为虚拟商品,一经订阅,概不退款。
  5. 在专栏阅读过程中,如有任何问题,请邮件联系service@geekbang.org。
`, + column_unit: "35讲", + sub_count: 3034, + update_frequency: "每周一 / 三 / 五更新", + column_price: 9900, + column_price_market: 9900, + articles: [{ + id: 0, + is_video_preview: true, + article_title: '第一课' + }, { + id: 1, + is_video_preview: false, + article_title: '第二课' + }, { + id: 2, + is_video_preview: false, + article_title: '第三课' + }, { + id: 3, + is_video_preview: false, + article_title: '第四课' + }] +}, { + id: 180, + type: 3, + column_cover: "https://static001.geekbang.org/resource/image/cc/fb/ccb3acf6fcfab959aee1d800b882f7fb.png", + column_subtitle: "程序员都应该学学怎么表达", + column_title: "如何做好一场技术演讲", + author_intro: "极客时间编辑部", + author_name: "极客时间", + column_intro: `

内容简介

程序员都应该学学怎么表达。

据调查,75% 的人在公众场合演讲时会感到非常恐慌。但是,公众演讲是每个人都需要面对的“刁难”,无论是学生时期的课题总结、毕业答辩,还是工作之后的公司内部项目汇报、外部技术交流,这些场合都需要我们具备优秀的演讲能力,才能更好地向别人展示我们的工作成果,让优秀的技术方案广为传播。

公众场合的演讲能力成为职场中越来越重要的一项基础技能。 谁知道,未来的某一天,会不会是你,站在新闻发布会的台上,向下面黑压压的人群宣布一款你公司的技术产品呢? 又或者,你作为一位技术大咖,在一场技术大会上向大家讲述你的技术棋局?

就像编程一样,演讲能力也是可以习得的。正如天生口吃的德摩斯梯尼通过自己的不断苦练也能成为古希腊著名的演说家一样,成为一个优秀的技术演讲者,也是有径可循的。

这里收录的 6 篇文章,包含多位技术大牛的演讲经验总结,从技术人员的角度来剖析做好一场技术演讲的术与道。

内容目录

本系列共 6 篇文章,前 5 篇文章沿着“内容准备—心态调节—陈述技巧—形象管理”依次展开,每篇文章的主题各有侧重点。最后一篇是综合性的经验阐述,和前五篇提到的几个关键点既相互对照,又加入了节奏掌控、回答问题的技巧等独创性内容。

限时福利

  1. 订阅后,分享专属海报,每邀请一位好友订阅有奖励。
  2. 戳此添加社群管理员,进入技术交流&福利群
↵↵

适宜人群

想提升演讲能力,向公众有效传播自己的技术方案、理念和观点的人;想学习更多的内容布局、组织方法,让自己的演讲更生动、更形象的人;想了解如何在公众场合管理好自己的身体语言,打造良好的个人形象的人。

购买须知

  1. 本系列共六篇文章,现已全部上线,购买成功后,即可通过“极客时间”App端、小程序端、Web端永久观看;
  2. 由于文字内容为虚拟商品,一经购买,概不退款;
  3. 在阅读过程中,如有任何问题,请邮件联系 service@geekbang.org。
`, + column_unit: "6讲", + sub_count: 17209, + update_frequency: "约150分钟", + column_price: 100, + column_price_market: 100, + articles: [{ + id: 0, + is_video_preview: true, + article_title: '第一课' + }, { + id: 1, + is_video_preview: false, + article_title: '第二课' + }, { + id: 2, + is_video_preview: false, + article_title: '第三课' + }, { + id: 3, + is_video_preview: false, + article_title: '第四课' + }] +}, { + id: 198, + type: 1, + column_cover: "https://static001.geekbang.org/resource/image/7f/6d/7f9edc0620b7083074b21cc9036ffa6d.jpg", + column_subtitle: "基于DDD的微服务拆分与设计", + column_title: "DDD实战课", + author_intro: "人保高级架构师", + author_name: "欧创新", + column_intro: `

随着分布式技术的快速兴起,我们已经进入到了微服务架构时代。微服务架构的出现,很好地实现了应用之间的解耦,解决了单体应用扩展性和弹性伸缩能力不足的问题。随着业务的复杂度升级,其好处自然不言而喻。

那微服务到底怎么拆分和设计才算合理,拆多小才叫微服务?这个尖锐的问题,在业内一直被热议。紧接着,继阿里巴巴成功转型中台战略之后,微服务设计和拆分再至风口浪尖,对于众多公司来说,都是一个不小的挑战。

那有没有好的方法来指导微服务和中台的设计呢?当然有,你也可能耳闻过,那就是领域驱动设计(DDD)。

本专栏将重点解决以上问题,力求用浅显易懂的案例,深入DDD的核心知识体系与设计思想,带你掌握一套完整而系统的基于DDD的微服务拆分与设计方法,明确从战略设计到战术设计的微服务标准设计过程,助力落地边界清晰、可持续演进的微服务架构。

你将获得

作者简介

欧创新,人保高级架构师,拥有十多年的软件架构经验。他热衷于采用DDD的设计方法实现中台业务建模,专注基于DDD的微服务设计和开发。目前,他正在深入探索传统企业中台数字化转型的技术和方法体系。

课程设计

专栏共21讲,分为基础篇、进阶篇和实战篇三部分。

基础篇主要讲解DDD的核心知识体系,具体包括:领域、子域、限界上下文、实体、值对象、聚合和聚合根等概念。借助浅显易懂的案例,带你理解它们以及它们之间的合作、依赖关系。

进阶篇主要讲解领域事件、DDD分层架构、几种常见的微服务架构模型以及中台设计思想等内容。带你深入微服务分层设计的底层原理与具体实现。

实战篇作为本课程的重点,包含多个实战小项目,例如中台和领域建模的实战、微服务设计实战等。最后作者会用一个典型的案例将DDD所有的知识点串联,带你走一遍基于DDD的微服务设计全流程。

另外,实战篇还补充有“如何借鉴微服务的设计思想来设计前端应用”“微服务设计原则”以及“分布式架构设计的关键注意事项”,以完善实战体系。

限时福利

  1. 订阅后,分享专属海报,每邀请一位好友订阅有奖励。
  2. 戳此添加社群管理员,进入技术交流&福利群
  3. 戳此申请学生认证,订阅课程一律 5 折。
↵↵

订阅须知

  1. 本专栏为订阅专栏,更新时间为2019年10月14日至2019年12月02日。订阅成功后,即可通过“极客时间”App端、小程序端、Web端永久阅读。
  2. 本专栏更新时间为每周一、三、五,形式为图文+音频,共计21期,定价68元。
  3. 企业批量购买请点击“企业充值”了解详情,可支持员工选课,企业支付。
  4. 本专栏为虚拟商品,一经订阅,概不退款。
  5. 在专栏阅读过程中,如有任何问题,请邮件联系service@geekbang.org。
`, + column_unit: "21讲", + sub_count: 3878, + update_frequency: "每周一 / 三 / 五更新", + column_price: 5900, + column_price_market: 5900, + articles: [{ + id: 0, + is_video_preview: false, + article_title: '第一课' + }] +}] \ No newline at end of file diff --git a/chapter5/backend/mockdata/comment.js b/chapter5/backend/mockdata/comment.js new file mode 100644 index 0000000..e0eeb4b --- /dev/null +++ b/chapter5/backend/mockdata/comment.js @@ -0,0 +1,32 @@ +module.exports = [ + { + id: 1, + avatar: 'https://static001.geekbang.org/account/avatar/00/0f/52/62/1b3ebed5.jpg', + name: '僵尸浩', + isTop: true, + content: '哈哈哈哈', + publishDate: '今天', + commentNum: 10, + praiseNum: 5 + }, + { + id: 2, + avatar: 'https://static001.geekbang.org/account/avatar/00/0f/52/62/1b3ebed5.jpg', + name: '极客主编', + isTop: true, + content: '我来送大礼了!!', + publishDate: '上周', + commentNum: 10, + praiseNum: 2 + }, + { + id: 3, + avatar: 'https://static001.geekbang.org/account/avatar/00/0f/52/62/1b3ebed5.jpg', + name: '极客老板', + isTop: true, + content: '我来发股票了!!!', + publishDate: '十年前', + commentNum: 10, + praiseNum: 0 + } +] \ No newline at end of file diff --git a/chapter5/backend/proto/detail.proto b/chapter5/backend/proto/detail.proto new file mode 100644 index 0000000..9be9ab3 --- /dev/null +++ b/chapter5/backend/proto/detail.proto @@ -0,0 +1,28 @@ +message Column { + required int32 id = 1; + required string column_cover = 2; + required string column_title = 3; + required string column_subtitle = 4; + required string author_name = 5; + required string author_intro = 6; + required string column_intro = 7; + required string column_unit = 8; + required uint32 sub_count = 9; + required string update_frequency = 10; + required uint32 column_price = 11; + optional uint32 column_price_market = 12; + repeated Article articles = 13; +} +message Article { + required uint32 id = 1; + required bool is_video_preview = 2; + required string article_title = 3; +} + +message ColumnResponse { + required Column column = 1; + repeated Column recommendColumns = 2; +} +message ColumnRequest { + required int32 columnid = 1; +} \ No newline at end of file diff --git a/chapter5/backend/run.js b/chapter5/backend/run.js new file mode 100644 index 0000000..2e1c8e5 --- /dev/null +++ b/chapter5/backend/run.js @@ -0,0 +1,5 @@ +// require('./backend/comment-list') +// require('./backend/comment-praise') +// require('./backend/article') +require('./detail') +require('./article') \ No newline at end of file diff --git a/chapter5/business/play/data.js b/chapter5/business/play/data.js new file mode 100644 index 0000000..ff5cf7a --- /dev/null +++ b/chapter5/business/play/data.js @@ -0,0 +1,145 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "./workspace/src/page.data.js"); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./workspace/src/page.data.js": +/*!************************************!*\ + !*** ./workspace/src/page.data.js ***! + \************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +module.exports = { + column: { + protocol: 'geek-rpc', + + ip: '127.0.0.1', + + port: 4000, + + protobufFile: __webpack_require__(/*! ./workspace/src/proto/detail.proto */ "./workspace/src/proto/detail.proto"), + + requestStruct: 'ColumnRequest', + responseStruct: 'ColumnResponse', + + then(res) { + return res.column; + } + }, + articleList: { + protocol: 'http', + + url: 'http://127.0.0.1:4003', + + before: function (data) { + return data; + }, + + then: function (res) { + return JSON.parse(res).data//.list; + }, + + catch: function () { + + } + } +} + +/***/ }), + +/***/ "./workspace/src/proto/detail.proto": +/*!******************************************!*\ + !*** ./workspace/src/proto/detail.proto ***! + \******************************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +module.exports = "message Column {\n required int32 id = 1;\n required string column_cover = 2;\n required string column_title = 3;\n required string column_subtitle = 4;\n required string author_name = 5;\n required string author_intro = 6;\n required string column_intro = 7;\n required string column_unit = 8;\n required uint32 sub_count = 9;\n required string update_frequency = 10;\n required uint32 column_price = 11;\n optional uint32 column_price_market = 12;\n repeated Article articles = 13;\n}\nmessage Article {\n required uint32 id = 1;\n required bool is_video_preview = 2;\n required string article_title = 3;\n}\n\nmessage ColumnResponse {\n required Column column = 1;\n repeated Column recommendColumns = 2;\n}\nmessage ColumnRequest {\n required int32 columnid = 1;\n}" + +/***/ }) + +/******/ }); \ No newline at end of file diff --git a/chapter5/business/play/template.tpl b/chapter5/business/play/template.tpl new file mode 100644 index 0000000..ec25c46 --- /dev/null +++ b/chapter5/business/play/template.tpl @@ -0,0 +1,2721 @@ + + + + + + + + + + + + + + + 极客时间 | Nginx核心知识100讲 + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
下载APP
+
+
登录
+
+
+
+
+
+
+
+
+
关闭
+
+
+
+
讲堂
+
算法训练营
+
企业服务
+
热点资讯
+
极客商城
+
客户端下载
+
兑换中心
+
+
+
渠道合作
+
推荐作者
+
+
+
+
+
+
+
+
+ +
+ +
+

+
+
+
+
当前播放: 01 | 课程综述
+ +
+
+
+
+
+
+
+ + +
+
00:07 / 03:45
+
+
+
高清 +
    +
  • + 高清 +
  • +
  • + 标清 +
  • +
  • + 普清 +
  • +
+
+
1.0x +
    +
  • + 2.0x +
  • +
  • + 1.5x +
  • +
  • + 1.25x +
  • +
  • + 1.0x +
  • +
  • + 0.5x +
  • +
+
+
+
+
+
00:00
+
+
+
+
+
+
+ +
+
付费课程,可试看
+ +
+
+
+
+

${column.column_title}

+
+
+
+
${column.column_unit} · ${column.update_frequency}
+
${column.sub_count} +
+
+
+
+ +
+ ${articleList.map((item, index)=> { + return ` +
+ ${item.had_freelyread ? '
免费
' : ''} +
${item.article_title}
+
+ ` + }).join('')} +
+
+ +
+
 写留言
+

精选留言(47)

+
    +
+ +
+
+ +
订阅¥129
+
+ +
+
+
+
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/chapter5/package.json b/chapter5/package.json new file mode 100644 index 0000000..2eb837f --- /dev/null +++ b/chapter5/package.json @@ -0,0 +1,22 @@ +{ + "name": "refactor", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "easy_sock": "^0.3.8", + "koa": "^2.11.0", + "koa-mount": "^4.0.0", + "memory-fs": "^0.5.0", + "mkdirp": "^0.5.1", + "protocol-buffers": "^4.1.0", + "request": "^2.88.0", + "text-loader": "0.0.1", + "webpack": "^4.41.2" + } +} diff --git a/chapter5/readme.md b/chapter5/readme.md index ae8ad6d..a520fb6 100644 --- a/chapter5/readme.md +++ b/chapter5/readme.md @@ -1,3 +1,5 @@ -# 关于request-factory -可以参见以前在腾讯视频开源的npm库,虽然和视频中演示的代码不太一样,而且代码风格会稍微老些,但功能更全面。 -https://github.com/tvfe/pigfarm-fetcher.git \ No newline at end of file +* 目录结构 + * backend 后端服务内容 + * business 业务配置中心,模拟云函数架构存储函数内容的地方 + * server 服务端运行逻辑 + * workspace 模拟开发者工作空间 \ No newline at end of file diff --git a/chapter5/server/create-template.js b/chapter5/server/create-template.js new file mode 100644 index 0000000..9117610 --- /dev/null +++ b/chapter5/server/create-template.js @@ -0,0 +1,17 @@ +const vm = require('vm'); + +const templateContext = vm.createContext({}); + +function createTemplate(templateContent) { + + return vm.runInContext( + `(function (data) { + with (data) { + return \`${templateContent}\` + } + })`, + templateContext + ); +} + +module.exports = createTemplate \ No newline at end of file diff --git a/chapter5/server/request-factory.js b/chapter5/server/request-factory.js new file mode 100644 index 0000000..faca9e4 --- /dev/null +++ b/chapter5/server/request-factory.js @@ -0,0 +1,46 @@ +function factory(config) { + if (!requestors[config.protocol] && !requestors['default']) { + throw new Error(`暂不支持的协议: ${config.protocol}`); + } + config.before = config.before || (d => d); + config.then = config.then || (d => d); + config.catch = config.catch || (d => d); + + requestors[config.protocol].compile(config); + + return async function (data) { + + try { + data = config.before(data); + + } catch (e) { + // 如果beforeHook抛出了错误,则交给catch处理。开发者可以在before抛出的error上挂属性来让catch做一些分辨逻辑。 + config.catch(e); + // 如果catch没抛出其他错误,则认为此次请求是平安取消的 + return Promise.resolve(null) + } + + return { + + result: await requestors[config.protocol] + .request(data) + + // 如果config.then里抛出乐错误 + .then(config.then) + + // 如果config.catch返回了null,那这整个请求过程其实是不算失败的 + .catch(config.catch) + } + } +} +const requestors = {} + +/** + * @param protocol 数据源协议名字 + * @param requestor{function} 请求流程定义 + */ +factory.registerProtocol = function (protocol, requestor) { + requestors[protocol] = requestor; +} + +module.exports = factory; \ No newline at end of file diff --git a/chapter5/server/requestors/geek-rpc.js b/chapter5/server/requestors/geek-rpc.js new file mode 100644 index 0000000..5784a40 --- /dev/null +++ b/chapter5/server/requestors/geek-rpc.js @@ -0,0 +1,62 @@ +const EasySock = require('easy_sock'); +const protobuf = require('protocol-buffers') +const fs = require('fs'); + +let schemas = null; +let easySock = null; + +module.exports = { + + compile: function (config) { + schemas = protobuf( + config.protobufFile + ); + + easySock = new EasySock({ + ip: config.ip, + port: config.port, + timeout: 500, + keepAlive: true + }) + + easySock.encode = function (data, seq) { + const body = schemas[config.requestStruct].encode(data); + + const head = Buffer.alloc(8); + head.writeInt32BE(seq); + head.writeInt32BE(body.length, 4); + + return Buffer.concat([head, body]) + } + easySock.decode = function (buffer) { + const seq = buffer.readInt32BE(); + const body = schemas[config.responseStruct].decode(buffer.slice(8)); + + return { + result: body, + seq + } + } + easySock.isReceiveComplete = function (buffer) { + if (buffer.length < 8) { + return 0 + } + const bodyLength = buffer.readInt32BE(4); + + if (buffer.length >= bodyLength + 8) { + return bodyLength + 8 + + } else { + return 0 + } + } + }, + + request: async function (data) { + return await new Promise((resolve, reject) => { + easySock.write(data, function (err, data) { + err ? reject(err) : resolve(data) + }) + }) + } +} \ No newline at end of file diff --git a/chapter5/server/requestors/http.js b/chapter5/server/requestors/http.js new file mode 100644 index 0000000..75c57e4 --- /dev/null +++ b/chapter5/server/requestors/http.js @@ -0,0 +1,14 @@ +const request = require('request'); + +let url = ''; +module.exports = { + compile: function (config) { url = config.url }, + request: async function (data) { + + return await new Promise((resolve, reject) => { + request(url, (err, data) => { + err ? reject(err) : resolve(data.body); + }) + }) + } +} \ No newline at end of file diff --git a/chapter5/server/run.js b/chapter5/server/run.js new file mode 100644 index 0000000..2452648 --- /dev/null +++ b/chapter5/server/run.js @@ -0,0 +1,54 @@ +const mount = require('koa-mount') + +const createTemplate = require('./create-template'); + +requestFactory.registerProtocol('geek-rpc', + require('./requestors/geek-rpc') +); +requestFactory.registerProtocol('http', + require('./requestors/http') +); + +module.exports = function (app) { + const koa = new (require('koa')) + + koa.use(async (ctx, next) => { + if (ctx.url == '/favicon.ico') { + return; + } + await next(); + }) + + Object.keys(app).forEach(routepath => { + + const dataConfig = eval(app[routepath].data); + + const requests = Object.keys(dataConfig) + .reduce((ret, key) => { + ret[key] = requestFactory(dataConfig[key]); + return ret; + }, {}); + const template = createTemplate(app[routepath].template); + + koa.use( + mount(routepath, async (ctx) => { + ctx.status = 200; + + const result = {}; + await Promise.all( + Object.keys(requests).map(key => { + return requests[key](ctx.query) + .then(res => { + result[key] = res; + return res; + }) + }) + ) + + ctx.body = template(result); + }) + ); + }); + + koa.listen(3000); +} \ No newline at end of file diff --git a/chapter5/server/test.js b/chapter5/server/test.js new file mode 100644 index 0000000..20a216b --- /dev/null +++ b/chapter5/server/test.js @@ -0,0 +1,29 @@ +const server = require('./run') +const fs = require('fs'); + +(async function () { + + const data = await new Promise((resolve) => { + fs.readFile( + __dirname + '/../business/play/data.js', "utf-8", + function (err, data) { + resolve(data); + } + ) + }) + const template = await new Promise((resolve) => { + fs.readFile( + __dirname + '/../business/play/template.tpl', "utf-8", + function (err, data) { + resolve(data); + } + ) + }) + + server({ + '/play': { + data, + template + } + }); +})() \ No newline at end of file diff --git a/chapter5/workspace/build.js b/chapter5/workspace/build.js new file mode 100644 index 0000000..c262483 --- /dev/null +++ b/chapter5/workspace/build.js @@ -0,0 +1,7 @@ +const uploader = require('./uploader'); + +uploader( + 'play', + __dirname + '/src/page.data.js', + __dirname + '/src/play.template.html' +) \ No newline at end of file diff --git a/chapter5/workspace/src/page.data.js b/chapter5/workspace/src/page.data.js new file mode 100644 index 0000000..27c7af9 --- /dev/null +++ b/chapter5/workspace/src/page.data.js @@ -0,0 +1,35 @@ +module.exports = { + column: { + protocol: 'geek-rpc', + + ip: '127.0.0.1', + + port: 4000, + + protobufFile: require(`${__dirname}/proto/detail.proto`), + + requestStruct: 'ColumnRequest', + responseStruct: 'ColumnResponse', + + then(res) { + return res.column; + } + }, + articleList: { + protocol: 'http', + + url: 'http://127.0.0.1:4003', + + before: function (data) { + return data; + }, + + then: function (res) { + return JSON.parse(res).data//.list; + }, + + catch: function () { + + } + } +} \ No newline at end of file diff --git a/chapter5/workspace/src/play.template.html b/chapter5/workspace/src/play.template.html new file mode 100755 index 0000000..ec25c46 --- /dev/null +++ b/chapter5/workspace/src/play.template.html @@ -0,0 +1,2721 @@ + + + + + + + + + + + + + + + 极客时间 | Nginx核心知识100讲 + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
下载APP
+
+
登录
+
+
+
+
+
+
+
+
+
关闭
+
+
+
+
讲堂
+
算法训练营
+
企业服务
+
热点资讯
+
极客商城
+
客户端下载
+
兑换中心
+
+
+
渠道合作
+
推荐作者
+
+
+
+
+
+
+
+
+ +
+ +
+

+
+
+
+
当前播放: 01 | 课程综述
+ +
+
+
+
+
+
+
+ + +
+
00:07 / 03:45
+
+
+
高清 +
    +
  • + 高清 +
  • +
  • + 标清 +
  • +
  • + 普清 +
  • +
+
+
1.0x +
    +
  • + 2.0x +
  • +
  • + 1.5x +
  • +
  • + 1.25x +
  • +
  • + 1.0x +
  • +
  • + 0.5x +
  • +
+
+
+
+
+
00:00
+
+
+
+
+
+
+ +
+
付费课程,可试看
+ +
+
+
+
+

${column.column_title}

+
+
+
+
${column.column_unit} · ${column.update_frequency}
+
${column.sub_count} +
+
+
+
+ +
+ ${articleList.map((item, index)=> { + return ` +
+ ${item.had_freelyread ? '
免费
' : ''} +
${item.article_title}
+
+ ` + }).join('')} +
+
+ +
+
 写留言
+

精选留言(47)

+
    +
+ +
+
+ +
订阅¥129
+
+ +
+
+
+
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/chapter5/workspace/src/proto/detail.proto b/chapter5/workspace/src/proto/detail.proto new file mode 100644 index 0000000..9be9ab3 --- /dev/null +++ b/chapter5/workspace/src/proto/detail.proto @@ -0,0 +1,28 @@ +message Column { + required int32 id = 1; + required string column_cover = 2; + required string column_title = 3; + required string column_subtitle = 4; + required string author_name = 5; + required string author_intro = 6; + required string column_intro = 7; + required string column_unit = 8; + required uint32 sub_count = 9; + required string update_frequency = 10; + required uint32 column_price = 11; + optional uint32 column_price_market = 12; + repeated Article articles = 13; +} +message Article { + required uint32 id = 1; + required bool is_video_preview = 2; + required string article_title = 3; +} + +message ColumnResponse { + required Column column = 1; + repeated Column recommendColumns = 2; +} +message ColumnRequest { + required int32 columnid = 1; +} \ No newline at end of file diff --git a/chapter5/workspace/uploader.js b/chapter5/workspace/uploader.js new file mode 100644 index 0000000..7a6cdd9 --- /dev/null +++ b/chapter5/workspace/uploader.js @@ -0,0 +1,43 @@ +const fs = require('fs'); +const mkdirp = require('mkdirp'); +const webpack = require('webpack'); +const mfs = new (require('memory-fs')); + +module.exports = function ( + businessName, + dataJSPath, + templatePath +) { + mkdirp.sync(__dirname + '/../business/' + businessName); + + fs + .createReadStream(templatePath) + .pipe(fs.createWriteStream(__dirname + '/../business/' + businessName + '/template.tpl')); + + const compileTask = webpack({ + mode: 'development', + devtool: false, + target: 'node', + + entry: dataJSPath, + + module: { + rules: [ + { test: /.proto$/, use: 'text-loader' } + ] + }, + + output: { + path: "/whatever", + filename: "data.js" + } + }); + + compileTask.outputFileSystem = mfs; + + compileTask.run(function(err) { + if (err) { return } + const content = mfs.readFileSync('/whatever/data.js') + fs.writeFileSync(__dirname + '/../business/' + businessName + '/data.js', content); + }) +} \ No newline at end of file diff --git a/lottery/index.js b/lottery/index.js new file mode 100644 index 0000000..76bf930 --- /dev/null +++ b/lottery/index.js @@ -0,0 +1,39 @@ +const list = [ + "Objectivezt", + "田大头", + "Geek_d4bf9b", + "筑梦师刘渊", + "数字Gannon", + "🌊", + "朋克是夏天的冰镇雪碧", + "許敲敲", + "tuyu", + "Middleware", + "menghai", + "小北", + "Sky-fly", + "渭河", + "micstone", + "Mr-L.x.D..☻", + "阿秀", + "葛维维", + "许童童", + "1024", + "Serendipity", + "莫奈", + "九", + "Glee", + "如也", + "忘了i.", + "刘彪" +] + +console.log('恭喜以下几位同学'); +for (let i = 0; i < 3; i++) { + const random = Math.floor(Math.random() * list.length); + + console.log(list[random]); + + list[random] = list[list.length - 1]; + list.length--; +} \ No newline at end of file