天天看點

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

目的 最近 Serverless 愈來愈火,我剛好在教育訓練,比較有時間去嘗試一些新東西,是以趁這個時候去使用下 Serverless,嘗試使用 Typescript 和 nodejs 開發,部署在騰訊雲 SCF 上的一個小工具,探讨下 Typescript+ Node.js + SCF  的最好實踐模式,并同時抛鑽引玉,希望有同學提供更好 的方案。   項目介紹 一、想法 本人有時候會追劇,但是劇集更新時,我一般都是不知道的。隻有空閑的時候,才會特地去檢視它們有沒有更新。如果有這麼一個工具,能夠在劇集更新的時候,主動告訴我,那麼我就不用時不時去查詢,對我來說就會友善很多。 在我沒有接觸到 Serverless 之前,我的想法是這樣的 —— 寫這樣的程式并不難,但是我得買個機器部署啊?如果有問題不能及時發現,還得上機器查日志,或者自己去 控制程式定時爬取的邏輯等等。 總的來說就是,實作與維護一個這樣的程式的成本遠大于了其帶給我的便利,讓我有想法卻懶于行動。 但是有了 Serverless 後,以上問題将不再是問題。例如部署難題,使用 Serverless 就是使用雲供應商提供的開發者工具,用它建立函數,打包上傳代碼即部署成功;又例如定時爬取邏輯,使用其提供的定時觸發器能力即可。這讓我能更專注于代碼實作。 在此,我不會很官方地去講 Serverless 的概念以及好處,僅僅是從一個開發者的角度來闡述我的想法。 二、實踐 1、流程圖 程式的整個流程圖如下圖所示,邏輯很簡單,這個項目的目的不在于實作一個多厲害的功能,而在于  Typescript + Node.js + SCF   的實踐方式的探索。

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

2、開發 開發能在 SCF 運作的Node.js 程式的其實與傳統的開發Node.js 程式在語言編寫上并沒有太大差別。比較明顯的不同在于,我們開發時得有一個入口的函數,比如像這樣:

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

更具體的入門文檔,可以看此處[1],跟着文檔一步步學習編寫一個簡單的函數。接下來回歸正題。 a. 環境搭建 首先為了友善開發,建議安裝騰訊雲 SCF 提供的指令行工具或者 vscode 插件。但是這裡我開發的時候 vscode 插件還沒釋出,是以這裡主要使用指令行工具,指令行工具的安裝與使用的文檔,具體可以看 此處[2]。 安裝好後,使用 scf init(具體參數得去看文檔填寫,這個提個建議,scf init提供互動式操作,采取問答的模式去建立)建立一個項目、項目檔案很簡單,一共就四個檔案,前三個,應該不多做介紹。第四個檔案 template.yaml 稱為模闆檔案,簡單來說是描述這個函數的檔案,比如函數的環境變量,觸發器類型等等,具體還是前往文檔[3]處檢視吧。

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

接下來,就是正常配置 tsconfig.json,如果沒有安裝 typescript 的同學請去官網安裝,然後 tsc --init 就可以快速生成一個 tsconfig.json,然後根據自己的需求配置即可。 然後,就是編寫 npm scripts。 主要就三個操作,build,dev,deploy。 可以使用 npm scripts 把 typescript 的編譯和 SCF CLI 的本地調試,打包和部署串聯在一起,使需要敲打的指令簡潔和語義化

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

最後,将本地倉庫與遠端倉庫關聯起來。( 這裡提一個優化:有一種場景是使用者已經建立了一個 git 倉庫,現在需要将倉庫裡的代碼寫成 SCF 模式下的代碼,并配合 SCF CLI 使用,目前 SCF CLI 隻支援 init 一個完整的項目,如果支援在一個已有項目中快速生成調試和部署的 yaml,對開發者來說是一個比較友善的功能 ) b. 編碼

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

我的主要邏輯代碼分為上面的檔案。

  • index,沒什麼好說的,就是一個入口檔案,負責組合其餘子產品的邏輯。
  • config 以及 config_extra,config_extra 檔案放了我的隐私配置,例如 redis 的 host,port 和密碼以及郵件服務的授權碼等,這些配置通過配合 .gitignore 是不會送出到遠端 git 倉庫,而 config 檔案 則是引入 config_extra 檔案中的配置,并與一些通用配置進行 merge,然後輸出到各個子產品。(注:此處也可以好好利用scf提供的環境變量功能,很适合這種場景,具體文檔[4])
  • config_extra_demo,告訴别的開發者,config_extra 檔案應該如何編寫
  • mailer,封裝郵件服務的初始化以及發送郵件方法
  • redis,封裝 redis 的連接配接以及同步 set 以及 get 方法
  • task,暫時簡單封裝了下初始化以及執行的通用邏輯。但日後該工具擴充,此處仍得考慮如何抽象以及通用化。
  • util,封裝了一些公用方法,例如封裝了 retry 方法,來包裝一些異步函數。

上面簡單介紹下主要邏輯代碼的檔案,具體的實作,有興趣可以移步到 Github 位址[5] 檢視 3、調試 上面也有提到我編寫的 npm scripts 裡有 npm run dev 的一條。本人開發這個項目時,調試都執行 npm run dev 來進行調試。這裡提一下,測試環境一般是需要和正式環境隔離的。是以可以建立一個 env.json 檔案,裡面填寫 并将 npm script 中的 dev 指令改成 npm run build && scf local generate-event timer timeup | scf native invoke --template template.yaml --env-vars env.json 然後在配置檔案中根據 process.env.NODE_ENV 變量來判斷是測試環境還是正式環境,并填寫對應環境依賴的服務的配置即可。 4、部署 上面講了這麼多,其實都不是我最想表達的,因為我并沒有在上面遇到一些很棘手的問題。而在部署的時候,我才發現在使用 typescript 時, 無法在騰訊雲 SCF 目前的部署要求以及項目的檔案目錄管理中做到完美的配合 。

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

後面和同僚讨論後,還是有不錯的方法是達到兩者的平衡。 下面是我的多次嘗試的一個過程。 如果不使用 typescript,僅使用 js 編寫 nodejs 程式,則不需要編譯的過程,部署函數時,隻需要打包然後部署即可;但是使用 typescript 後,則多了一步将 ts 代碼編譯成 js 代碼的步驟。為了管理好項目的檔案目錄,我傾向于 ts 和 js 檔案分别存放在不同的檔案夾,例如,src 檔案夾存放 ts 檔案,dist 則是編譯後得到的 js 檔案。我一開始的檔案目錄便是如此。 第一次嘗試 → 檔案目錄:

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

→ tsconfig.json 指定編譯 src 檔案夾下的 ts 檔案,輸出到 dist 檔案夾

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

→ template.yaml CodeUri 指向 dist 檔案夾

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

根據上面的配置,在本地調試是可以的。但是當部署到雲上,測試是失敗的。如果大家熟練的話可以立刻發現問題所在,打包沒有把 node_modules 打包進去。主要邏輯代碼依賴的第三方庫全都找不到,測試當然失敗了。 第二次嘗試 根據第一次嘗試,我使用 npm scripts 的 pre 鈎子,在執行部署前,編輯 ts 代碼,同時把 node_modules 拷貝到 dist 檔案夾,然後再打包部署解決了這個問題。 → package.json

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

→ copy_node_modules.js

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

→ dist 檔案夾下的檔案

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

雖然這樣做可以運作了,在本地檔案目錄管理合理,但是送出到雲上的代碼是編譯後的,基本沒啥可讀性,就是一坨能運作的東西,項目代碼也不完整。是以個人認為, 最完美的是本地開發的項目代碼和交到雲上的項目代碼是一緻的,不需要通過額外的腳本去閹割。雖然目前騰訊雲 SCF 控制台的 webIDE 還隻是能看入口檔案,不過之後會接入 Cloud Studio,起碼可以看到整個代碼檔案夾的每個檔案,說不定以後就支援線上支援 typescript 編譯(雖然不知道可不可能)。是以本人開始了第三次嘗試。 第三次嘗試 我有一個想法: template.yaml 中指定的 Handler,即入口函數,從 index.main_handler 寫成 檔案夾 /index.main_handler,即入口函數可以在某個檔案夾裡。 我在 template.yaml 處的 Handler 寫成 dist/index.main_handler,CodeUri 寫成了根目錄,這樣就可以打包整個檔案夾,然後指定 Handler 為 dist 檔案夾的 index 檔案的 main_handler 函數。 → template.yaml

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

本地調試時,是成功的! 但是在部署的時候,

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

額,好吧,我覺得是這個方案是不行的了,因為不符合 SCF 的要求,通過不了校驗。 第四次嘗試 這是我第四次嘗試。但是不是最完美的,在 檔案管理退了一步,允許 ts 和編譯後的 js 放在一起。這樣能做到把整個項目都打包上去,而且可運作,但是 ts 和 js 放在一起,檔案管理不太合理。修改的地方如下: index.ts 檔案從 src 檔案夾移動到根目錄 → tsconfig.json 編輯根目錄下的 index.ts 和 src 檔案夾下的 ts 檔案,剔除 node_modules,輸出到根目錄

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索
js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

→ template.yaml CodeUri 改成根目錄,Handler 改成 index.main_handler,即跟 CLI 生成的一樣

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

編譯後結果

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

最後部署到雲上 SCF,是可以運作的,而且是把整個項目都打包了上去,日後騰訊雲 SCF 接入了 Cloud Studio,WebIDE 看到的檔案架構和本地看到的檔案架構是一緻的。 第五次嘗試 兜兜轉轉,有時候問題解決很簡單。群組内同僚讨論後,一位大佬同僚點出 ——  可不可以在根目錄寫一個 index 檔案,然後調用編譯後的 index 檔案的入口方法?。 一語驚醒夢中人!是的,一開始就沒注意到,還可以這樣解決,思維一直在一個圈子裡繞來繞去,沒有跳出來。這樣做的成本很低,而且能達到了我之前說到的理想狀态: 本地開發的項目代碼和交到雲上的項目代碼是一緻的,不需要通過額外的腳本去閹割 實施方法即是,把 typescript 檔案放在 src 檔案夾下,編輯後的 js 檔案放在 dist 檔案夾下,在根目錄編寫一個 index.js 檔案,檔案裡的 main_hanlder 方法調用編譯後的 index 檔案的入口函數,下面是一些核心代碼。 → index.js

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

→ tsconfig.json

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

→ template.json

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

→ 編譯後結果

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

三、成果 簡單展示下代碼線上運作後的結果。

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索
js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

  總結 上面說了這麼多,這裡給一個總結就是 ——  雖然騰訊雲 SCF 沒有原生支援 TypeScript,但是經過一些方法還是可以做到兩者的完美配合。 首先本地開發是沒啥問題的,上面提到的嘗試,都是為了能夠在本地調試成功的同時可以部署到雲上。 主要是部署的問題,其中可行的三個嘗試: 第一個  是通過一些額外的方法去适配,但是做不到雲上的項目和實際的項目的一緻,如第二次嘗試。 第二個   是檔案管理上退了一步,不做到極緻的分明,如第四次嘗試。 第三個   是在根目錄寫一個 index.js 檔案,調用具有真正邏輯的入口函數,做個轉發,如第五次嘗試,也就是本人認為目前最好的實踐方式。 最後,以上的五個嘗試,是本人開發的時候的想法與實踐,也許不太正确,有誤歡迎大家來批評。如果大家有更好的方法,歡迎讨論。五次嘗試的源碼都在  Github 位址 [6] ,前四次嘗試均有對應分支,master 分支為第五次嘗試。   References [1]https://cloud.tencent.com/product/scf/getting-started [2]https://cloud.tencent.com/document/product/583/33445 [3]https://cloud.tencent.com/document/product/583/33454 [4]https://cloud.tencent.com/document/product/583/30228 [5]Github 位址: https://github.com/Juliiii/scf-crawler [6]Github 倉庫: https://github.com/Juliiii/scf-crawler 想了解更多? Hello Serverless 技術沙龍「深圳站」來了! 這場沙龍将圍繞騰訊雲 Serverless 2.0 的運作原理、應用場景,騰訊雲雲函數的架構設計、冷啟動優化、本地開發調試,以及 Serverless 在樂凱撒新餐飲服務上的應用實踐,從 0 到 1 介紹 Serverless 2.0,與開發者一同交流未來的無伺服器形态。 活動時間: 2019 年 8 月 17 日 13:00-17:30 活動地點: 深圳市南山區深南大道 10000 号騰訊大廈 2F 多功能廳

js 判斷 隻第一次運作該方法_雲函數 + TypeScript + Node.js 最佳實踐探索

點選文末