云開(kāi)發(fā) 云函數(shù)的模塊知識(shí)

2020-07-22 18:10 更新

由于云函數(shù)與Nodejs息息相關(guān),需要我們對(duì)云函數(shù)與Node的模塊以及Nodejs的一些基本知識(shí)有一些基本的了解。下面只介紹一些基礎(chǔ)的概念,如果你想詳細(xì)深入了解,建議去翻閱一下Nodejs的官方技術(shù)文檔:

技術(shù)文檔:Nodejs API 中文技術(shù)文檔

一、Nodejs的內(nèi)置模塊

在前面我們已經(jīng)接觸過(guò)Nodejs的fs模塊、path模塊,這些我們稱(chēng)之為Nodejs的內(nèi)置模塊,內(nèi)置模塊不需要我們使用npm install下載,就可以直接使用require引入:

const fs = require('fs')
const path = require('path')
const url = require('url')

Nodejs的常用內(nèi)置模塊以及功能如下所示,這些模塊都是可以在云函數(shù)里直接使用的:

  • fs 模塊: 文件目錄的創(chuàng)建、刪除、查詢(xún)以及文件的讀取和寫(xiě)入;

  • os模塊: 提供了一些基本的系統(tǒng)操作函數(shù);

  • path 模塊: 提供了一些用于處理文件路徑的API;

  • url模塊: 用于處理與解析 URL;

  • http模塊: 用于創(chuàng)建一個(gè)能夠處理和響應(yīng) http 響應(yīng)的服務(wù);

  • querystring模塊: 解析查詢(xún)字符串;

  • util模塊: util 模塊主要用于支持 Node.js 內(nèi)部 API 的需求,大部分實(shí)用工具也可用于應(yīng)用程序與模塊開(kāi)發(fā)者;

  • net模塊: 用于創(chuàng)建基于流的 TCP 或 IPC 的服務(wù)器;

  • dns模塊: 用于域名的解析;

  • crypto模塊: 提供加密功能,包括對(duì) OpenSSL 的哈希、HMAC、加密、解密、簽名、以及驗(yàn)證功能的一整套封裝;

  • zlib模塊: zlib 可以用來(lái)實(shí)現(xiàn)對(duì) HTTP 中定義的 gzip 和 deflate 內(nèi)容編碼機(jī)制的支持。

  • process模塊: 提供有關(guān)當(dāng)前 Node.js 進(jìn)程的信息并對(duì)其進(jìn)行控制.作為一個(gè)全局變量,它始終可供 Node.js 應(yīng)用程序使用,無(wú)需使用 require(), 它也可以使用 require() 顯式地訪(fǎng)問(wèn).

二、Node的global全局對(duì)象

和JavaScript的全局對(duì)象(Global Object)類(lèi)似,Nodejs也有一個(gè)全局對(duì)象global,它以及它的所有屬性(一些全局變量都是global對(duì)象的屬性)都可以在程序的任何地方訪(fǎng)問(wèn)。下面就來(lái)介紹一下Nodejs在云函數(shù)里比較常用的全局變量。

1、dirname 和filename

dirname是獲得當(dāng)前執(zhí)行文件所在目錄的完整目錄名,node還有另外一個(gè)常用變量filename,它是獲得當(dāng)前執(zhí)行文件的帶有完整絕對(duì)路徑的文件名。我們可以新建一個(gè)云函數(shù)比如nodefile,然后在nodefile云函數(shù)的index.js里輸入以下代碼:

const cloud = require('wx-server-sdk')
cloud.init({
    env: cloud.DYNAMIC_CURRENT_ENV
  })
exports.main = async (event, context) => {
    console.log('當(dāng)前執(zhí)行文件的文件名', __filename );
    console.log('當(dāng)前執(zhí)行文件的目錄名', __dirname );
}

將云函數(shù)部署上傳之后,通過(guò)小程序端調(diào)用、本地調(diào)試或云端測(cè)試就可以執(zhí)行云函數(shù),得到如下的打印結(jié)果(還記得云函數(shù)的打印日志可以在哪里查看么?):

當(dāng)前執(zhí)行文件的文件名 /var/user/index.js
當(dāng)前執(zhí)行文件的目錄名 /var/user

由此可見(jiàn)云函數(shù)在云端Linux環(huán)境就放置在/var/user文件夾里面。

2、module、exports、require

還有一些變量比如module,module.exports,exports等實(shí)際上是模塊內(nèi)部的局部變量,它們指向的對(duì)象根據(jù)模塊的不同而有所不同,但是由于它們通用于所有模塊,也可以當(dāng)成全局變量。

  • module對(duì)當(dāng)前模塊的引用,module.exports 用于指定一個(gè)模塊所導(dǎo)出的內(nèi)容,即可以通過(guò) require() 訪(fǎng)問(wèn)的內(nèi)容。

  • require用于引入模塊、JSON、或本地文件,可以從node_modules引入模塊,可以使用相對(duì)路徑引入本地模塊,路徑會(huì)根據(jù) __dirname定義的目錄名或當(dāng)前工作目錄進(jìn)行處理。

  • exports表示該模塊運(yùn)行時(shí)生成的導(dǎo)出對(duì)象。如果按確切的文件名沒(méi)有找到模塊,則 Nodejs會(huì)嘗試帶上.js、.json或.node拓展名再加載。

/為前綴的模塊是文件的絕對(duì)路徑,放到云函數(shù)里require('/var/user/config/config.js') 會(huì)加載云函數(shù)目錄里的config文件夾里的config.js,這里require('/var/user/config/config.js')在云函數(shù)的路徑里等同于相對(duì)路徑的require('./config/config.js')。當(dāng)沒(méi)有以 '/'、'./' 或 '../' 開(kāi)頭來(lái)表示文件時(shí),這個(gè)模塊必須是一個(gè)核心模塊或加載自node_modules 目錄。

在nodefile云函數(shù)的目錄下面新建一個(gè)config文件夾,在config文件夾里創(chuàng)建一個(gè)config.js,云函數(shù)的目錄結(jié)構(gòu)如下圖所示:

nodefile // 云函數(shù)目錄
├── config //config文件夾
│   └── config.js //config.js文件
└── index.js
└── config.json 
└── package.json 

然后再在config.js里輸入以下代碼,通常我們用這樣的方式申明一些比較敏感的信息,或者比較通用的模塊:

module.exports = {
    AppID: 'wxda99ae45313257046',  //可以是其他變量,這里只是參考
    AppKey: 'josgjwoijgowjgjsogjo', 
  }

然后在nodefile云函數(shù)的index.js里輸入以下代碼(下面并非實(shí)際代碼,大家看著添加):

//下面兩句放在exports.main函數(shù)的前面
const config = require('./config/config.js')
const {AppID,AppKey} = config
//省略了部分代碼
exports.main = async (event, context) => {
    console.log({AppID,AppKey})
}

將云函數(shù)的所有文件都部署上傳到云端之后,再來(lái)執(zhí)行云函數(shù),我們就可以看到config/config.js里面的變量就被傳遞到了index.js里了,這同時(shí)也說(shuō)明在云函數(shù)目錄之下不僅可以創(chuàng)建文件(前面創(chuàng)建過(guò)圖片),還可以創(chuàng)建模塊,通過(guò)module.exports和require來(lái)達(dá)到創(chuàng)建并引入的效果。

3、process.env屬性

process 對(duì)象提供有關(guān)當(dāng)前 Node.js 進(jìn)程的信息并對(duì)其進(jìn)行控制,它有一個(gè)比較重要的屬性process.env,返回包含用戶(hù)環(huán)境的對(duì)象。

比如上面的nodefile云函數(shù),打開(kāi)云開(kāi)發(fā)控制臺(tái),在云函數(shù)列表里找到nodefile,然后點(diǎn)擊配置在彈窗的環(huán)境變量里添加一些環(huán)境變量,比如NODE_ENV、ENV_ID、name(因?yàn)槭浅A浚ㄗh用大寫(xiě)字母),它的值為字符串,然后我們將nodefile云函數(shù)的index.js代碼改為如下:

const cloud = require('wx-server-sdk')
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})
exports.main = async (event, context) => {
  return process.env //process可以不必使用require就可以直接用
}

右鍵云函數(shù)增量上傳之后,調(diào)用該云函數(shù),然后在云函數(shù)的返回的對(duì)象里就可以看到除了有我們?cè)O(shè)置的變量以外,還有一些關(guān)于云函數(shù)環(huán)境的信息。因此我們可以把一些需要手動(dòng)可以修改或者比較私密的變量添加到配置里,然后在云函數(shù)里調(diào)用,比如我們想在小程序上線(xiàn)之后修改小程序的云開(kāi)發(fā)環(huán)境,可以添加ENV_ID字段,值到時(shí)根據(jù)情況來(lái)修改:

const cloud = require('wx-server-sdk')
const {ENV_ID} = process.env
cloud.init({
    env: ENV_ID
})

三、wx-server-sdk的模塊

再來(lái)回顧一下wx-server-sdk這個(gè)第三方模塊,它也是云開(kāi)發(fā)必備的核心依賴(lài),云開(kāi)發(fā)的諸多API都是基于此。我們可以在給云函數(shù)安裝了wx-server-sdk之后(也就是右鍵云函數(shù),在終端執(zhí)行了 npm install),在電腦上打開(kāi)云函數(shù)的node modules文件夾,可以看到雖然只安裝了一個(gè)wx-server-sdk,但是卻下載了很多個(gè)模塊,這些模塊都是通過(guò)三個(gè)核心依賴(lài)@cloudbase/node-sdk(原tcb-admin-node)、protobuf、jstslib來(lái)安裝的。

要想對(duì)wx-server-sdk有一個(gè)深入了解,我們可以研究一下最核心的@cloudbase/node-sdk(原tcb-admin-node),具體可以參考@cloudbase/node-sdk的Github官網(wǎng),同時(shí)由于wx-server-sdk順帶下載了很多依賴(lài),比如@cloudbase/node-sdk、xml2js、request等,這些依賴(lài)可以在云函數(shù)里直接引入。

const request = require('request')

request模塊雖然是第三方模塊,但是已經(jīng)通過(guò)wx-server-sdk下載了,在云函數(shù)里直接通過(guò)require就可以引入。由于wx-server-sdk模塊是每個(gè)云函數(shù)都會(huì)下載安裝的,我們完全可以把它當(dāng)成云函數(shù)的內(nèi)置模塊來(lái)處理,而通過(guò)wx-server-sdk順帶下載的N多個(gè)依賴(lài),我們也可以直接引入,不必再來(lái)下載,而在使用npm install安裝完成之后的package-lock.json里查看這些依賴(lài)的版本信息。

四、第三方模塊

Nodejs生態(tài)所擁有的第三方模塊是所有編程語(yǔ)言里最多了,比Python、PHP、Java還要多,借助于這些開(kāi)源的模塊,可以大大節(jié)省我們的開(kāi)發(fā)成本,這些模塊在npm官網(wǎng)地址都可以搜索到,不過(guò)npm官網(wǎng)的第三方模塊大而全,哪些才是Nodejs開(kāi)發(fā)人員最常用最優(yōu)秀的模塊呢?我們可以在Github上面找到awesome Nodejs,這里有非常全面的推薦。

在awesome-nodejs里,這些優(yōu)秀的模塊被分為了近50個(gè)不同的類(lèi)別,而其中大多數(shù)都是可以用于云函數(shù)的,可見(jiàn)云函數(shù)的強(qiáng)大遠(yuǎn)不只停留在云開(kāi)發(fā)的技術(shù)文檔上,我們接下來(lái)會(huì)在這一章會(huì)選取一些比較有代表性的模塊來(lái)結(jié)合云函數(shù)進(jìn)行講解。

當(dāng)我們要在云函數(shù)里引入第三方模塊時(shí),需要先在該云函數(shù)package.json里的dependencies里添加該模塊并附上版本號(hào)"第三方模塊名": "版本號(hào)",版本號(hào)的表示方法有很多,npm install 會(huì)下載相應(yīng)的版本(只列舉一些比較常見(jiàn)的):

  • latest,會(huì)下載最新版的模塊;

  • 1.2.x,等同于1.2,會(huì)下載>=1.2.0<3.0.0的版本;

  • ~1.2.4,會(huì)下載>=1.2.4 <1.3.0的版本;

  • ^1.2.4,會(huì)下載>=1.2.3 <2.0.0的版本

比如我們要在云函數(shù)里引入lodash的最新版,就可以去該云函數(shù)package.json里添加"lodash": "latest",注意是添加到dependencies屬性里面,而且package.json的寫(xiě)法也要符合配置文件的格式要求,尤其要注意最后一項(xiàng)不能有逗號(hào),,以及不能在json配置文件里寫(xiě)注釋?zhuān)?/p>

  "dependencies": {
      "lodash": "latest"
    }

npm install時(shí)候生成一份package-lock.json文件,用來(lái)記錄當(dāng)前狀態(tài)下實(shí)際安裝的各個(gè)npm package的具體來(lái)源和版本號(hào)。不同的版本號(hào)可能對(duì)運(yùn)行的結(jié)果造成不一樣的影響,所以為了保證版一致會(huì)有package-lock.json,通常我們用最新的即可。

五、云函數(shù)的運(yùn)行機(jī)制

云函數(shù)運(yùn)行在服務(wù)端Linux的環(huán)境中,一個(gè)云函數(shù)在處理并發(fā)請(qǐng)求的時(shí)候會(huì)創(chuàng)建多個(gè)云函數(shù)實(shí)例,每個(gè)云函數(shù)實(shí)例之間相互隔離,沒(méi)有公用的內(nèi)存或硬盤(pán)空間,因此每個(gè)云函數(shù)的依賴(lài)也是相互隔離的,所以每個(gè)云函數(shù)我們都要下載各自的依賴(lài),無(wú)法做到復(fù)用。

云函數(shù)實(shí)例的創(chuàng)建、管理、銷(xiāo)毀等操作由平臺(tái)自動(dòng)完成。每個(gè)云函數(shù)實(shí)例都在 /tmp 目錄下(這里是服務(wù)端的絕對(duì)路徑/tmp,不是云函數(shù)目錄下的./tmp)提供了一塊 512MB 的臨時(shí)磁盤(pán)空間用于處理單次云函數(shù)執(zhí)行過(guò)程中的臨時(shí)文件讀寫(xiě)需求,需特別注意的是,這塊臨時(shí)磁盤(pán)空間在函數(shù)執(zhí)行完畢后可能被銷(xiāo)毀,不應(yīng)依賴(lài)和假設(shè)在磁盤(pán)空間存儲(chǔ)的臨時(shí)文件會(huì)一直存在。如果要持久化的存儲(chǔ),最好是使用云存儲(chǔ)。

云函數(shù)應(yīng)是無(wú)狀態(tài)的,也就是一次云函數(shù)的執(zhí)行不依賴(lài)上一次云函數(shù)執(zhí)行過(guò)程中在運(yùn)行環(huán)境中殘留的信息。為了保證負(fù)載均衡,云函數(shù)平臺(tái)會(huì)根據(jù)當(dāng)前負(fù)載情況控制云函數(shù)實(shí)例的數(shù)量,并且會(huì)在一些情況下重用云函數(shù)實(shí)例,這使得連續(xù)兩次云函數(shù)調(diào)用如果都由同一個(gè)云函數(shù)實(shí)例運(yùn)行,那么兩者會(huì)共享同一個(gè)臨時(shí)磁盤(pán)空間,但因?yàn)樵坪瘮?shù)實(shí)例隨時(shí)可能被銷(xiāo)毀,并且連續(xù)的請(qǐng)求不一定會(huì)落在同一個(gè)實(shí)例(因?yàn)橥瑫r(shí)會(huì)創(chuàng)建多個(gè)實(shí)例),因此云函數(shù)不應(yīng)依賴(lài)之前云函數(shù)調(diào)用中在臨時(shí)磁盤(pán)空間遺留的數(shù)據(jù)。總的原則即是云函數(shù)代碼應(yīng)是無(wú)狀態(tài)的。

  • 由于云函數(shù)是按需執(zhí)行, 云函數(shù)在return返回之后就會(huì)停止運(yùn)行, 和普通 node 本地運(yùn)行的行為有些差異,這個(gè)要注意一下;

  • 如果云函數(shù)需要處理一些文件的下載,可以把文件存儲(chǔ)在服務(wù)器的臨時(shí)目錄/tmp里,云函數(shù)的目錄是沒(méi)有寫(xiě)權(quán)限的;

  • 云函數(shù)存在冷啟動(dòng)和熱啟動(dòng)的問(wèn)題,所謂冷啟動(dòng)就是云函數(shù)完整執(zhí)行整個(gè)實(shí)例化實(shí)例、加載函數(shù)代碼和node,執(zhí)行函數(shù)的整個(gè)過(guò)程,而熱啟動(dòng)則是函數(shù)實(shí)例和執(zhí)行被復(fù)用,main 函數(shù)外的代碼可能不會(huì)被執(zhí)行,因此有些變量的聲明不要寫(xiě)在main 函數(shù)外面,當(dāng)云函數(shù)被高并發(fā)調(diào)用時(shí),main 函數(shù)外的變量可能會(huì)成為跨實(shí)例的“全局變量”;

  • 不要在云函數(shù)異步流程中執(zhí)行關(guān)鍵任務(wù),也就是一些關(guān)鍵任務(wù)的函數(shù)前面要加一個(gè)await,以免任務(wù)沒(méi)有執(zhí)行完,云函數(shù)就終止了;

  • 由于云函數(shù)是無(wú)狀態(tài)的,因此執(zhí)行環(huán)境通常會(huì)從頭開(kāi)始初始化(冷啟動(dòng)),當(dāng)發(fā)生冷啟動(dòng)時(shí),系統(tǒng)會(huì)對(duì)函數(shù)的全局環(huán)境進(jìn)行評(píng)估。如果云函數(shù)導(dǎo)入了模塊,那么在冷啟動(dòng)期間加載這些模塊會(huì)增加延遲時(shí)間,因此正確加載依賴(lài)項(xiàng)而不加載函數(shù)不使用的依賴(lài)項(xiàng),可以縮短此延遲時(shí)間以及部署函數(shù)所需的時(shí)間。
以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)