利用ActionCable实现即时通信
立即解锁
发布时间: 2025-09-06 01:56:12 阅读量: 6 订阅数: 54 AIGC 


Rails现代前端开发指南
### 利用 ActionCable 实现即时通信
#### 1. 现有通信方式的问题与 WebSocket 解决方案
在之前的开发中,我们通过定期轮询服务器来模拟与服务器的实时连接,这种方式存在一定的弊端。一方面,本地发起大量 HTTP 请求会带来额外的开销;另一方面,频繁请求服务器可能会影响性能。
为了解决这些问题,我们可以采用 WebSocket 协议。与传统的 HTTP 连接不同,WebSocket 允许客户端和服务器通过一个持久的连接进行通信。可以将传统 HTTP 连接类比为对讲机,同一时间只能一方进行广播;而 WebSocket 则像电话,双方可以同时交流。WebSocket 实际上是一个双向的数据流,客户端和服务器在接收到新数据时都能执行相应的操作。
#### 2. Rails 中的 ActionCable 库
Rails 提供了名为 ActionCable 的库,它是 WebSocket 的封装,使得在客户端和服务器端使用 WebSocket 变得更加容易。此外,Hotwire 和 Turbo 提供了实用工具,让我们可以将 ActionCable 和 Turbo Streams 结合起来,无需编写新的 JavaScript 代码就能实现 ActionCable 的功能。
#### 3. 安装 ActionCable
ActionCable 的安装已经作为通用 Rails 设置的一部分完成。Ruby gem 已经包含在 Gemfile 中,JavaScript 包也已经在 package.json 文件里。为了能在 ActionCable 客户端代码中使用 TypeScript,我们可以使用以下命令安装 @types/actioncable 包:
```bash
yarn add @types/actioncable
```
ActionCable 的配置可以在 config/cable.yml 文件中查看:
```yaml
development:
adapter: redis
url: redis://localhost:6379/1
test:
adapter: test
production:
adapter: redis
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
channel_prefix: north_by_production
```
ActionCable 通常在后台运行,与用户的 HTTP 请求分离,这样 ActionCable 的广播不会延迟服务器的响应。Hotwire 改变了 ActionCable 的默认设置,使得开发环境使用 Redis 来管理后台处理。
在开发环境中安装 Redis 最简单的方法是使用 Docker。如果你熟悉 Docker,可以运行以下命令:
```bash
docker run --rm -it -p 6379:6379 redis:latest
```
如果你不想使用 Docker 安装 Redis,可以将开发环境配置修改为 `adapter: redis` 并删除 `url` 行。`async` 适配器可以在不使用后台队列的情况下模拟后台任务,这在开发环境中是可行的,但生产环境默认仍需要安装 Redis 来将 ActionCable 数据存储在内存中。
#### 4. Turbo Streams 与 ActionCable
`turbo-rails` 宝石(gem)提供了一组用于管理 ActionCable 连接的辅助方法。这些方法允许 Rails 自动通过 ActionCable 连接发送 Turbo Stream HTML,并在客户端接收到消息时执行 Turbo Stream 操作。
这些辅助方法大致可以实现以下三个功能:
- 通过视图中的辅助方法将视图连接到 ActionCable 频道。
- 作为控制器请求响应的一部分,向 ActionCable 频道广播 Turbo Stream。
- 当 ActiveRecord 对象被修改时,自动作为回调向 ActionCable 频道广播 Turbo Stream。
`turbo-rails` 提供的辅助方法在默认行为的假设和可定制性方面有所不同。你可以选择直接发送任意内容到由 Turbo Streams 管理的 ActionCable 频道,也可以让 `turbo-rails` 将内容包装在 Turbo Stream HTML 结构中,还可以利用默认设置,将模型的默认部分发送到具有默认名称的流中。具体选择哪种方式取决于你的需求与 Rails 约定的契合程度,以及你在 ActiveRecord 模型中定义频道行为的熟练程度。
#### 5. 准备视图
ActionCable 在同步多个浏览器会话方面非常有用,无论是同一用户的不同设备,还是多个用户查看相同的信息。在这部分,我们要实现的是在一个浏览器中将音乐会标记为收藏后,其他打开的该用户的浏览器能立即更新。
在开始之前,我们需要对视图进行一些重构,以便更轻松地集成 `turbo-rails` 的 ActionCable 辅助方法。当前存在一个常见问题,很多应用程序都会遇到类似的情况。
现有的代码中,当我们点击“设为收藏”按钮将音乐会标记为收藏时,会向服务器发送一个表单请求,服务器返回包含 Turbo Stream 的 HTML 响应,客户端的 Turbo 会根据这个响应更新 DOM。
在即将实现的版本中,我们仍然点击“设为收藏”按钮并发送 HTML 请求,但在服务器端创建新的收藏对象后,服务器会将 Turbo Stream 消息排队,并最终通过 ActionCable 发送给客户端,然后 Turbo 再次根据 Turbo 流更新 DOM。
需要注意的是,ActionCable 响应发生在常规的 Rails 请求/响应周期之外,因此 ActionCable 广播无法访问基于请求的全局变量或会话变量。特别是,我们用于显示日程安排的许多视图部分依赖于 `current_user` 全局变量来判断音乐会是否为收藏以及是否显示编辑按钮。
我们需要修改代码,让 ActionCable 调用的任何部分都接收用户参数,而不是在部分内部调用 `current_user`。例如,`app/views/concert/_concert.html.erb` 文件中使用了四次 `current_user`,将这些使用替换为 `user` 并不困难,但我们还需要修改对该视图的调用。具体来说,`ConcertController` 需要更改其引用:
```ruby
# app/controllers/concerts_controller.rb
def show
if params[:inline]
render(@concert, user: current_user)
end
end
def update
respond_to do |format|
if @concert.update(concert_params)
format.html do
redirect_to(@concert, user: current_user)
end
format.json { render(:show, status: :ok, location: @concert) }
else
format.html { render(:edit) }
format.json do
render(json: @concert.errors, status: :unprocessable_entity)
end
end
end
end
```
类似地,`app/views/schedule_day/_schedule_day.html.erb` 文件需要在调用渲染单个音乐会时添加 `user: current_user`,`app/favorites/create.turbo_stream.erb` 和 `app/favorites/destroy.turbo_stream.erb` 文件也需要进行相应的修改。
#### 6. 使用 Turbo Streams 进行广播
视图代码重构完成后,就可以开始进行广播了。ActionCable 通过频道进行工作。客户端指定要订阅的频道名称,服务器向该频道发送消息,Rails 会将这些消息通过正确的 WebSocket 发送出去。使用 Turbo 时,`turbo-rails` 宝石通过 `Turbo::StreamsChannel` 类和许多访问该类的辅助方法完成了很多幕后工作。
在客户端,我们需要注册视图以接收某
0
0
复制全文
相关推荐










