Docker容器中Nginx用redis_pass代理WebSocket连接Redis故障问询
核心问题解答
1. redis_pass无法代理WebSocket的原因
ngx_http_redis模块是用于将HTTP请求直接转换为Redis GET命令的HTTP网关模块,本身不支持WebSocket协议的握手、帧转发逻辑,也不能识别Redis原生的RESP通信协议,你这个用法完全不符合模块的设计场景,不存在所谓的已知问题,是选型错误。你使用的0.3.9是该模块的最新稳定版本,不存在版本过旧的问题。
2. 业务链路的错误理解
Django Channels的标准通信链路为:浏览器 <--WebSocket--> Nginx <--WebSocket--> Django ASGI服务 <--RESP协议--> Redis通道层
Redis是Django服务内部用来做跨实例消息同步的组件,不需要直接对外暴露WebSocket端口,你完全不需要把浏览器的WebSocket流量转发到Redis,直接转发给Django的ASGI服务即可。
3. 正确的配置修改
将nginx.conf中location /chat_ws段的配置替换为标准WebSocket代理配置:
location /chat_ws { proxy_pass http://asgi; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 86400; }
你当前配置中的load_module加载ngx_http_redis模块的配置、以及redis_pass相关的配置都可以直接删除。
4. 模块日志排查方法
如果需要调试ngx_http_redis模块的日志,可以在nginx.conf的http段添加以下配置开启debug级别的错误日志:
error_log /dev/stdout debug;
你当前场景不需要使用该模块,无需做日志排查。
5. 额外优化建议
你当前将Nginx、Redis、Django三个服务打包在同一个容器中,不符合Docker单进程的最佳实践,建议拆分为三个独立的服务,通过docker-compose的服务名互相访问,Django的CHANNEL_LAYERS配置中Redis地址直接填写Redis服务的服务名即可,无需走127.0.0.1。
内容的提问来源于stack exchange,提问作者T. J.

