腾讯云SCF无服务器云函数有哪些应用?

  • 回答 (5)
  • 关注 (2)
  • 查看 (740)

SCF无服务器云函数应该会成为未来发张趋势,那么就现在而言有哪些应用呢?

HXD3C0134HXD3C0134提问于
萌萌呆想玩游戏却不知道这么开机的吃瓜少年。回答于
推荐

可以用 SCF 无服务器云函数定时备份数据库

mysqldump 准备

常用来导出数据库备份数据的的 mysqldump 工具,在云函数中也同样能使用;但是由于云函数环境并未内置 mysqldump,因此我们要自行打包工具。

通过 mysql 社区版下载地址,选择操作系统为 Linux - geneic,选择操作系统版本为 (x86,64-bit),下载 tar.gz 压缩包并存储在本地。下载完成后,解压压缩包,可以看到解压后的文件夹内包含有 bin、lib 等目录;而我们要找到的 mysqldump 工具就在 bin 目录下。同时可以看到 bin 目录下还有 libcrytpo.so.1.0.0 和 libssl.so.1.0.0 两个动态库。这两个库也是工具在运行时所要依赖的库,但在 bin 下的这两个文件实际为文件链接,实际指向分别是 ../lib/libcrypto.so.1.0.0 和 ../lib/libssl.so.1.0.0,因此这两个真实文件是在 lib 目录下。因此,为了确保

mysqldump 工具可以运行成功,我们将 bin 目录下的 mysqldump 文件拷贝到我们提前准备的项目根目录下,同时将 lib 目录下的 libcrypto.so,libcrypto.so.1.0.0,libssl.so,libssl.so.1.0.0 四个文件也拷贝到项目根目录下。

由于拷贝出来的 mysqldump 和 so 动态库文件是 Linux 版本,如果需要验证可用性,我们可以将准备好的项目目录拷贝到一台 Linux 服务器上,通过运行 mysqldump 命令验证工具的可用性。

./mysqldump -h {host} -P {port} -u{user} -p{password} {dbName} > dump.sql

使用如上命令,就可以将数据库内某一个具体的库导出到对应的 sql 文件内,我们可以通过命令运行时的输出,和导出文件的内容,判断是否运行成功。

云函数准备

接下来,我们来准备好需要定时运行的云函数。此处云函数的主要功能,就是在每一次运行时,调用上一步骤中我们准备好的 mysqldump 工具,来连接远程数据库并在本地生成 dump 文件。由于云函数的本地环境中不提供持久存储,生成的 dump 文件,需要上传到对象存储中做持久化,并在所需要的时候可以下载使用。

我们在这里使用 python 2.7 作为开发语言,在项目根目录下创建 index.py 文件,并在文件内输入如下内容并保存。

#coding=utf-8

import os
from qcloud_cos import CosConfig
from qcloud_cos import CosS3Client
import sys
import logging
import time

logging.basicConfig(level=logging.INFO, stream=sys.stdout)


DB_HOST = os.getenv('dbhost') #'sh-cdb-irye027y.sql.tencentcdb.com'
DB_PORT = os.getenv('dbport') #'63374'
DB_USER = os.getenv('dbuser') #'root'
DB_USER_PASSWORD = os.getenv('dbpwd') #'abc123!@#'
DB_NAME = os.getenv('dbname') #'cAuth'

BACKUP_PATH = '/tmp'

SECRET_ID = os.getenv('secretid') #'AKIDQm6iUh2NJ6jL41tVUis9KpY5Rgv49zyC'
SECRET_KEY = os.getenv('secretkey') #'xxxlbNyQo4EMJ5jPGA7sUvHWaSAlxxxxx'
REGION = os.getenv('cosregion') #'ap-shanghai'
BACKUP_BUCKET = os.getenv('cosbucket') #"dbbackup-1253970226"

config = CosConfig(Secret_id=SECRET_ID, Secret_key=SECRET_KEY, Region=REGION, Token="")
client = CosS3Client(config)


def backup2cos(file, bucket, key):
    response = client.put_object_from_local_file(
        Bucket=bucket,
        LocalFilePath=file,
        Key=key,
    )
    print(response)
    return response

def main_handler(event, context):
    timestr = time.strftime('%Y%m%d-%H%M%S', time.localtime(time.time()))
    db = DB_NAME
    filename = db+"-"+timestr+".sql"
    filepath = BACKUP_PATH+os.sep+filename
    print "Start Backup"
    dumpcmd = "./mysqldump -h " + DB_HOST + " -P " + DB_PORT + " -u" + DB_USER + " -p'" + DB_USER_PASSWORD + "' " + db + " > " + filepath
    print dumpcmd
    print os.popen(dumpcmd).read()
    print "Backup script completed"
    print "Your backups has been created in '" + filepath + "' file"
    print os.popen('ls -l /tmp').read()
    print "finish backup"
    print "start send to cos"
    backup2cos(filepath,BACKUP_BUCKET,"/"+filename)
    print "finish send to cos"

if __name__ == "__main__":
    main_handler("","")

在代码中,我们使用到了 COS 的 v5 版本 sdk 进行备份后的文件上传操作,因此我们需要安装好依赖便于脚本调用。我们可以在项目根目录通过执行 pip install cos-python-sdk-v5 -t . 命令,来将 cos 的 python sdk 以及相关依赖安装到项目目录中。

云函数打包及创建

在我们完成以上几部操作以后,我们在项目目录里有了 mysqldump 工具及依赖的 so 库,有了 cos sdk 及依赖包,以及我们的入口函数文件 index.py。接下来我们就需要将项目文件打包为 zip 格式以便上传至云函数,完成函数创建。

部署包打包

由于项目目录下的 mysqldump 工具作为二进制程序,需要在云函数的环境中运行,因此需要具有可执行权限,需要在 Linux 或 Mac 环境下为此文件赋予可执行权限后再打包,因此建议在 Linux 或 Mac 环境下执行打包。

我们可以将项目目录放置到 Linux 或 Mac 环境下后,通过在项目目录下执行 chmod +x mysqldump 命令,为 mysqldump 工具附加上可执行权限。

完成附加权限后,可以继续在根目录下通过执行 zip mysqldump.zip * 命令,将所有文件打包到 mysqldump.zip 内,生成可以用于创建函数的 zip 包。

创建及配置函数

通过如上步骤创建的 zip 包,由于体积稍大,需要通过对象存储 COS 的方式上传。因此我们先准备好 COS 的存储桶。我们需要在特定地域创建两个存储桶,一个用于上传及更新函数代码使用,一个用于存储备份的 mysql dump 文件。两个存储桶分别命名为 codefile 和 dbbackup。

然后我们将上一步创建的 zip 包,上传到 codefile 根目录中,作为函数创建时的代码来源。

接下来。我们开始创建函数,在特定地域下创建名为 mysqldump 的函数,运行环境选择为 python2.7,超时时间可配置为 60 秒。接下来上传代码的位置,选择为从 COS 上传代码,选择 codefile 存储桶,并指定代码文件为 /mysqldump.zip,同时入口函数为 index.main_handler,与 index.py 文件中的 main_handler 函数对应。

同时,由于我们代码中的数据库相关配置,COS 读写相关配置,均从环境变量中读取的,因此我们也需要对函数配置上所需的环境变量,配置包括 dbhost,dbport,dbuser,dbpwd,dbname,secretid,secretkey,cosregion,cosbucket 的环境变量对应的值,使用具体的数据连接信息以及对应的 COS 读写认证相关的信息。

配置完成后,我们就完成了函数的创建。

测试及启动函数

完成函数创建后,我们就可以来测试下函数的运行情况,并完成最终的定时触发配置。

通过控制台右上角的测试按键,我们可以直接触发函数的运行。通过函数的输出日志,我们可以查看代码的运行情况,检查 dump 文件是否生成正常,是否成功上传到 COS 存储桶中。同时我们也可以到对应的备份存储桶中,查看生成的文件,检查是否数据正确,备份正常。

确认函数测试运行正常后,我们就可以在触发器中,为函数新增一个定时触发器了。我们可以根据自身需要,配置为每天,或每12小时,或每月的指定时间运行。定时触发器可以按 crontab 格式编写触发时间,既可以按一定时间周期,也可以指定具体时间运行。

爱胖次的瓜皮回答于

使用 SCF 无服务器云函数定时拨测站点并邮件告警

利用无服务器架构中提供的定时触发能力,在运维监控场景有很多种用处,例如定时备份、定时拨测、定时统计等。在互联网业务监控运维的场景下,我们通常可以利用定时拨测,检测系统或服务的健康状态,并在系统异常的情况下及时发出告警,避免造成业务中断。

接下来,我们就利用无服务器云函数实现一个简单的拨测脚本,可以定时拨测指定的业务服务,并在异常时发出邮件告警。我们同样通过 Python 来实现函数代码,利用 requests 库发出 http 请求来探查系统的工作情况,并在探查出问题的时候利用 python 自带的 smtplib 邮件发送库发出告警邮件。

# -*- coding: utf8 -*-
import json
import logging
import requests
from email.mime.text import MIMEText
from email.header import Header
import smtplib
import os


logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
#logger.addHandler(logging.StreamHandler())

test_url_list = [
    "http://www.baidu.com",
    "http://www.qq.com",
    "http://cloud.tencent.com",
    "http://unkownurl.com"
]

email_server_config = {
    "server":"smtp.qq.com",
    "port":465,
    "user":"3473058547@qq.com",
    "pwd":os.getenv("EMAIL_PWD"),
    "fromAddr":"3473058547@qq.com"
}

email_notify_list = [
    "3473058547@qq.com"
]

def send_mail(toAddrList,subject,content):
    logger.info("send mail")
    try:
        receivers = toAddrList
        message = MIMEText(content, 'plain', 'utf-8')
        message['From'] = Header("自动拨测", 'utf-8')
        message['To'] = Header("异常通知接收", 'utf-8')
        message['Subject'] = Header(subject, 'utf-8')

        smtpObj = smtplib.SMTP_SSL(email_server_config["server"], email_server_config["port"])
        #smtpObj = smtplib.SMTP(email_server_config["server"], email_server_config["port"])
        smtpObj.login(email_server_config["user"],email_server_config["pwd"])
        smtpObj.sendmail(email_server_config["fromAddr"], receivers, message.as_string())
        logger.info("send success")
    except Exception as e:
        logger.warn(str(e))
        logger.warn(type(e))
        logger.warn("Error: send fail")

def test_url(url_list):
    errorinfo = []
    for url in url_list:
        resp = None
        try:
            resp = requests.get(url,timeout=3)
        except (requests.exceptions.Timeout, requests.exceptions.ConnectionError, requests.exceptions.ConnectTimeout) as e:
            logger.warn("request exceptions:"+str(e))
            errorinfo.append("Access "+ url + " timeout")
        else:
            if resp.status_code >= 400:
                logger.warn("response status code fail:"+str(resp.status_code))
                errorinfo.append("Access "+ url + " fail, status code:" + str(resp.status_code))
    if len(errorinfo) != 0:
        send_mail(email_notify_list,"拨测异常通知","\r\n".join(errorinfo))

def main_handler(event, context):
    test_url(test_url_list)
    

if __name__ == '__main__':
    main_handler("", "")

在这段代码里,我们需要拨测的地址放置在 test_url_list 列表中,并在拨测时,通过 GET 方法发起调用。在发起调用后,无论是 URL 访问超时,还是返回的 HTTP 状态码错误,均会记录 URL 拨测结果,并通过 Email 发送出来。而 Email 的发送配置,我们存储在 email_server_config 中,并且从环境变量中获取 Email server 的登录密码,避免在代码中暴露密码的泄露风险。同时,通知邮件的接收者,通过 email_notify_list 这个列表保存,向这个列表中添加更多的邮件地址,可以确保更多相关人员在拨测到异常时,接收到告警邮件。

同时,这段代码中的邮件服务器使用的是 QQ 邮箱。QQ 邮箱的 SMTP 邮件发送服务,可以在邮箱的设置-账号中开启,并且在开启 SMTP 服务后,可以通过申请授权码,作为邮箱的登录账号使用。

云函数配置

接下来,我们通过创建函数,配置触发,让拨测可以正常的运行起来。

创建及配置函数

首先我们来创建和配置函数。创建前,我们可以先选择合适的地域来部署函数,甚至可以选择为多地域同时部署,检验多地发起拨测时的联通性。选择好地域后,我们创建函数,输入函数名,选择运行环境为 Python 2。同时,函数的运行超时也需要一定程度的放大,例如设置为 60s,避免因为拨测时 URL 访问超时导致的函数运行超时,无法正常发出邮件。同时在创建函数时,我们也需要配置好函数的环境变量,设置 EMAIL_PWD 环境变量名,并填写通过邮箱配置获得的登录授权码,或登录密码。

在函数代码界面,可以通过把本地已经存储的 index.py 文件夹打包成 zip 包,然后上传的方式提交代码,也可以通过直接在代码编辑窗口粘贴如上代码的方式,完成代码提交和保存。

在配置触发器时,我们可以先跳过这个步骤,完成函数运行测试后再配置定时触发器启动函数。

测试及启动函数

完成函数创建后,我们可以通过 "测试" 案例触发函数,查看运行情况。拨测函数未处理函数入参,因此任何测试入参,或者无入参都可以触发函数。通过测试时的输出日志,我们可以查看拨测结果,邮件发送情况。

通过日志确认函数运行正确后,我们就可以根据需求配置上触发器,开始函数的定时拨测运行。最简单的可以通过选择每 5 分钟运行一次来进行拨测,如果有特殊的定时运行需求,也可以通过自行填写 cron 格式来选择合适的运行触发时间或周期。

璟櫆工具开发者回答于

可以使用腾讯云 SCF 云函数压缩 COS 对象存储文件

尝试一种流式文件处理的方式,通过单个文件压缩后数据立即提交 COS 写的方法,一次处理一个文件,使得被压缩文件无需在 SCF 的缓存空间内堆积,压缩文件也无需放在缓存或内存中,而是直接写入 COS。在这里,实际利用了两种特性:ZIP 文件的数据结构特性和 COS 的分片上传特性

zip 文件的数据结构

在官方文档中给出的 zip 文件格式如下:

  Overall .ZIP file format:

    [local file header 1]
    [file data 1]
    [data descriptor 1]
    . 
    .
    .
    [local file header n]
    [file data n]
    [data descriptor n]
    [archive decryption header] (EFS)
    [archive extra data record] (EFS)
    [central directory]
    [zip64 end of central directory record]
    [zip64 end of central directory locator] 
    [end of central directory record]

COS 分片上传

COS 分片上传按照如下操作即可进行:

  1. 初始化分片上传:通过初始化动作,获取到此次上传的唯一标识ID。此ID需要保存在本地并在后续上传分片时使用。
  2. 上传分片:通过初始化时获取到的ID,配合文件分片的顺序编号,依次上传文件分片,获取到每个分片的ETag;COS 会通过 ID 和分片顺序编号,拼接文件。
  3. 结束上传:通过初始化时获取到的ID,结合分片的顺序编号和ETag,通知 COS 分片上传已经完成,可以进行拼接。

在上传过程中,还随时可以查询已上传分片,或结束取消分片上传。

文件压缩处理流程设计

利用 zip 文件数据结构中文件压缩数据在前目录和额外标识在后的特性,和 COS 支持分片上传的特性,我们可以利用流式文件处理方式来依次处理文件,并且做到处理完成一个文件压缩就上传处理后的压缩数据分片。这种处理流程可以简化为如下说明:

  1. 初始化 zip 文件数据结构,并将数据结构保存在内存中。
  2. 初始化 COS 分片上传文件,保存好分片上传 ID。
  3. 下载要放入压缩包的文件至本地,使用 zip 算法,生成压缩文件的数据内容并保存在内存中,并根据目录格式,更新zip数据格式中的目录标识。
  4. 将压缩后的文件数据使用 COS 上传分片,上传至 COS 中。
  5. 清理删除下载至本地的需压缩文件。
  6. 根据需要,重复 3~5 步骤,增加压缩包内的文件。
  7. 在压缩文件处理完成后,使用分片上传,将内存中的 zip 文件数据结构最后的目录结构部分上传至 COS。
  8. 通知 COS 结束上传,完成最终 zip 文件的自动拼接。

在这个处理流程中,一次只处理一个文件,对本地缓存和内存使用都只这一个文件的占用,相比下载全部文件再处理,大大减小了本地缓存占用和内存占用,这种情况下,使用少量缓存和内存就可以完成 COS 中大量文件的压缩打包处理。

使用SCF进行 COS 文件压缩处理实现

流式压缩文件库 archiver

我们这里使用 node.js 开发语言来实现 COS 文件压缩处理。我们这里使用了 cos-nodejs-sdk-v5 sdk 和 archiver 模块。其中 archiver 模块是实现zip和tar包压缩的流式处理库,能够通过 append 输入欲压缩文件,通过 stream 输出压缩后的文件流。archiver的简单用法如下:

// require modules
var fs = require('fs');
var archiver = require('archiver');

// create a file to stream archive data to.
var output = fs.createWriteStream(__dirname + '/example.zip');
var archive = archiver('zip', {
    zlib: { level: 9 } // Sets the compression level.
});

// pipe archive data to the file
archive.pipe(output);

// append a file from stream
var file1 = __dirname + '/file1.txt';
archive.append(fs.createReadStream(file1), { name: 'file1.txt' });

// append a file
archive.file('file1.txt', { name: 'file4.txt' });

// finalize the archive (ie we are done appending files but streams have to finish yet)
// 'close', 'end' or 'finish' may be fired right after calling this method so register to them beforehand
archive.finalize();

archiver 将会在每次 append 文件的时候,将文件的压缩数据输出到 pipe 指定的输出流上。因此,我们在这里可以通过实现我们自身的 WriteStream,获取到 archiver 的写请求,并把写入内容转移到 COS 模块的分片上传能力上。在这里,我们实现的 WriteStream 为:

var Writable = require('stream').Writable;
var util = require('util');

module.exports = TempWriteStream;

let handlerBuffer;

function TempWriteStream(options) {
  if (!(this instanceof TempWriteStream))
    return new TempWriteStream(options);
  if (!options) options = {};
  options.objectMode = true;
  handlerBuffer = options.handlerBuffer;
  Writable.call(this, options);
}

util.inherits(TempWriteStream, Writable);

TempWriteStream.prototype._write = function write(doc, encoding, next) {
  handlerBuffer(doc);
  process.nextTick(next)
};

通过集成 nodejs 中的 Writable stream,我们可以将写操作转移到我们所需的 handle 上去,handle 可以对接 COS 的分片上传功能。

COS 分片上传

COS 分片上传功能的实现如下,我们将其封装为 Upload 模块:

const cos = require('./cos')

let Duplex = require('stream').Duplex;
function bufferToStream(buffer) {
    let stream = new Duplex();
    stream.push(buffer);
    stream.push(null);
    return stream;
}

// 大于4M上传
const sliceSize = 4 * 1024 * 1024

function Upload(cosParams) {
    this.cosParams = cosParams;
    this.partNumber = 1;
    this.uploadedSize = 0;
    this.Parts = []
    this.tempSize = 0;
    this.tempBuffer = new Buffer('')
}

Upload.prototype.init = function (next) {
    const _this = this;
    cos.multipartInit(this.cosParams, function (err, data) {
        _this.UploadId = data.UploadId
        next()
    });
}
Upload.prototype.upload = function(partNumber, buffer) {
    const _this = this;
    const params = Object.assign({
        Body: bufferToStream(buffer),
        PartNumber: partNumber,
        UploadId: this.UploadId,
        ContentLength: buffer.length
    }, this.cosParams);
    cos.multipartUpload(params, function (err, data) {
        if (err) {
            console.log(err)
        } else {
            _this.afterUpload(data.ETag, buffer, partNumber)
        }
    });
}


Upload.prototype.sendData = function (buffer) {
    this.tempSize += buffer.length;
    if (this.tempSize >= sliceSize) {
        this.upload(this.partNumber, Buffer.concat([this.tempBuffer, buffer]))
        this.partNumber++;
        this.tempSize = 0;
        this.tempBuffer = new Buffer('')
    } else {
        this.tempBuffer = Buffer.concat([this.tempBuffer, buffer]);
    }
}

Upload.prototype.afterUpload = function (etag, buffer, partNumber) {
    this.uploadedSize += buffer.length
    this.Parts.push({ ETag: etag, PartNumber: partNumber })
    if (this.uploadedSize == this.total) {
        this.complete();
    }
}

Upload.prototype.complete = function () {
    this.Parts.sort((part1, part2) => {
        return part1.PartNumber - part2.PartNumber
    });
    const params = Object.assign({
        UploadId: this.UploadId,
        Parts: this.Parts,
    }, this.cosParams);
    cos.multipartComplete(params, function (err, data) {
        if (err) {
            console.log(err)
        } else {
            console.log('Success!')
        }
    });
}

Upload.prototype.sendDataFinish = function (total) {
    this.total = total;
    this.upload(this.partNumber, this.tempBuffer);
}

module.exports = Upload;

对于 COS 本身已经提供的 SDK,我们在其基础上封装了相关查询,分片上传初始化,分片上传等功能如下:

const COS = require('cos-nodejs-sdk-v5');

const cos = new COS({
    AppId: '125xxxx227',
    SecretId: 'AKIDutrojxxxxxxx5898Lmciu',
    SecretKey: '96VJ5tnlxxxxxxxl5To6Md2',
});
const getObject = (event, callback) => {
    const Bucket = event.Bucket;
    const Key = event.Key;
    const Region = event.Region
    const params = {
        Region,
        Bucket,
        Key
    };
    cos.getObject(params, function (err, data) {
        if (err) {
            const message = `Error getting object ${Key} from bucket ${Bucket}.`;
            callback(message);
        } else {
            callback(null, data);
        }
    });
};

const multipartUpload = (config, callback) => {
    cos.multipartUpload(config, function (err, data) {
        if (err) {
            console.log(err);
        }
        callback && callback(err, data);
    });
};

const multipartInit = (config, callback) => {
    cos.multipartInit(config, function (err, data) {
        if (err) {
            console.log(err);
        }
        callback && callback(err, data);
    });
};

const multipartComplete = (config, callback) => {
    cos.multipartComplete(config, function (err, data) {
        if (err) {
            console.log(err);
        }
        callback && callback(err, data);
    });
};

const getBucket = (config, callback) => {
    cos.getBucket(config, function (err, data) {
        if (err) {
            console.log(err);
        }
        callback && callback(err, data);
    });
};


module.exports = {
    getObject,
    multipartUpload,
    multipartInit,
    multipartComplete,
    getBucket
};

在具体使用时,需要将文件中 COS 相关登录信息的APPId,SecretId,SecretKey等替换为自身可用的真实内容。

功能入口实现函数

我们在最终入口函数 index.js 中使用各个组件来完成最终的目录检索,文件压缩打包上传。在这里,我们利用函数入参来确定要访问的 bucket 名称和所属地域,期望压缩的文件夹和最终压缩后文件名。云函数入口函数仍然为 main_handler。

// require modules
const fs = require('fs');
const archiver = require('archiver');

const cos = require('./cos');

const Upload = require('./Upload')

const TempWriteStream = require('./TempWriteStream')

const start = new Date();

const getDirFileList = (region, bucket, dir, next) => {
    const cosParams = {
        Bucket: bucket,
        Region: region,
    }
    const params = Object.assign({ Prefix: dir }, cosParams);

    cos.getBucket(params, function (err, data) {
        if (err) {
            console.log(err)
        } else {
            let fileList = [];
            data.Contents.forEach(function (item) {
                if (!item.Key.endsWith('/')) {
                    fileList.push(item.Key)
                }
            });
            next && next(fileList)
        }
    })
}

const handler = (region, bucket, source, target) => {

    const cosParams = {
        Bucket: bucket,
        Region: region,
    }
    const multipartUpload = new Upload(Object.assign({ Key: target}, cosParams));

    const output = TempWriteStream({ handlerBuffer: multipartUpload.sendData.bind(multipartUpload) })

    var archive = archiver('zip', {
        zlib: { level: 9 } // Sets the compression level.
    });
    output.on('finish', function () {
        multipartUpload.sendDataFinish(archive.pointer());
    });

    output.on('error', function (error) {
        console.log(error);
    });

    archive.on('error', function (err) {
        console.log(err)
    });

    archive.pipe(output);

    multipartUpload.init(function () {
        getDirFileList(region, bucket, source, function(fileList) {
            let count = 0;
            const total = fileList.length;
            for (let fileName of fileList) {
                ((fileName) => {
                    let getParams = Object.assign({ Key: fileName }, cosParams)
                    cos.getObject(getParams, (err, data) => {
                        if (err) {
                            console.log(err)
                            return
                        }
                        var buffer = data.Body;
                        console.log("download file "+fileName);
                        archive.append(buffer, { name: fileName.split('/').pop() });
                        console.log("zip file "+fileName);
                        count++;
                        if (count == total) {
                            console.log("finish zip "+count+" files")
                            archive.finalize();
                        }
                    })
                })(fileName)
            }
        })
    })
}

exports.main_handler = (event, context, callback) => {
    var region = event["region"];
    var bucket = event["bucket"];
    var source = event["source"];
    var zipfile = event["zipfile"];
    //handler('ap-guangzhou', 'testzip', 'pic/', 'pic.zip');
    handler(region, bucket, source, zipfile)
}

测试及输出

最终我们将如上的代码文件及相关依赖库打包为zip代码包,创建函数并上传代码包。同时我们准备好一个 COS Bucket命名为 testzip, 在其中创建 pic 文件夹,并在文件夹中传入若干文件。通过函数页面的测试功能,我们使用如下模版测试函数:

{
"region":"ap-guangzhou",
"bucket":"testzip",
"source":"pic/",
"zipfile":"pic.zip"
}

函数输出日志为:

...
2017-10-13T12:18:18.579Z 9643c683-b010-11e7-a4ea-5254001df6c6 download file pic/DSC_3739.JPG
2017-10-13T12:18:18.579Z 9643c683-b010-11e7-a4ea-5254001df6c6 zip file pic/DSC_3739.JPG
2017-10-13T12:18:18.689Z 9643c683-b010-11e7-a4ea-5254001df6c6 download file pic/DSC_3775.JPG
2017-10-13T12:18:18.690Z 9643c683-b010-11e7-a4ea-5254001df6c6 zip file pic/DSC_3775.JPG
2017-10-13T12:18:18.739Z 9643c683-b010-11e7-a4ea-5254001df6c6 download file pic/DSC_3813.JPG
2017-10-13T12:18:18.739Z 9643c683-b010-11e7-a4ea-5254001df6c6 finish zip 93 files
2017-10-13T12:18:56.887Z 9643c683-b010-11e7-a4ea-5254001df6c6 Success!

可以看到函数执行成功,并从 COS Bucket 根目录看到新增加的 pic.zip 文件。

项目源代码及改进方向

目前项目所有源代码已经放置在 Github 上,路径为 https://github.com/qcloud-scf/demo-scf-compress-cos。可以通过下载或 git clone 项目,获取到项目源代码,根据自身帐号信息,修改 cos 文件内的帐号 APPId、SecretId、SecretKey这些认证信息,然后将根目录下所有文件打包至 zip 压缩包后,通过 SCF 创建函数并通过 zip 文件上传代码来完成函数创建,根据上面所属的“测试及输出”步骤来测试函数的可用性。

函数在此提供的仍然只是个demo代码,更多的是为大家带来一种新的思路及使用腾讯云 SCF 无服务器云函数和 COS 对象存储。基于此思路,Demo本身后续还有很多可以改进的方法,或根据业务进行变化的思路:

  1. 文件的处理目前还是下载一个处理一个,其实我们可以使用多线程和队列来加速处理过程,使用若干线程持续下载文件,使用队列对已经下载完成待处理的文件进行排队,然后使用一个压缩线程从队列中读取已下载的文件后进行压缩上传处理。这种方式可以进一步加快大量文件的处理速度,只是需要小心处理好缓存空间被使用占满后的等待和文件处理完成后的删除释放空间。
  2. 目前 Demo 从入参接受的是单个地域、Bucket、目录和输出文件,我们完全可以改造为从多个地域或Bucket拉取文件,也可以传递指定的文件列表而不是仅一个目录,同时函数执行触发可以使用 COS 触发或 CMQ 消息队列触发,能够形成更加通用的压缩处理函数。
萌萌哒小昕玥一直积极治疗的神经病~回答于

以后应该会广泛应用到,酒香不怕巷子深

那一年淡然Java研发工程师回答于

可以自动定时备份数据库,再也不用花那么多步骤了

可能回答问题的人

  • 腾讯云计算产品团队

    腾讯云 · 产品团队 (已认证)

    137 粉丝0 提问0 回答
  • CVM 产品团队

    28 粉丝0 提问7 回答
  • 小仙女和科学家

    9 粉丝0 提问0 回答
  • 怕冷的阳阳

    腾讯云 · 高级工程师 (已认证)

    19 粉丝1 提问0 回答
  • DRRR

    腾讯云 · 产品经理 (已认证)

    7 粉丝0 提问4 回答
  • 腾讯云serverless团队

    腾讯云 · 产品团队 (已认证)

    86 粉丝0 提问24 回答

扫码关注云+社区

领取腾讯云代金券