歡迎關注知乎專欄 —— 前端的逆襲
歡迎關注個人博客,知乎,GitHub。html
原文首發於我的博客:Serverless 實戰:打造我的閱讀追蹤系統 | 呂立青的博客前端
在互聯網時代,知識能夠說從未像如此通常廉價,可是再好的知識如果對我的沒有產生價值的話,那也只不過是一種信息噪音而已。我在 我的知識管理:知識的三種形態 這篇文章中使用 材料 -> 資料 -> 知識
這樣的路徑來解釋信息的流通,如何方便快捷而且有效地收集材料,再將其整理轉化爲有價值的我的知識體系結構,在這個信息嚴重碎片化的時代變得尤其重要。而在 去僞存真的知識管理之路 一文中也詳細闡述瞭如何將網絡上的碎片化文章歸入統一的稍後閱讀體系,好比說有時候在朋友圈看到一篇好文章,但暫時沒時間直接看,或是這篇文章值得再讀一遍,細讀一遍,那麼我就會將其存入稍後閱讀工具即 Instapaper 當中,諸如此類的還有 Pocket、收趣等等。git
隨着時間的推移,Instapaper 裏面的文章將會變得愈來愈多,就像咱們在代碼中所註釋的 TODO:
可能就變成了 Never Do
,稍後讀也是同樣地被人廣爲詬病:Read it Later = Read Never。其實我發現文章堆積的一個永恆痛點就是沒有有效的方式追蹤本身的閱讀需求與能力,其核心緣由就是由於讀完的速度趕不上添加的速度。從而沒辦法可視化出來評估本身的閱讀進度,合理安排閱讀計劃,也就沒辦法給予本身適當的獎勵,進而失去了閱讀的動力。github
上回博客大賽寫過一篇 基於 GitHub 的敏捷學習方法之道與術,其中提到使用 GitHub Issue 來管理本身的學習計劃,從而就又產生了這麼一個想法,就是將個人稍後閱讀列表跟 GitHub 結合起來,使用 ZenHub 豐富的圖表功能將閱讀體系進行追蹤與可視化。web
首先讓咱們直接來看一下最終的具體效果圖,在這裏簡單介紹一下 CFD(Cumulative Flow Diagram)即累積流圖,這是一種能讓你快速瞭解項目或產品工做概況的圖表,關注的是價值的流動效率,價值的流動最直接的體現就是需求卡片在各個隊列中的數量。express
裏特定律(Little’s law)告訴咱們交付時間(Delivery time)依賴於在製品數量(Work In Progress, WIP)。WIP 是指全部已經初始但還未完成的工做,例如:全部在分析(Analysis)與完成(Done)之間的工做。必需要首先留意的就是 WIP,若是 WIP 增長了,交付日期就會有風險。ZenHub 所提供的 Release Report 中最有效果的就是預測完成日期,總之就是跟敏捷方法結合起來,使用項目管理的方式來管理本身的閱讀列表,固然其實我也在進一步的探索之中,可是每次看看到這個走勢圖就能對本身的閱讀列表有了更多的掌控和理解,至少減小了文章堆積時所產生的焦慮感。npm
那麼這背後是怎麼經過 APIs 來實現的呢?固然,在真正進入正題以前咱們先來簡單介紹一下 Serverless 架構。Serverless 指的是在構建 Web 應用程序的時候,而不用擔憂如何配置服務器,可是這並不意味着應用程序不會在服務器上運行,而是說服務器的管理均可以儘量地交給相應的雲平臺,從而最大程度地減輕開發人員的部署與配置工做。與之對應的一個名詞可能就是 Function As a Service(FAAS),由 AWS Lambda 這個命名上就能想到,當咱們在構建 Serverless 架構時,實際上咱們是在寫一個個的 Function 即函數而已。編程
首先讓咱們來介紹一下 IFTTT 即 if this then that 的縮寫。通俗的來說,IFTTT 的做用就是若是觸發了一件事,則執行設定好的另外一件事。所謂的「事」,指的是各類應用、服務之間能夠進行有趣的連鎖反應。IFTTT 的宗旨是 Put the internet to work for you (讓互聯網爲你服務)。用戶能夠在 IFTTT 裏設定任何一個你須要的條件,當條件達到時,便會觸發下一個指定好的動做。它就像是一座神奇的橋樑,能鏈接咱們平常所用的各類網絡服務。json
而咱們如今遇到的這個串聯式的場景下是特別合適 Serverless 架構的,使用 IFTTT 而且將它跟 Instapaper 帳號綁定,設置文章添加、高亮、歸檔等行爲做爲 trigger 條件,而後將相關信息發到某一個指定 API endpoint。先把操做 GitHub Issue 和 ZenHub 的各類 APIs 準備好,結合 IFTTT 的觸發器與 Marker 工具可以很是方便地與之相集成,最後咱們能夠產出這樣一個 APIs 交互流程圖:api
雖然 AWS Lambda 是 Serverless 架構的典範,但它也有一些槽點以及我以爲已經被人說得足夠多了,因此咱們今天就來嚐嚐鮮,着重介紹和使用一下 Webtask。推出該服務的這家公司 Auth0 你可能沒有據說過,但你一直知道大名鼎鼎的 JWT 即 JSON Web Token,這是一種開放標準(RFC 7519),一般被運用在身份驗證(Authentication)和信息交換等須要安全傳輸信息的場景下。
首先讓咱們來安裝工具初始化項目以及註冊帳號,而後使用電子郵件進行登陸:
npm install -g wt-cli
wt init <YOUR-EMAIL>複製代碼
建立項目目錄,添加 index.js
文件並添加如下內容:
module.exports = function (cb) {
cb(null, 'Hello World');
}複製代碼
而後在該目錄中運行如下命令進行應用程序部署以後,點擊控制檯中輸出的 URL 就能看到編程史上最有名氣沒有之一的 HelloWorld!
:
wt create index複製代碼
Webtask 有一個實用工具 webtask-tools
能夠將應用程序綁定到 Webtask 上下文,讓咱們將以前所 export 的簡單函數修改成綁定到 Webtask 的 Express app,而後就能夠愉快地使用 Express 進行開發,一切就又回到了熟悉的味道:
const Express = require('express')
const Webtask = require('webtask-tools')
const bodyParser = require('body-parser')
const app = Express()
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
require('./routes/reading')(app)
module.exports = Webtask.fromExpress(app)複製代碼
Webtask context 還有一個很是重要的用途就是在部署時傳輸一些敏感信息好比安全 Token,從而在應用程序當中能夠隨時使用它們。下面的部署命令中 --secret
後面所傳入的 ACCESS_TOKEN
都會在後續與 GitHub 和 ZenHub APIs 交互時被用到。
wt create index --bundle --secret GITHUB_ACCESS_TOKEN=$GITHUB_ACCESS_TOKEN --secret ZENHUB_ACCESS_TOKEN=$ZENHUB_ACCESS_TOKEN --secret ZENHUB_ACCESS_TOKEN_V4=$ZENHUB_ACCESS_TOKEN_V4複製代碼
# ./routes/reading.js
module.exports = (app) => {
app.post('/reading', (req, res) => {
const { GITHUB_ACCESS_TOKEN, ZENHUB_ACCESS_TOKEN, ZENHUB_ACCESS_TOKEN_V4 } = req.webtaskContext.secrets
}
}複製代碼
得益於 IFTTT 很是豐富的第三方服務,IFTTT 能夠直接建立 Instapaper 與 GitHub Issue 相集成的 Applet:If new item saved, then create a new issue - IFTTT,就能夠在當 Instapaper 新增文章的時候,自動在 GitHub 所指定的倉庫 Issues · JimmyLv/reading 中建立一個新的 Issue 並添加相應的標題、連接以及描述等相關信息。
但僅僅只是添加一個 Issue 還不夠,這時候還須要將這個 Issue 加入到指定的 Milestone 從而利用 ZenHub 的圖表功能,使用 GitHub 的 Webhooks 功能就能夠輕鬆幫咱們把 Issue 更新的狀態轉發到咱們所指定的 webtask 地址:
因此咱們的 Webtask 就須要處理 GitHub Webhook 所轉發的 POST 請求,其中包括了 Issue 的類型和內容,在拿到 'opened'
即新建 Issue 類型的 action 以後咱們能夠對其進行相應的處理即添加到 Milestone 當中:
if (action === 'opened') {
fetch(`${url}?access_token=${GITHUB_ACCESS_TOKEN}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
milestone: 1
})
})
.then(() => console.info(`[END] set milestone successful! ${html_url}`))
.catch(err => res.json(err))
}複製代碼
結合 ZenHub 的 Milestone 燃盡圖咱們能夠清晰地看到剩餘閱讀量的多少,而且可以跟理想中的閱讀速度進行對比,從而判斷本身何時可以所有讀完全部的文章。可能有些小夥伴看到這裏會有所疑問了,這些所謂的 Story Point 是從哪兒來的呢?那麼接下來就要提到咱們將要集成的 ZenHub API 了。
GitHub Issue 的任何變更都會觸發 Webhook,從而咱們能夠在 Issue 被加入 Milestone 以後再處理下一個 'milestoned'
action,即:
if (action === 'milestoned') {
fetch(`https://api.zenhub.io/p1/repositories/${REPO_ID}/issues/${number}/estimate?access_token=${ZENHUB_ACCESS_TOKEN}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ estimate: 1 })
})
.then(() => {
console.info(`[END] set estimate successful! ${html_url}`)
return fetch(`https://api.zenhub.io/v4/reports/release/591dc19e81a6781f839705b9/items/issues?access_token=${ZENHUB_ACCESS_TOKEN_V4}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `add_issues%5B0%5D%5Bissue_number%5D=${number}&add_issues%5B0%5D%5Brepo_id%5D=${REPO_ID}`
})
})
.then(() => console.info(`[END] set release successful! ${html_url}`))
.catch(err => res.json(err))
}複製代碼
這樣咱們就完成了對每一個 GitHub Issue 的估點,以及設置了對應的 Release,接下來全部的變更都將體如今 ZenHub 的圖表當中。
說了這麼多,不要忘了整個閱讀系統最最核心的部分依然仍是要「閱讀」啊!在衆多的稍後閱讀工具中我無比喜好 Instapaper 並遲遲沒有轉到 Diigo 的緣由就在於它優秀、簡潔、純粹的閱讀體驗,讓人能夠專一在閱讀自己這件事情上,在被 Pinterest 收購以後更是將全部的諸如全文搜索、無限高亮/筆記、速讀等 Premium 功能都變成了免費,豈不美哉?
那麼在完成閱讀歸檔以後,最後一步就是在 GitHub 當中將 Issue 關閉掉,可是 IFTTT 的 GitHub 服務並無提供 close Issue 的接口,因而乎咱們就只有利用 IFTTT 新推出的 Maker 本身建立一個,即 Instapaper 規劃做爲一個 IF trigger,而後 Maker 用於發出一個 Web 請求,能夠是 GET ,PUT, POST, HEAD, DELETE, PATCH 或者 OPTIONS 之中的任何一種,你甚至還能夠制定 Content Type 和 Body。
app.get('/reading', (req, res) => {
const { GITHUB_ACCESS_TOKEN } = req.webtaskContext.secrets
const title = req.query.title
let keyword = encodeURIComponent(title.replace(/\s/g, '+'))
fetch(`https://api.github.com/search/issues?q=${keyword}%20repo:jimmylv/reading`)
.then(response => response.json())
.then(data => {
console.info('[RESULT]', data)
if (data.total_count > 0) {
data.items.forEach(({ url, html_url }) =>
fetch(`${url}?access_token=${GITHUB_ACCESS_TOKEN}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ state: 'closed' }),
})
.then(() => console.info(`[END] issue closed successful! ${html_url}`))
.catch(err => res.json('error', { error: err })))
res.json({ message: 'Closed issue successful!' })
}
})
}複製代碼
上述代碼就能夠用於處理 IFTTT Marker 所發送的 GET 請求,咱們從 query 參數中取到文章標題以後再去搜索相對應的 Issues,再經過 GitHub API 將其關閉。
而與此同時,咱們在文章的閱讀過程當中,有時候也會想要對文章中的亮點部分進行高亮,甚至評論本身的一些想法和總結,那咱們也能夠用過 IFTTT Marker 和 Webtask 的套路添加至 GitHub Issues 的 comments 當中,具體的代碼就不貼了,更多內容都已經放在個人 GitHub 上:JimmyLv/demo.serverless-mern。
隨着時間的推移,平常你只須要在 Instapaper 添加文章、閱讀文章便可,而背後利用 Serverless 所搭建的整套閱讀追蹤系統將會不辭辛苦的幫你記錄下全部的蹤影和筆記,你只須要在特定的時候按期 review、分析閱讀的效果與預測效果,與此同時結合本身的時間統計系統,能夠持續不斷地改進本身的閱讀目標與閱讀計劃。
最後再來考慮一下後續計劃,就好比說我如今只是簡單把 Instapaper 中高亮部分和閱讀筆記做爲評論放到 GitHub 的 comments 裏面,可是最終我須要把它收藏到本身的我的知識庫即 Diigo,這也是能夠經過 API 自動實現的,以及最終須要被刻意記憶的部分還須要與 Tinycards API 相集成,對抗艾賓浩斯遺忘曲線。
與此同時呢,還須要把估點根據不一樣的文章類型和難易程度具體劃分一下,而不是如今簡簡單單的 1 點,好比說 Instapaper 也有根據字數所預測的閱讀分鐘數,以及根據中文或英文、技術或雞湯等不一樣種類文章閱讀難度進行區分,從而使整套追蹤系統更具備效性與參考性。