生图API 怎么避免重复计费?加一层结果缓存和请求去重(nano-banana-pro 实践)
2026/8/15 21:07:09 网站建设 项目流程

有个月对账,我发现出图次数比预期高了三成。查下来原因很蠢:同样的提示词被重复调用了。

重复是怎么产生的

三个来源,都很常见:

一、用户手抖。生成按钮点了没反应(其实在跑),用户又点一次,两个一模一样的请求同时打出去。

二、重试打偏。网络超时触发重试,但服务端其实已经生成成功了,只是响应没回来。重试等于又生成一张。

三、批量任务重跑。跑到一半挂了,重跑的时候没有断点,前面成功的那些又生成了一遍。

三种加起来,浪费的比例相当可观。

解法:一个 key 管两件事

给每次请求算一个内容指纹,用它同时做缓存和去重。

const crypto = require('crypto'); function fingerprint({ model, prompt, aspect, size, refImage }) { return crypto.createHash('sha256') .update(JSON.stringify({ model, prompt, aspect, size, refImage: refImage || '' })) .digest('hex'); }

关键是把所有影响出图结果的参数都算进去。漏掉aspect_ratio的话,同一句提示词出方图和长图会撞成同一个 key,取到错的缓存。

缓存 + 在途去重

const cache = new Map(); // fp -> 结果 const inflight = new Map(); // fp -> 正在跑的 Promise async function genImage(params) { const fp = fingerprint(params); // 1) 已经生成过,直接返回 if (cache.has(fp)) return cache.get(fp); // 2) 正在生成中,复用同一个 Promise —— 这一步挡住了「用户连点两次」 if (inflight.has(fp)) return inflight.get(fp); const p = callGenApi(params) .then((r) => { cache.set(fp, r); return r; }) .finally(() => inflight.delete(fp)); inflight.set(fp, p); return p; }

inflight这层比缓存还重要。缓存只能挡住"已经跑完的重复",而用户连点、并发重试制造的是"同时发出的重复"——那时候缓存里什么都没有,只有在途表能挡住。

什么时候不该走缓存

得留一个绕过缓存的口子,否则用户会困惑:"我就是想换一张,为什么每次都给我同一张。"

async function genImage(params, { noCache = false } = {}) { if (noCache) return callGenApi(params); // ... }

界面上的「重新生成」按钮走noCache: true,正常路径走缓存。

持久化

内存 Map 重启就没了,批量任务重跑的场景挡不住。生产上建议落到 Redis 或数据库:

  • key:指纹
  • value:图片地址 + 生成时间
  • TTL:按图床的链接有效期定,别缓存得比链接还久

该服务返回的是图片链接,如果你把图转存到了自己的存储,缓存里存自己的地址,就不用担心过期问题。

上线之后

重复调用基本归零,那三成的额外消耗省下来了。

顺带的好处是用户体验变好了:连点两次不会等两倍时间,第二次直接复用第一次的结果,秒返回。

这类优化投入很小——核心逻辑就上面那二十来行——但账单和体验都能立刻看到变化,属于性价比很高的一类活。


接口服务:甜甜圈API(dashengfenshen.cn)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询