Flask API本地正常,部署Heroku后偶现返回过时数据求助
我开发了一个投票网站,本地测试功能完全正常,但部署到Heroku后,会随机获取到错误/过时的JSON数据。比如选票原有候选人c1、c2、c3,移除c3后,c3有时仍会显示,有时则不会。即使在Firefox开发者工具中禁用缓存、用Ctrl+F5强制刷新也无法解决。
前端缓存破击与禁用缓存
在请求URL后添加时间戳参数,并设置请求头禁用缓存:
useEffect(() => { const fetchVotingStatus = async () => { try { const response = await fetch(`/get_voting_status?_t=${timestamp}`, { method: 'GET', headers: { 'Cache-Control': 'no-cache, no-store', 'Pragma': 'no-cache', 'Expires': '0', }, }) const data = await response.json(); if (!data.voting_status.President) { // 总统投票关闭,重定向到首页 navigate('/'); } else { // 投票开放,获取候选人列表 fetchCandidates(); } } catch (error) { console.error('获取投票状态出错:', error); } };
后端添加禁用缓存响应头
通过after_request中间件给指定路由添加禁用缓存的响应头:
def add_no_cache_headers(response): response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate, max-age=0' response.headers['Pragma'] = 'no-cache' response.headers['Expires'] = '0' response.headers['Access-Control-Allow-Origin'] = '*' return response @app.after_request def apply_no_cache(response): no_cache_routes = [ 'no_cache', 'get_voter', 'getJsonVoteStatus', 'handle_submit_vote', 'open_vote', 'close_vote', 'pres_candidates', 'memb_candidates', 'AO_candidates', 'SE_candidates', 'MC_candidates', 'finance_candidates', 'IandB_candidates', 'othervote_prompt', 'othervote_options', 'open_vote', 'close_vote' ] if request.endpoint in no_cache_routes: return add_no_cache_headers(response) return response
核心怀疑点:后端全局变量的持久化问题
本地测试通常是单进程运行,全局变量的修改会立即生效;但Heroku默认使用多进程/多实例部署,每个进程会维护自己的全局变量副本。当你修改了某个进程中的全局变量(比如移除候选人c3),其他进程的全局变量还是旧值,导致请求被分发到不同进程时,返回的数据随机出现新旧两种状态。这是最可能的原因。
具体解决步骤
替换全局变量为外部持久化存储
把存储候选人列表、投票状态等数据的全局变量,换成数据库(比如PostgreSQL,Heroku支持免费版)、Redis缓存。所有数据读写都通过统一的外部存储,确保所有进程获取的是同一数据源。验证Heroku的进程数量
检查Heroku的dyno配置,默认可能启动多个web进程。可以临时设置为单进程测试:heroku ps:scale web=1如果单进程下问题消失,就坐实了多进程全局变量不一致的问题。
排查Heroku的路由缓存
虽然你已经设置了缓存头,但Heroku的路由层(Router)可能会对GET请求做缓存。可以在响应头中添加Vary: Accept-Encoding,或者确保Cache-Control配置完全覆盖路由层的缓存策略。另外,确认你的get_voting_status等路由确实被包含在no_cache_routes列表中,没有拼写错误(比如getJsonVoteStatus和前端请求的/get_voting_status是否对应正确的endpoint名称)。前端请求的幂等性验证
确保fetchCandidates()函数也使用了同样的缓存破击策略(添加时间戳参数),避免候选人数据的请求被缓存。
内容的提问来源于stack exchange,提问作者user8394642

