Serverless 架構應用開發指南:建立自己的 Serverless 短鏈服務
在想用 Serverless 可以做點什麼簡單的線上應用後,我想到了一個是線上短鏈生成服務。最後的結果見:http://x.pho.im/,一個非常簡單的線上應用。
因為上面的程式碼中,不能自動建立域名。然後,再針對資料庫進行了一些優化。
程式碼邏輯
這裡的程式碼邏輯比如簡單:
- 建立短鏈時,使用生成一個四位的字串
- 將原有的 URL 和生成的 URL 儲存到 DynamoDB 中
- 在返回的 HTML 中,輸出對應的 URL
- 重定向時,從 DynamoDB 讀取對應的短鏈
- 如果短鏈存在,則執行 302 重定向;如果不存在,則返回一個 404。
建立首頁
首頁只是一個簡單的 HTML 表單:
const base_page = `<html>
<h1>Hi!</h1>
<form method="POST" action="">
<label for="uri">Link:</label>
<input type="text" id="link" name="link" size="40" autofocus />
<br/>
<br/>
<input type="submit" value="Shorten it!" />
</form >
</html>`
module.exports.handler = (event, context, callback) => {
console.log(JSON.stringify(event));
callback(
null,
{
statusCode: 200,
body: base_page,
headers: {'Content-Type': 'text/html'},
}
);
}
當我們提交的時候,就會觸發對應的 POST 請求。
生成短鏈
如上所述,對於個短鏈請求,我們要做這麼幾件事:
- 解析出提交表單中的連結
- 根據 URL 生成對應的短鏈
- 將對應的 URL 和短鏈的對應關係儲存到 DynamoDB 中
- 如果成功,則返回生成的短鏈;失敗則,返回一個 400
事實上,在儲存 URL 和短鏈的 map 之前,我們應該先判斷一下資料中是否已經有相應的短鏈。不過,對於這種只針對於我一個使用者的短鏈服務來說,這個步驟有點浪費錢——畢竟要去掃描一遍資料庫。所以,我也不想去新增這樣的擴充套件功能。
接下來,讓我們回到程式碼中去,程式碼的主要邏輯都是在 Promise 裡,按順序往下執行。
解析出提交表單中的連結
首先,我們通過 querystring
庫來解決中表單中的連結。
const submitted = querystring.parse(event.body).link;
根據 URL 生成對應的短鏈
接著,使用 Node.js 中的 crypto.randomBytes
方法來生成八位的偽隨機碼。
crypto.randomBytes(8)
.toString('base64')
.replace(/[=+/]/g, '')
.substring(0, 4)
由於生成的偽隨機碼是 Buffer 型別,因此需要轉換為字串。同時,因為生成的短鏈中不應該有 “=+/”,它會導致生成的 URL 有異常。於是,我們便替換掉偽隨機碼中的這些特殊字型。最後,擷取生成的字串的前 4 位。
現在,我們就可以將其儲存到資料中了。
儲存到 Dynamo 資料庫中。
對應的儲存邏輯如下所示,我們 new 了一個 DocumentClient 物件,然後直接儲存到資料庫中。put
函式中的物件,即是對應的引數。
return docClient.put({
TableName: tableName,
Item: {
slug: slug,
url: submitted
},
Expected: {
url: {Exists: false}
}
}).promise().then(() => { return slug; });
最後,我們返回了 slug
,用於接下來的處理。
返回短鏈給使用者
一切處理正常的話,我們將向用戶返回最後的內容:
return callback(
null,
{
statusCode: 200,
body: RenderPage(path.join(prefix, slug).replace(':/', '://'), prefix),
headers: {'Content-Type': 'text/html'}
}
);
其中的 HTML 部分的渲染邏輯如下所示:
function RenderPage (link, submitted) {
return `
<html>
<body>
<h3>
<a href="${link}">${link}</a>
</h3>
<p>URL ${submitted} was shortened to:
<a href="${link}">${link}</a>
</p>
</body>
</html>`
};
是的,只是返回短鏈和原有的連結了。
好了,現在我們已經擁有這個短鏈了。接下來,就是點選這個短鏈,看看背後會發生些什麼?
重定向短鏈
首先,我們先在我們的 serverless.yml
中,將短鏈的路徑配置為引數:
functions :
...
redirect:
handler: redirect/index.handler
events:
- http:
path: /{slug}
method: get
然後,從資料庫中按短鏈的 slug 查詢對應的 URL:
const slug = event.pathParameters.slug;
docClient.get({
TableName: tableName,
Key: {
slug: slug
}
}, (err, data) => {
})
如果存在對應的短鏈,則 302 重定向對原有的 URL:
const item = data.Item;
if (item && item.url) {
callback(
null,
{
statusCode: 302,
body: item.url,
headers: {
'Location': item.url,
'Content-Type': 'text/plain'
}
}
)
}
如果沒有,則返回一個 404。
我們的程式碼就是這麼的簡單,現在讓我們來部署測試一下。
部署及測試短鏈服務
如果你還沒有 clone 程式碼的話,執行下面的命令來安裝:
serverless install -u https://github.com/phodal/serverless-guide/tree/master/url-shorter -n url-shorter
然後執行 yarn install
來安裝對應的依賴。
如果你在 Route53 上註冊有相應的域名,修改一下 serverless.yml
檔案中的域名,我們就可以使用 serverless create_domain
來建立域名的路由。
緊接著,執行 serverless deploy
來部署。
api keys:
None
endpoints:
GET - https://4rr5ndhaw3.execute-api.us-east-1.amazonaws.com/dev/
POST - https://4rr5ndhaw3.execute-api.us-east-1.amazonaws.com/dev/
GET - https://4rr5ndhaw3.execute-api.us-east-1.amazonaws.com/dev/{slug}
functions:
main: url-shorter-dev-main
create: url-shorter-dev-create
redirect: url-shorter-dev-redirect
Serverless Domain Manager Summary
Domain Name
x.pho.im
Distribution Domain Name
d2s4y0p5nuw3k7.cloudfront.net
Serverless: Removing old service versions...
一切準備就緒了。
Done!