天天看點

阿裡雲函數計算 + Aglio 實作 API Blueprint markdown 渲染

本文首發于我的部落格 阿裡雲函數計算 + Aglio 實作 API Blueprint markdown 渲染

這次想聊聊我在寫的站裡的一個核心的功能——API 文檔的編輯與展示。我最初的想法是,使用 Markdown 來編寫文檔。但是這樣在格式上難免不好統一。跟

@BillSJC

聊的時候,他向我推薦了 Swagger 和 API Blueprint。這兩個都是 API 文檔自動生成工具。對比過後,我決定選擇 API Blueprint,因為它是使用類似 markdown 的文法進行文檔編寫的,相比 Swagger 的使用 YAML,API Blueprint 寫的 markdown 可讀性更強一些:

# GET /message
+ Response 200 (text/plain)

        Hello World!           

地鼠不友好

API Blueprint 的官網上

https://apiblueprint.org/

推薦了從文檔編輯到解析、渲染的諸多工具支援。

我的需求是找到一個 API Blueprint 的渲染工具,将我在 Apicon 編寫的 markdown 渲染轉換成 HTML 文檔。但官網展示的這些工具大多都是 Node.js 編寫的,我并沒有找到支援 Golang 的工具。但我又不想額外在伺服器上再維護一個容器,用于專門運作轉換 markdown 的 Node.js 代碼。

這時,

向我介紹了 Serverless 的概念,同時推薦了阿裡雲的函數計算。

Serverless?

從字面上意思來了解 Serverless,即無伺服器。

将這個概念放在阿裡雲的函數計算服務上,即我隻需要将我實際的業務代碼上傳到函數計算服務上,然後通過約定好的觸發器去調用這個服務。在這其中,我并不需要去關心伺服器相關的硬體配置,環境配置,以及運維等這些事情。我隻需要專注于業務代碼即可。

這可能和我們平時做的 RESTful API 有些相似,RESTful API 其實也可以看做是通過一個 HTTP 觸發器,去調用相關服務。雖然二者的最終結果都是一樣的,但 RESTful API 的背後,往往是我們的伺服器一直在監聽是否有請求,一旦有請求來了,就進行處理;而函數計算這個“事件驅動”更像是一個被動的東西,被觸發後執行一段事先設定好的步驟,然後結束。

就像一個函數一樣,每次執行都是差不多的步驟和内容,前後之間的兩次調用并不會有太大的關聯和影響。我們可以來看下大廠都在用它在幹什麼:

新浪微網誌 - 調用函數将使用者上傳的圖檔進行個性化處理

115 - 觸發函數對使用者上傳的日志進行壓縮、轉換

芒果 TV - 觸發函數記錄視訊檔案的屬性資訊

石墨文檔 - 使用函數來處理多使用者文檔編輯的沖突

這麼一看我是選對了,新浪都用來處理圖檔了,那我用來處理 markdown 豈不也是合情合理?嘛,開始吧!

Aglio 渲染 markdown

API Blueprint 的渲染器我之是以選擇 Aglio

https://github.com/danielgtaylor/aglio

,是因為它不僅可以自定義主題,還可以自定義模闆。這樣我就可以将頁面中我不需要的标題、導航欄等内容删除,僅保留内容主體。同時 Aglio 不僅支援 CLI 使用,還可以當做一個包,放在項目中調用。

我們先在本地實作一個 markdown 的渲染,再将它改寫成函數計算的模式。

先把 Aglio 給拉下來:

yarn add aglio           

然後我們就可以開始編寫主要的代碼了:

let aglio = require('aglio')
let blueprint = `
# GET /message
+ Response 200 (text/plain)
        Hello World!
`

options = {
    themeTemplate: 'index.jade'
};

aglio.render(blueprint, options, function (err, html, warnings) {
    if (err) return console.log(err);
    if (warnings) console.log(warnings);
    console.log(html);
});           

這裡我使用的是 Aglio 的預設主題,你可以在 Aglio GitHub Repo 的

olio-theme

分支下載下傳到主題需要的

index.jade

mixins.jade

這兩個模闆檔案。然後就可以開始魔改這兩個

jade

檔案了:

doctype

include mixins

html
    head
        meta(charset="utf-8")
        style!= self.css
    body.preload
        div
            .row
                .content
                    block content
                        +Content('primary', false)           

我這邊将導航欄,标題欄、不用的 JavaScript 都去掉了。然後

mixins.jade

也是按需修改,相關英文的提示我也進行了漢化。最後的效果是這樣的:

阿裡雲函數計算 + Aglio 實作 API Blueprint markdown 渲染

下面我們來将其部署到阿裡雲函數計算上。

部署到阿裡雲函數計算

因為我們這裡的檔案很多,故使用阿裡雲提供的

fun

工具來進行部署。

Mac 安裝

fun

brew install fun           

安裝好後,先輸入

fun config

來跟着指令行提示進行基本的設定:

Aliyun Account ID (阿裡雲ID)
Aliyun Access Key ID ***************    (AK)
Aliyun Secret Access Key ***************    (SAK)
Default region name cn-hongkong        (地區,我是選擇跟自己輕量應用一樣的地區,友善直接内網調用)
The timeout in seconds for each SDK client invoking 300        (上傳代碼逾時時間)
The maximum number of retries for each SDK client 3        (上傳失敗後的嘗試次數)
Allow to anonymously report usage statistics to improve the tool over time? No        (是否幫助阿裡雲改進此工具)           

有坑注意

第一項

Aliyun Account ID

填寫的是阿裡雲的賬号 ID,并不是登入時填寫的賬号。賬号 ID 可以在

基本資料

-

安全設定

中找到。

之後就可以執行

fun init

來初始化一個服務了。它會給我們建立

template.yml

檔案,我們進行如下配置:

ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
  Aglio-Function:
    Type: 'Aliyun::Serverless::Service'
    Properties:
      Description: 'Markdown render'
    Aglio-Function:
      Type: 'Aliyun::Serverless::Function'
      Properties:
        Handler: index.handler
        Runtime: nodejs8
        CodeUri: './'
      Events:
        http-test:
          Type: HTTP
          Properties:
            AuthType: ANONYMOUS
            Methods: ['POST']           

注意這裡的

Events

,即建立了一個 HTTP 觸發器,僅允許使用 POST 進行調用,且不做鑒權。

在所有觸發器中,HTTP 觸發器隻能在建立服務時添加,之後無法再添加。是以,在使用

fun

部署項目時,我們通過

template.yml

檔案建立。

下面編寫

index.js

,将我們的業務代碼與阿裡雲函數計算的入口進行對接:

var getRawBody = require('raw-body');
module.exports.handler = function(req, resp, context) {
    let options = {
        themeTemplate: 'index.jade'
    };

    let aglio = require('aglio')
    getRawBody(req, function(err, body) {
        aglio.render(body.toString(), options, function (err, html, warnings) {
            if (err) return console.log(err);
            if (warnings) console.log(warnings);
            resp.send(html);
        });
    });            

這裡通過引入

require('raw-body');

來擷取請求的 body,通過 Aglio 處理後傳回。

之後執行

fun deploy

部署服務。注意:

node_modules

檔案夾的内容也要一并上傳,函數計算不會再次進行拉包。

魔改 Aglio

在阿裡雲函數計算的控制台中調試我們剛才部署的函數,發現會報 502 錯誤。

問題出在 Aglio 在渲染 markdown 時,會往

node_modules

目錄下寫入一個

JavaScript

的臨時緩存檔案。但阿裡雲的函數計算隻開放了

/tmp

目錄下的寫入權限,其餘都是隻讀。

那麼這裡我隻能魔改 Aglio 源碼了。還好函數計算不會使用包管理拉包,它就隻用我們傳上去的

node_modules

問題出在 Aglio 的檔案寫入那裡,我們隻需更改寫檔案的目錄即可:

/node_modules/aglio-theme-olio/lib/main.js

檔案,第 144 行與第 249 行:

// Line 144
//compiledPath = path.join(ROOT, 'cache', (sha1(key)) + ".css");
compiledPath = path.join('/tmp', (sha1(key)) + ".css");

// Line 249
//compiledPath = path.join(ROOT, 'cache', (sha1(key)) + ".js");
compiledPath = path.join('/tmp', (sha1(key)) + ".js");           

然後寫入的 JavaScript 的臨時檔案中,有使用

require

引入

node_modules

目錄其它的包,這裡的相對路徑我們改成絕對路徑,否則還是會報錯 502。

還是在

main.js

檔案下,第 236 行:

// Line 236
//return compiled = "var jade = require('jade/runtime');\n" + (jade.compileFileClient(filename, options)) + "\nmodule.exports = compiledFunc;";
return compiled = "var jade = require('/code/node_modules/jade/runtime');\n" + (jade.compileFileClient(filename, options)) + "\nmodule.exports = compiledFunc;";           

之後再部署,就可以看到啦~

阿裡雲函數計算 + Aglio 實作 API Blueprint markdown 渲染

總結一下吧~

又學到了新的玩意兒呢。對于一些功能單調、而又十分常用的功能,又不想專門放在伺服器上花心思去維護,不妨可以嘗試一下阿裡雲的函數計算。Serverless 這個東西,用得好真的可以解決很多問題。

阿裡雲的

fun

工具很好用,讓我能快速部署進而能進行調試。這一次下來也踩了很多坑,感覺阿裡雲的這個僅

/tmp

目錄可寫的設定就很蠢。他為何不能改成目前代碼執行目錄可寫呢?然後 Aglio 方面,Aglio 可以說是 API Blueprint 目前可定制度最高的工具了,這點不可否認。像臨時緩存檔案目錄寫死在

node_modules

,這個從開發的角度其實也能了解啦。我一開始也不相信自己能魔改成功的哈哈。

然後關于函數計算的定價方面:

這個真的不用太擔心,阿裡雲财大氣粗。

阿裡雲函數計算 + Aglio 實作 API Blueprint markdown 渲染

每月免費次數 100 萬次!!

128Mb 每月免費秒數:3200000秒!!換算一下大概是 37 天。也就是說我即使一個月内一秒一次地調用都夠。

是以對于個人使用者來說,函數計算真的是完全免費了。

這裡需要注意一下阿裡雲記憶體的計算方式,他并不是按照函數運作時的實際記憶體占用計算的,而是按照你給函數運作環境設定的執行記憶體計算。

也就是說我設定 1024 Mb 的最大執行記憶體,即使實際調用時遠遠沒有用到這麼多,阿裡雲還是按照 1024 Mb 來計費。

推薦是設定實際運作記憶體占用的兩倍大小。像我這個 markdown 渲染,一次實際占用記憶體 66 Mb 左右,是以我選擇的是 128 Mb 的執行記憶體。

接下來要做的就是關掉函數計算的公網通路,然後讓 ECS 從内網調用它。如果可以的話,再把函數計算的日志給打開,後面還要繼續探索。(≧∇≦)ノ

繼續閱讀