優(yōu)化托管于阿里云函數(shù)計(jì)算的Node.js應(yīng)用 - 以Parse為例
上文介紹了怎么快速搬遷Parse到阿里云函數(shù)核算,可是這只是一個(gè)跑起來(lái)的比如,還有一些問(wèn)題需求咱們優(yōu)化。本文會(huì)介紹常見(jiàn)的優(yōu)化點(diǎn)和辦法,從辦法來(lái)看適用于一切Serverless渠道的應(yīng)用。
Serverless的缺陷
沒(méi)有任何技能形狀是完美的,Serverless供給了杰出的可伸縮性和并發(fā)性,供給了細(xì)粒度的資源分配,優(yōu)化了成本,相對(duì)的也有難以調(diào)試等缺點(diǎn)。
這些問(wèn)題是Serverless這種技能形狀本身形成的,并不是阿里云函數(shù)核算獨(dú)有的。不同的云廠商能夠經(jīng)過(guò)周邊建設(shè)來(lái)補(bǔ)償一些問(wèn)題,比如阿里云函數(shù)核算的日志和監(jiān)控相對(duì)比較完善,Serverless Devs工具處理了一部分調(diào)試問(wèn)題。
用更傳統(tǒng)的觀點(diǎn)來(lái)了解Serverless的本質(zhì),能夠看作擴(kuò)容縮容戰(zhàn)略極端激進(jìn)的集群,而每個(gè)函數(shù)都是布置在這一個(gè)一個(gè)機(jī)器上罷了。云廠商的機(jī)器特別迷你,計(jì)價(jià)單位顆粒小。而縮容戰(zhàn)略能夠?qū)?,擴(kuò)容戰(zhàn)略能夠近乎無(wú)限大,縮容戰(zhàn)略是固定,不能夠自定義。
那么對(duì)于一個(gè)隨時(shí)可能創(chuàng)建隨時(shí)可能被毀掉的機(jī)器,布置于其間的服務(wù)要面臨兩個(gè)方面的問(wèn)題
- 服務(wù)毀掉
- 服務(wù)發(fā)動(dòng)
服務(wù)毀掉時(shí)內(nèi)存、文件體系的數(shù)據(jù)都丟失了。服務(wù)發(fā)動(dòng)的時(shí)分需求一些必要的初始化,需求發(fā)動(dòng)程序。
咱們先看下毀掉引起的耐久化問(wèn)題。
耐久化改善
Parse是支撐文件上傳的,存儲(chǔ)文件的FileAdapter是能夠自定義的。
一般來(lái)說(shuō)對(duì)于文件需求,能夠直接運(yùn)用阿里云目標(biāo)存儲(chǔ)OSS,一般挑選標(biāo)準(zhǔn)型就能夠了。
Parse官方不支撐阿里云OSS,理論上能夠運(yùn)用parse-server-s3-adapter,可是我之前沒(méi)有配置過(guò),能夠完全能夠自定義,直接運(yùn)用OSS官方的SDK就行了。
'use strict'; var OSS = require('ali-oss').Wrapper; const DEFAULT_OSS_REGION = "oss-cn-hangzhou"; function requiredOrFromEnvironment(options, key, env) { options[key] = options[key] || process.env[env]; if (!options[key]) { throw `OSSAdapter requires option '${key}' or env. variable ${env}`; } return options; } function fromEnvironmentOrDefault(options, key, env, defaultValue) { options[key] = options[key] || process.env[env] || defaultValue; return options; } function optionsFromArguments(args) { let options = {}; let accessKeyOrOptions = args[0]; if (typeof accessKeyOrOptions == 'string') { options.accessKey = accessKeyOrOptions; options.secretKey = args[1]; options.bucket = args[2]; let otherOptions = args[3]; if (otherOptions) { options.bucketPrefix = otherOptions.bucketPrefix; options.region = otherOptions.region; options.directAccess = otherOptions.directAccess; options.baseUrl = otherOptions.baseUrl; options.baseUrlDirect = otherOptions.baseUrlDirect; } } else { options = accessKeyOrOptions || {}; } options = requiredOrFromEnvironment(options, 'accessKey', 'OSS_ACCESS_KEY'); options = requiredOrFromEnvironment(options, 'secretKey', 'OSS_SECRET_KEY'); options = requiredOrFromEnvironment(options, 'bucket', 'OSS_BUCKET'); options = fromEnvironmentOrDefault(options, 'bucketPrefix', 'OSS_BUCKET_PREFIX', ''); options = fromEnvironmentOrDefault(options, 'region', 'OSS_REGION', DEFAULT_OSS_REGION); options = fromEnvironmentOrDefault(options, 'directAccess', 'OSS_DIRECT_ACCESS', false); options = fromEnvironmentOrDefault(options, 'baseUrl', 'OSS_BASE_URL', null); options = fromEnvironmentOrDefault(options, 'baseUrlDirect', 'OSS_BASE_URL_DIRECT', false); return options; } function OSSAdapter() { var options = optionsFromArguments(arguments); this._region = options.region; this._bucket = options.bucket; this._bucketPrefix = options.bucketPrefix; this._directAccess = options.directAccess; this._baseUrl = options.baseUrl; this._baseUrlDirect = options.baseUrlDirect; let ossOptions = { accessKeyId: options.accessKey, accessKeySecret: options.secretKey, bucket: this._bucket, region: this._region }; this._ossClient = new OSS(ossOptions); this._ossClient.listBuckets().then((val) => { var bucket = val.buckets.filter((bucket) => { return bucket.name === this._bucket }).pop(); this._hasBucket = !!bucket; }); } OSSAdapter.prototype.createBucket = function () { if (this._hasBucket) { return Promise.resolve(); } else { return this._ossClient.putBucket(this._bucket, this._region).then(() => { this._hasBucket = true; if (this._directAccess) { return this._ossClient.putBucketACL(this._bucket, this._region, 'public-read'); } return Promise.resolve(); }).then(() => { return this._ossClient.useBucket(this._bucket, this._region); }); } }; OSSAdapter.prototype.createFile = function (filename, data, contentType) { let options = {}; if (contentType) { options.headers = {'Content-Type': contentType} } return this.createBucket().then(() => { return this._ossClient.put(this._bucketPrefix + filename, new Buffer(data), options); }); }; OSSAdapter.prototype.deleteFile = function (filename) { return this.createBucket().then(() => { return this._ossClient.delete(this._bucketPrefix + filename); }); }; OSSAdapter.prototype.getFileData = function (filename) { return this.createBucket().then(() => { return this._ossClient.get(this._bucketPrefix + filename).then((val) => { return Promise.resolve(val.content); }).catch((err) => { return Promise.reject(err); }); }); }; OSSAdapter.prototype.getFileLocation = function (config, filename) { var url = this._ossClient.signatureUrl(this._bucketPrefix + filename); url = url.replace(/^http:/, "https:"); return url; }; module.exports = OSSAdapter; module.exports.default = OSSAdapter;
這個(gè)是我正在用的adapter,能夠參閱運(yùn)用。特別是getFileLocation,要根據(jù)自己情況運(yùn)用。
Parse還有一個(gè)緩存,一般默許運(yùn)用本地環(huán)境,可是考慮到Serverless的特性,這一部分還是要耐久化用于加快。官方供給的RedisCacheAdapter能夠直接運(yùn)用。Redis集群要求不是很高,最好復(fù)用已有的,單獨(dú)運(yùn)用成本有點(diǎn)高。
發(fā)動(dòng)改善
Serverless函數(shù)的生命周期問(wèn)題一直是搬遷的阻止,比較明顯的是異步懇求丟失、高雅下線困難。阿里云函數(shù)核算對(duì)于模型有必定擴(kuò)展,額定供給了一些Hook。
初始化只會(huì)進(jìn)行一次,preFreeze和preStop便是退出前的Hook,這三處也是同樣的計(jì)費(fèi)。
因?yàn)镻arse也涉及到數(shù)據(jù)庫(kù)銜接,所以能夠?qū)?shù)據(jù)庫(kù)銜接部分移動(dòng)到initialize中。
除了生命周期上一般來(lái)說(shuō)還有一些挑選
提升內(nèi)存分配:函數(shù)核算能夠自行配置內(nèi)存,對(duì)于部分應(yīng)用(特別是有初始化掃描等)加大內(nèi)存能夠改善發(fā)動(dòng)速度
調(diào)整結(jié)構(gòu)或者渠道:對(duì)于NodeJs而言,新版別普遍都有性能上的優(yōu)化,選用盡可能新的NodeJs版別也能夠加快發(fā)動(dòng)。假如實(shí)在對(duì)時(shí)刻很靈敏,可能要考慮Rust等發(fā)動(dòng)速度更友好的語(yǔ)言。
在發(fā)動(dòng)函數(shù)中初始化更多的共享資源:這個(gè)其實(shí)不能處理第一次冷發(fā)動(dòng)的時(shí)刻,可是能夠讓每次call的耗時(shí)更少。
減縮包大?。簩?duì)于不必要的三方庫(kù)優(yōu)先移除,也能夠運(yùn)用更精簡(jiǎn)的版別進(jìn)行替換。
定時(shí)激活:這個(gè)最早在AWS Lambda上廣泛運(yùn)用,其實(shí)本質(zhì)上是保存一個(gè)常駐實(shí)例,可是依靠的云廠商的機(jī)制。比如AWS Lambda大約30-40分鐘收回之前的活躍實(shí)例。這樣只需求一個(gè)定時(shí)觸發(fā)器就能夠進(jìn)行激活操作。這個(gè)辦法在一切Serverless渠道都能夠運(yùn)用。可是需求正確處理來(lái)自HTTP觸發(fā)器和Event觸發(fā)器的邏輯。