Java后端的翻译插件开发:并发场景下的三个坑
背景
最近在一个 Java 后端项目里集成了翻译能力(文档国际化需求),踩了三个并发相关的坑,记录下来给同样做后端集成的朋友参考。
坑一:API 限流
翻译 API 通常有频率限制(比如每分钟 60 次请求)。单线程调用没问题,但后端服务是多线程并发——10 个请求同时进来,每个请求要翻 5 段文本,瞬间 50 次 API 调用,直接触发限流。
解法一:令牌桶限流。 在调用层加一个令牌桶,控制每秒发出的请求数。Guava 的 RateLimiter 够用。
private static final RateLimiter rateLimiter = RateLimiter.create(50.0); // 每秒 50 次
public String translate(String text) {
rateLimiter.acquire(); // 阻塞等待令牌
return translationApi.translate(text);
}
解法二:批量合并。 把多个短文本合并成一个请求(如果 API 支持批量),减少请求次数。
坑二:线程池堆积
翻译 API 响应时间不稳定,正常 200ms,偶尔 2~3 秒。如果直接用同步调用,慢请求会占住线程池,把正常请求堵在外面。
解法:异步 + 超时。 翻译调用走异步,设 3 秒超时,超时走降级(返回原文或缓存)。
CompletableFuture<String> future = CompletableFuture.supplyAsync(
() -> translationApi.translate(text), executor
);
try {
return future.get(3, TimeUnit.SECONDS);
} catch (TimeoutException e) {
return text; // 降级:返回原文
}
坑三:缓存一致性
翻译结果通常会缓存(同样的原文不应该重复翻译,费钱)。但缓存有个坑:如果翻译模型升级了,同样的输入可能得到不同的输出,缓存里的旧结果和新结果不一致。
解法:版本化缓存 key。 在缓存 key 里加上模型版本号,模型升级后旧缓存自动失效。
String cacheKey = "v2:" + DigestUtils.md5Hex(text); // v2 = 模型版本
一个额外提醒
翻译 API 的计费是按 token 算的。并发场景下,如果不对单次请求的文本长度做限制,一个超长文本可能消耗大量 token,瞬间把预算烧完。
我的做法:单次请求限制 2000 字,超过就分段。分段时注意别从段落中间切断,按句子边界切。
总结
后端集成翻译能力,核心不是"能不能调通",是"并发下稳不稳"。限流、超时、缓存,三个坑踩完就稳了。
Java 后端开发,偶尔做点翻译相关的事。关注我,分享更多后端实战。
浙公网安备 33010602011771号