在Ruby中使用WebSocket

声明: 此文翻译自WebSockets in Ruby, 限于本人才疏学浅,其中有翻译不当之处,敬请指出,感激不尽!

在我的主要工作中,需要构建一个一直占用相当大CPU时间片的数据系统。这个任务主要用于在地理编码以及local reference system(本地地理系统?)之间进行编码以及解码。举个例子,这个工作将帮助我们在系统中标记一条对应于街道上某个地点的记录,并且可以知道本地地理位置所对应的坐标。

在第一次的尝试中,我开发了一个用于地理编码的Ruby库以及一个简单的基于Sinatra的web服务。当时我的解决方案表现得还不错,直到后来客户要求对每一个鼠标滑过的事件进行交互。这个需求上的更改让我不得不再一次通过Javascript语言去构建一个同样用于地理编码的基础构件,在之后的一段时间里,一切也都表现得非常好。

而意料之中的是,我们再一次决定在系统中允许每个用户与多个街道关联。现在,每次下载800KB的数据(存储在索引数据库中,用于记录最新的会话信息)尚且可以承受;但是潜在上来说,几个MB的数据将是致命的,甚至软件也有可能在会话的响应之前被使用-而这只是用户所期待的功能之一。

我知道我们必须寻找一个完美的解决方案,并且使一切都是可以管理控制的。在以前,我涉足过WebSocket领域(比如node.js以及Socket.IO)并且知道相关的底层知识。从之前的搜索中,我意识到Ruby在这方面的欠缺,我很快又考虑通过在节点上的Javascript端口来实现需求。这样的想法使我非常激动。

可选方案

第一步是找出可用的方案。以下列举我找到的:

  1. sinatra-websocket
  2. faye-websocket
  3. websocket-rails
  4. tubesock
  5. webmachine-ruby

在上述五种方案中,前三种方案都是基于事件机制的,而tubesock使用了rake hijacking技术,webmachine-ruby通过基于Celluloid::IO的HTTP服务器Reel提供WebSockets。

首先,考虑到我已经使用了Sinatra,于是我试用了sinatra-websocket。但是因为部分原因,我无法将连接方式迁移到WebSocket,所以我决定快速跳过。而且坦白说的话,我还直接跳过了faye-websocket

接下来的两个备选方案遇到了同样的问题:在一个配置较低的Heroku的站点上启动Rails并且加载了整个系统之后,剩下的内存只够几十个客户端同时使用的了。除此之外,Rails的启动时间加上其他用于构建的时间偶尔会让Heroku认为系统中出现异常,结果导致进程在服务正常启动之前就已经被强行退出了。

假如你有所留意,那么你也就知道了,剩下的唯一一个方案,就是webmachine-ruby

webmachine-ruby

配置webmachine-ruby的环境还是相对容易的。为了逐步进行,我首先把原来基于HTTP的服务迁移到它的资源结构。比起Rails以及Sinatra,它更加具有面向对象的味道。它的分发器是易于理解的,我非常喜欢通过visual debugger来摆玩这一切。

迁移到WebSocket上后,一切都变了。我能建议的(包括文档中说明的)就是,你完全可以跳过常规的基础配置,转而提供一个可调用的配置项,比如:

App = Webmachine::Application do |app|
  app.configure do |config|
    config.adapter = :Reel
    config.adapter_options[:websocket_handler] = proc do |websocket|
      websocket << "hello, world"
    end
  end
end

这是相当多的文档所提到的方法。因为它只期望handler支持#call方法,所以你可以写一个你自己的ad-hoc分发器:

class WebsocketHandler
  def call(websocket)
    message = websocket.read
    # do something with the message, call methods on other objects, log stuff, have your fun
  end
end

很多文档并不提及一些套接字编程的基础。假如你发现你的handler被挂起并且不再处理响应,这意味着你需要重新修改程序,但是不需要为此感到烦恼:你只需要实现一个不断从套接字中读取信息并且让Celluloid::IO实现它的非阻塞魔术方法的循环就行了:

class WebsocketHandler
  def call(websocket)
    loop do
      message = websocket.read
      # do something with the message, call methods on other objects, log stuff, have your fun
    end
  end
end

因为非阻塞的特点,你不再需要担心你的CPU占用会一直停留在100%。然而,你会受到节点在CPU使用率以及事件处理方面同样的限制(比如,假如你的程序是CPU密集型的,它便会影响自身的吞吐量)。

幸运的是,我们可以在Ruby中使用线程。我决定通过为每一个客户端指定一个Celluloid Actor来好好利用线程。这个做法允许我去提供一些CPU密集型的操作而不需要妥协于系统中的其他用户。到目前为止,这个方案表现得不错。

疏漏的地方

我的解决方案本来应该考虑非WebSocket的客户端,但事实上我却没有做到。webmachine-ruby通过允许你实现流式API而将此变得简单且没有后顾之忧。我想这将只需要一些JS代码去做相互之间的反馈并且提供一个指向接收者的连接。

这篇文档并未涵盖所有可能在套接字连接时出现的事件(onerror, on close, onopen, onmessage)。你可以在连接套接字的时候看到它们,并且每一个都带着一个块。

这个工具也并不提供一个成熟的结合频道以及信息代理的发布/订阅系统。如果你更多的是需要这方面的工具,其实可以考虑使用faye以及websocket-rails

对于配置 WebSocket,你可以按照以下步骤进行操作: 1. 首先,确保你的服务器环境支持 WebSocketWebSocket 是一种在 Web 浏览器和服务器之间建立持久连接的协议。大多数现代的 Web 服务器和框架都已经内置了 WebSocket 支持。 2. 在服务器端,你需要编写一个 WebSocket 服务器来处理客户端的连接请求和消息传输。对于不同的编程语言和服务器环境,有不同的实现方式。以下是一些常见的编程语言和对应的 WebSocket 库: - Node.js:使用 `ws` 或 `socket.io` 库。 - Python:使用 `websockets` 或 `tornado` 库。 - Java:使用 `javax.websocket` 或 `Spring WebSocket` 模块。 - Ruby使用 `em-websocket` 或 `faye-websocket` 库。 - PHP:使用 `Ratchet` 或 `ReactPHP` 库。 根据你选择的编程语言和服务器环境,选择合适的库或模块,并按照其文档进行安装和配置。 3. 在客户端,你可以使用 JavaScript 来创建一个 WebSocket 连接,并与服务器进行通信。以下是一个简单的示例代码: ```javascript const socket = new WebSocket('ws://example.com/ws'); // 连接成功时触发 socket.onopen = function() { console.log('WebSocket 连接已建立'); }; // 接收到消息时触发 socket.onmessage = function(event) { console.log('收到消息:', event.data); }; // 发送消息 socket.send('Hello, server!'); // 关闭连接 socket.close(); ``` 将 `'ws://example.com/ws'` 替换为你的 WebSocket 服务器地址。在 `onopen`、`onmessage` 和 `onclose` 事件处理程序,你可以自定义你的业务逻辑。 4. 配置服务器的防火墙和代理规则,确保 WebSocket 的连接和消息传输正常工作。具体的配置方式取决于你使用的服务器和网络环境。 请注意,以上仅是 WebSocket 配置的基本步骤,具体实现可能因编程语言、服务器环境和业务需求而有所不同。建议查阅相关文档和教程,以获取更详细的配置指南和示例代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值