Rails+Nginx+Puma环境下用户数据串位问题求助
在Rails 4.2.6 + Nginx + Puma环境中,接入Nginx后出现用户数据串位异常:第一个请求用户(如Henry)携带自定义HTTP头HTTP_MY_APP_REQUEST_USER发起请求后,后续其他用户(如Jane)的请求仍返回首个用户的数据。直接请求Puma时功能正常,已尝试添加Cache-Control头但无效果,无法确定问题根源是Nginx配置、Rails的session_store :active_record_store还是Ruby Thread的使用。
相关代码
application_controller.rb
def check_authentication(method_name) begin user_id = (request.headers['HTTP_MY_APP_REQUEST_USER'].to_i / 317) rescue 0 # Thread.current is used other places to check user Thread.current[:user_id] = (user_id > 0) ? user_id : session[:user].counter end end
Nginx服务器配置
upstream puma { server 0.0.0.0:3000; } server { listen 9100; server_name localhost; client_max_body_size 100m; gzip on; gzip_comp_level 4; gzip_min_length 1000; gzip_proxied no-cache no-store; gzip_types text/plain application/javascript application/json application/x-javascript text/xml text/css application/xml text/javascript; keepalive_timeout 10; proxy_cache_key "$scheme$request_method$host$request_uri"; server_tokens off; add_header Cache-Control "no-cache, no-store, must-revalidate"; add_header Pragma "no-cache"; add_header Expires 0; root /home/apps/server/current/public; location / { try_files $uri @puma; } location @puma { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_redirect off; proxy_pass http://puma; } error_log /var/log/nginx/error.log; access_log /var/log/nginx/access_log; }
排查方向建议
优先排查Ruby Thread.current的线程安全问题:Puma是多线程服务器,线程会被请求复用。
Thread.current[:user_id]在请求结束后未清理,后续请求复用该线程时会继承前一个用户的ID。直接请求Puma时可能因测试场景线程复用频率低未触发问题,接入Nginx后因keepalive连接导致线程复用更频繁,从而暴露串位问题。建议在Rails的after_action中添加清理逻辑验证:after_action :clear_thread_user_id def clear_thread_user_id Thread.current[:user_id] = nil end验证Nginx缓存配置:当前配置中仅设置了
proxy_cache_key但未配置proxy_cache_path,实际未启用代理缓存,可排除Nginx缓存导致的串位。但需确认Nginx的keepalive_timeout与Puma的线程池配置是否匹配,避免长连接导致线程持续被占用。Rails Session隔离验证:虽然直接请求Puma正常,但仍需添加日志确认每个请求的
session_id和对应的session[:user].counter是否匹配,排除Session未正确隔离的极端情况。添加详细日志定位:在
check_authentication方法中增加日志,记录每个请求的request_id、HTTP_MY_APP_REQUEST_USER值、计算后的user_id、Thread.current.object_id(线程唯一标识)、session[:user].counter,通过日志可直观看到线程是否被复用、user_id是否被正确覆盖,以及串位发生时的线程ID是否重复。
内容的提问来源于stack exchange,提问作者MysteriousNothing

