背景
笔者最近在开发一个小程序,数据是从网站上爬下来的。但是爬数据需要登录,也就是需要有 cookie。
众所周知,这种爬数据的任务,用一个 FaaS 云函数(阿里叫函数计算 FC)就可以了。非常的经济实惠,按量付费,上个月一共 5k 次调用花了 0.7 元。小流量的服务用这个绝对够了,真的香~~
但是,需要 cookie 的这个场景碰到了点问题。最好的办法呢,就是只获取一次 cookie,然后把它存起来,每次需要的时候读一下。等过期了,更新一下就好了。
(如何获取 cookie 可以看这篇《你知道 FaaS 还能用 Puppeteer 吗?》)
好的,那么问题来了。这个 cookie 存哪,也就是存储的问题:
- 内存?用一个变量存在内存里无疑是最方便的,但是 FaaS 每次运行都不是在一个机器里啊,第二次请求根本就读不到。所以内存不行;
- 数据库?毫无疑问是可以的,而且最好是 Redis。然后看了看价格,最低配置的 0.08 元/小时。嗯~~好像不太贵。算算一个月多少钱,0.08 x 24 x 30 = 57.6。什么??!!这么贵??!!不行不行,我这么节(kou)俭(men)的人,怎么能选这么奢侈的方案,果断换;
- 对象存储?也就是 OSS。正好那天阿里云的客服给我打电话,他说 OSS 一年才 9 块钱(还是 7 块钱来着)。嚯??还有这好事?就是它了。
正文
新建 FaaS
确定了存储用 OSS,接下来就是如何用 FaaS 调用 OSS 了(新建 OSS 的步骤这里就不介绍了)。毫不意外的,我们在新建函数的模板里找到了现成的 Demo,直接点击新建:
然后来看看 Demo 的代码:
'use strict';
var OSS = require('ali-oss');
exports.handler = async (event, context, callback) => {
// const eventObj = JSON.parse(event.toString());
// 注意 context.credentials
let client = new OSS({
region: `oss-${context.region}`,
accessKeyId: context.credentials.accessKeyId,
accessKeySecret: context.credentials.accessKeySecret,
stsToken: context.credentials.securityToken,
internal: true,
bucket: 'YOUR_BUCKET_NAME'
});
try {
let result = await client.put('test.txt', new Buffer('Hello OSS'));
result = await client.get('test.txt');
const content = result.content.toString('utf8');
console.log(content);
callback(null, content);
} catch (e) {
console.log(e);
callback(e, null)
}
}
Demo 给的很简单明了,这里面说个关键点。context.credentials 这块,是自带的,不需要修改。但是需要给 FaaS 授权个有操作 OSS 权限的角色,然后修改 bucket 的名字就可以了。不得不说,这个真的是太方便了,不需要处理 secret、token 什么的。直接点「测试函数」,就能在 OSS 里看到多了一个 test.txt 文件。
代码实现
接下来我们为了方便调用,优化一下这块的代码,把它 RESTful 化。分别利用,PUT、GET、DELETE 来处理不同的逻辑。
'use strict';
var OSS = require('ali-oss');
exports.handler = async (event, context, callback) => {
const eventObj = JSON.parse(event.toString());
const { queryParameters, body, requestContext } = eventObj;
let client = new OSS({
region: `oss-${context.region}`,
accessKeyId: context.credentials.accessKeyId,
accessKeySecret: context.credentials.accessKeySecret,
stsToken: context.credentials.securityToken,
internal: true,
bucket: 'YOUR_BUCKET_NAME'
});
const result = {
code: 0,
data: null,
message: 'success',
};
try {
switch (requestContext.http.method) {
case 'GET': {
const { filename } = queryParameters;
const file = await client.get(filename); // 'test.txt'
result.data = { content: file.content.toString('utf8') };
}
break;
case 'PUT': {
const bodyObj = JSON.parse(body)
const { filename, content } = bodyObj;
result.data = await client.put(filename, new Buffer(content));
}
break;
case 'DELETE': {
const bodyObj = JSON.parse(body)
const { filename } = bodyObj;
result.data = await client.delete(filename);
}
break;
}
callback(null, result);
} catch (e) {
console.log(e);
result.code = e.status;
result.message = JSON.stringify(e);
callback(null, result);
}
}
好了,事情到此基本上就完成了。不过如果这种接口,暂时可都是没有做任何权限控制的。要是被人发现了,直接调用接口往里面写入大量文件,把你 OSS 爆了……咳咳,这不关我事啊。
结语
一些简单的、少量的数据用 OSS 存储,的确非常的经济实惠。甚至一些列表类的数据,存个 csv 的文件就够用了,走数据库多贵啊。
好了,每天一个省钱小技能就到这,各位下期见(好像串台了……)。