欢迎关注知乎专栏 —— 前端的逆袭
欢迎关注个人博客,知乎,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 也有根据字数所预测的阅读分钟数,以及根据中文或英文、技术或鸡汤等不一样种类文章阅读难度进行区分,从而使整套追踪系统更具备效性与参考性。