最近用腾讯云SCF做了一个免费的高考志愿服务 填教授 需要输出大量的数据,这个时候直接吐数据给客户端就又慢又贵了,作为一个免费服务,还是需要努力降低流量成本的,因此尝试了一下SCF输出压缩数据。
'use strict';
const zlib = require("zlib");
exports.main_handler = async (event, context, callback) => {
let res = new Array(1000).join("就是想测试一下SCF云函数输出做压缩好不好使\n");
let encoding = event.headers['accept-encoding']||event.headers['Accept-Encoding'];
console.log(encoding)
if(/gzip/.test(encoding)){
let data = zlib.gzipSync(res).toString("base64");
return {
isBase64Encoded:true,
statusCode:200,
headers:{
"Content-Type":"text/json; charset=utf-8",
'Access-Control-Allow-Origin:':'*',
'Content-Encoding': 'gzip'
},
body:data
}
}else if(/deflate/.test(encoding)){
let data = zlib.deflateSync(res).toString("base64");
return {
isBase64Encoded:true,
statusCode:200,
headers:{
"Content-Type":"text/json; charset=utf-8",
'Access-Control-Allow-Origin:':'*',
'Content-Encoding': 'deflate'
},
body:data
}
}else{
return {
"isBase64Encoded": false,
"statusCode": 200,
"headers": {
"Content-Type":"text/json; charset=utf-8",
'Access-Control-Allow-Origin:':'*',
},
"body": res
};
}
};
需要做的主要就是把要返回的数据用zlib给压缩一下,然后用base64编码,最后通过集成响应方式吧编码数据输出给api网关。
看一下效果还不错:
压缩比、压缩程度等更多压缩选项可以参考 https://wiki.jikexueyuan.com/project/nodejs/zlib.html
刚看了一下API网关配置,在基础配置中不知道什么时候出现了“响应压缩”选项,还是默认选中的。这本来是一个很方便的好事,但是这次API网关的升级却带来了几个新的bug:
1 响应压缩无法关闭,唯一的关闭方式是自己声明content-encoding。而按照http协议,唯一允许声明content-encoding的情况是压缩,所以如果按照协议实现的话就还是无法关闭压缩,除非丢开协议不管了自己耍流氓声明个自定义的编码方式。
2 响应压缩声称对1k以内的内容不压缩,实际上对于非集成响应的SCF也没有兑现。
3 不管是否使用集成响应,api网关都不能正确吐出content-length头部了。就算SCF里面显式的声明了content-length也不行(唯一的例外是使用集成响应并且吐出的内容非常小的情况)。丢失了content-length信息会导致其他程序和系统出现不可预测的异常,比如COS通过api网关回源到SCF的情况下,如果获取不到content-length会直接保存下来一个0字节文件。
绕过这些bug的一个恶心的方式是scf总是压缩数据后输出,让api网关总是忽略相应压缩直接把压缩好的数据送出,此时cos会保存下来压缩过的文件,因此使用数据的程序需要自行解压数据。
官方推荐的一个也挺恶心的方式是自定义插件。