1 简介
Action Cable 将 WebSocket 与 Rails 应用的其余部分无缝集成。有了 Action Cable,我们就可以用 Ruby 语言,以 Rails 风格实现实时功能,并且保持高性能和可扩展性。Action Cable 为此提供了全栈支持,包括客户端 JavaScript 框架和服务器端 Ruby 框架。同时,我们也能够通过 Action Cable 访问使用 Active Record 或其他 ORM 编写的所有模型。
2 Pub/Sub 是什么
Pub/Sub,也就是发布/订阅,是指在消息队列中,信息发送者(发布者)把数据发送给某一类接收者(订阅者),而不必单独指定接收者。Action Cable 通过发布/订阅的方式在服务器和多个客户端之间通信。
3 发布与订阅的实现
3.1 连接
连接是客户端-服务器通信的基础。每当服务器接受一个 WebSocket,就会实例化一个连接对象。所有频道订阅(channel subscription)都是在继承连接对象的基础上创建的。连接本身并不处理身份验证和授权之外的任何应用逻辑。WebSocket 连接的客户端被称为连接用户(connection consumer)。每当用户新打开一个浏览器标签、窗口或设备,对应地都会新建一个用户-连接对(consumer-connection pair)。
==连接是 ApplicationCable::Connection 类的实例。对连接的授权就是在这个类中完成的,对于能够识别的用户,才会继续建立连接。==
示例(当前门店连接的身份信息识别):
# app/channel/application_cable/connection.rb |
全栈可以通过cookie方法,前后端分离可以使用request.params方法,所以此次连接的可以使用[EXAMPLEHOST]/cable?hotel_id=1这样的格式。
3.2 频道(channel)
和常规 MVC 中的控制器类似,频道用于封装逻辑工作单元。默认情况下,Rails 会把 ApplicationCable::Channel 类作为频道的父类,用于封装频道之间共享的逻辑。
3.2.1 父频道设置
# app/channels/application_cable/channel.rb |
==以后的新频道都是继承于这个类,可以在这个类里定义通用方法。==
3.2.2 订单频道设置
使用下列命令新建一个channel:(OrderChannel)
rails g channel order |
该频道使用了根据对象来订阅频道的方法,因此,该频道建立在已经存在hotels表的前提,在rails console可以使用下列命令检查。
# rails console |
这里涉及到两个方法(stream_from,stream_for)见下面代码。
class OrderChannel < ApplicationCable::Channel |
可以看出stream_from的参数是一个字符串,可以用于一个固定的频道的订阅,stream_for的参数可以是一个具体的关联对象。我们主要是使用这个stream_for方法去做与门店的关联。这样可以订阅 Z2lkOi8vVGVzdEFwcC9Qb3N0LzE 这样的广播。==所以这里推荐用stream_for。==
3.2.3 频道的订阅
这里以react为例子讲解订阅的过程
step1
在react项目中引入actioncable,actioncable-client-react两个npm包。
npm方式:
npm i --save actioncable actioncable-client-react |
yarn方式:
yarn add actioncable actioncable-client-react |
step2
在代码中引入ActionCable组件
... |
step3
使用ActionCable组件实现消息订阅(actioncable.createConsumer)及消息接收方法(onReceived)
const cable = actioncable.createConsumer(`ws://192.168.1.43:3000/cable?hotel_id=${this.props.hotelInfo.sourceId}`) |
这样就完成了前端的订阅了
3.2.4 信息的发布
假设想给hotel_id为1的门店推送新订单,因为这个项目中使用了stream_for一个对象的方式,因此可以使用下列的方法去推送信息:
new_order = Order.last |
这样的话,前面订阅了hotel的门店就可以收到我们发出的信息了,在react项目中的
ActionCableProvider组件中的onReceived方法中就可以得到后端发布的消息了。
==tips:这里最好使用Job去调用,不然在消息过于密集的时候会出现接收不到推送的问题。==
4 本地调试(开发环境)
step1
安装nginx
step2
在rails项目的 config/environments/development.rb
# 允许任何源访问 |
config/application.rb
# 访问/cable的http请求指向action_cable |
step3
这步是为了在局域网中调试用的,若是没有这种要求的话可以不改,本地的前端项目只需要订阅0.0.0.0加上启动项目的端口就行了。
nginx.conf
... |
这样所有通过ip访问你本地3000端口的请求都能转发到你的localhost:3000了,reload一遍nginx,就可以在同一个局域网内访问你本地的服务了。