action cable 默认用 redis 是因其 pub/sub 机制天然支持广播,避免多进程/服务器下连接状态不一致;生产环境必须显式配置 config.action_cable.adapter = :redis 并确保 redis url 正确,否则消息丢失。

为什么Action Cable默认要用Redis做后端
Action Cable 默认用 Redis 是因为它的 Pub/Sub 机制天然适合广播消息——每个频道(channel)就是一个逻辑通道,publish 发一次,所有 subscribe 到该频道的连接都能实时收到。Rails 不自己维护连接状态同步,而是把“谁在听哪个频道”这个事交给 Redis 去广播,避免了多进程/多服务器下内存不一致的问题。
如果你没配 Redis,Action Cable 在单进程开发模式下会用内存后端(adapter: :test 或 :inline),但一上生产、开多个 Puma worker,就会出现消息只发给部分用户的情况——这不是 bug,是设计使然。
如何配置Action Cable使用Redis
关键不是“能不能用”,而是“配错就收不到消息”。常见漏项有三处:
-
config/environments/production.rb里必须显式设置config.action_cable.adapter = :redis,不能只靠环境变量 -
REDIS_URL环境变量要指向真实 Redis 实例(如redis://localhost:6379/1),不能是redis://127.0.0.1(某些 Docker 网络下解析失败) - 如果用了 Sentinel 或集群,得改用
redis://+ 密码 + db + 参数,例如:redis://:password@sentinel-host:26379/0?sentinels=192.168.1.10:26379,192.168.1.11:26379&role=master
验证是否生效:启动 Rails console,运行 ActionCable.server.pubsub.class,返回 ActiveSupport::Notifications::Instrumenter 就是错了;返回 Redis::PubSub 才对。
stream_from 的 channel 名怎么写才不会冲突
stream_from 后面的字符串就是 Redis 频道名,它直接拼进 PUBLISH 命令,所以必须满足两个条件:唯一性 + 可预测性。
典型错误写法:stream_from "notifications" —— 所有用户挤一个频道,互相收到别人的通知;stream_from "notifications_#{Time.now.to_i}" —— 每次订阅都换频道,broadcast 根本找不到人。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
正确做法是绑定到业务实体上:
- 用户私有频道:
stream_from "notifications:#{current_user.id}" - 房间类频道:
stream_from "chat_room:#{params[:room_id]}" - 带权限校验的频道:
stream_from "project_updates:#{project.id}",然后在subscribed里加return reject if !can?(:view, project)
注意:冒号 : 是惯用分隔符,不是语法要求;但别用空格、中文、斜杠,Redis 频道名只认 ASCII 字符。
ActionCable.server.broadcast 和 redis-cli publish 的关系
它们发的是同一个东西,只是入口不同:ActionCable.server.broadcast 是 Rails 封装后的接口,底层调的就是 redis.publish;而 redis-cli publish 是直连 Redis,绕过 Rails 认证和生命周期管理。
所以调试时可以这样交叉验证:
- 在 Rails console 里执行
ActionCable.server.broadcast "chat_room:123", {msg: "hello"} - 同时在终端跑
redis-cli subscribe "chat_room:123",看是否收到 - 反过来,用
redis-cli publish "chat_room:123" '{"msg":"from cli"}',看页面 WebSocket 是否触发received回调
容易忽略的一点:Rails broadcast 发送的是 JSON 字符串,但 redis-cli publish 发的是裸字符串——如果你发 hello(没引号),前端 JSON.parse 会报错;务必用单引号包住整个 JSON:redis-cli publish "chat_room:123" '{"msg":"ok"}'。
最常被跳过的环节是频道命名与 broadcast 调用之间的映射一致性——写代码时 copy-paste 频道名,一个地方少个冒号、多一个空格,消息就石沉大海。上线前用 redis-cli pubsub channels 看一眼当前活跃频道,比猜日志快得多。










