用SCF云函数输出gzip/deflate压缩过的数据,以及API网关的Bug

最近用腾讯云SCF做了一个免费的高考志愿服务 填教授 需要输出大量的数据,这个时候直接吐数据给客户端就又慢又贵了,作为一个免费服务,还是需要努力降低流量成本的,因此尝试了一下SCF输出压缩数据。

代码语言:javascript
复制
'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会保存下来压缩过的文件,因此使用数据的程序需要自行解压数据。

官方推荐的一个也挺恶心的方式是自定义插件。