You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google Cloud Functions每次调用耗时逐次递增问题排查

问题核心原因

你的接口耗时持续上涨的根因是Redis连接逻辑完全违背了Cloud Functions的实例复用模型,叠加串行IO的性能问题,两者共同导致耗时随请求量线性恶化:

  • 你在全局作用域创建了Redis客户端实例,但每次请求处理时都重复调用client.connect()新建连接,请求结束又强制调用client.disconnect()断开连接。Cloud Functions会在冷启动后复用同一个运行实例处理多批请求,这种反复连断的逻辑会产生大量未正常释放的半开连接,很快占满Redis服务端的连接池,后续新的连接请求只能排队等待资源,请求量越大排队越久,耗时自然持续上涨。
  • 两个核心业务接口(/games、/playercount)中,你使用for循环串行执行Redis查询,每个查询都要等上一个查询返回才能发起,当游戏房间码数量随请求量上涨时,这部分串行IO的耗时会线性增长,和连接泄漏的问题叠加后,耗时会直接翻几十倍。
  • 现有Redis逻辑没有做异常兜底,一旦某次连接出错,客户端内部状态会错乱,后续请求会卡在隐式重试、重复建连的流程里,进一步拉长响应时间。另外你写的socket: 6379是无效配置参数,node-redis v4版本不会读取这个字段,配置错误也会增加不必要的重连开销。
修复方案

按以下步骤改完即可解决耗时持续上涨的问题:

  1. 改造Redis连接逻辑,全局复用长连接,禁止每个请求反复连断
    把连接逻辑抽成全局单例,只在客户端未就绪时建连,不要在请求结束时主动断开连接,供同实例下的所有请求复用:
    const client = redis.createClient({
        database: 0,
        password: "hunter123",
        url: "redis://database.skeld.net:6379" // 把端口直接拼到URL里,去掉无效的socket参数
    });
    
    let isRedisReady = false;
    async function getRedis() {
        if (!isRedisReady) {
            await client.connect();
            isRedisReady = true;
            // 监听连接错误,标记状态以便后续自动重连
            client.on("error", () => {
                isRedisReady = false;
            });
        }
        return client;
    }
    
    接口内直接调用const client = await getRedis()拿实例即可,删除所有请求内的client.connect()和client.disconnect()调用。
  2. 把串行Redis查询改成批量并行查询
    不要在循环里逐个await Redis请求,改用Promise.all一次性发起所有查询,大幅降低IO等待时间,以/games接口为例:
    app.get("/games", async (request, response) => {
        console.log("/games request");
        const client = await getRedis();
        const codes = await client.sMembers("skeldsync.games");
        response.set("Cache-Control", "public, max-age=10, s-maxage=20");
        response.setHeader("Access-Control-Allow-Origin", "*");
        const lobbyInfos = [];
        // 批量并行拉取所有房间信息
        const lobbyList = await Promise.all(
            codes.map(code => client.hGetAll("ImpostorRedis" + code))
        );
        for (const lobbyInfo of lobbyList) {
            if (lobbyInfo.in_progress == "true") continue;
            lobbyInfos.push(lobbyInfo.lobby_data);
        }
        response.send(JSON.stringify(lobbyInfos));
        console.log("Lobby infos length: " + lobbyInfos.length);
    });
    
    /playercount接口同理改造,用Promise.all批量拉取所有房间的玩家人数字段,再累加计算总数即可。
  3. 可选优化:你已经给接口配置了Cache-Control响应头,可以直接给Cloud Functions配层CDN,静态接口(/regionInfo、/ip、/deeplink)的请求根本不需要打到函数实例,能进一步降低负载。

改完后连接泄漏问题会彻底消失,Redis连接数会稳定在合理范围,单条Redis命令的耗时会回归正常水平,加上并行查询的优化,哪怕请求量涨到数万级,接口耗时也会稳定在百毫秒级,不会出现持续上涨到十几秒的情况。

内容的提问来源于stack exchange,提问作者Daniel Budworth-Mead

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 02:24:24