I previously published this article on my personal blog early 2013, but thought I'd share it today with our Codeship readers. Enjoy!
I've been using Rails 4 (beta) a lot recently. In a previous post we looked at how ActionController::Live can be used with Server-Sent Events, but the problem with that is that there's no way for the client to communicate back to the web server. Enter: WebSockets.
The main issue with implementing WebSockets is that they have to keep their connections open for a long period of time, and when you're only running a small cluster of Rails servers, it eats up potential connections fast. That's why I was excited to hear about two important developments: concurrency in Rails 4 and Rack Hijack.
Concurrency in Rails 4
Rails 4 is now full concurrent, which means that there is no full-stack lock on a request. That means that if you use a concurrent server like Puma you can handle many requests at a time with a single process.
Even better, you can use ruby Threads inside your Rails app. That's how ActionController::Live works (for streaming).
What this means for us is that we can use Threads to hold websocket connections open without bogging down our server.
Also, this means that our solution does not use Eventmachine, nor does it implement a reactor in any way. It's concurrent.
Rack Hijack
Rack Hijack came with Rack 1.5.0 which was released in January 2013. Rack hijack allows you to access the underlying socket of a Rack connection in order to bidirectionally communicate with the client. Since Rails is built on Rack we can grab a handle to the client socket right from a Rails controller.
Tubesock
Tubesock is the gem that I made to encapsulate this functionality. It's very small and new and untested so caveat emptor and all that. At its core it provides a module for Rails controllers and a wrapper method to hijack the rack connection. Then it wraps the ruby gem websocket to handle WebSocket handshakes and frames. Here's an example of using it in a controller:
class ChatController < ApplicationController include Tubesock::Hijack def chat hijack do |tubesock| tubesock.onopen do tubesock.send_data "Hello, friend" end tubesock.onmessage do |data| tubesock.send_data message: "You said: #{data}" end end end end
Right inside the controller action we can hijack the connection and then use some blocks to send information.
You can check out the Tubesock gem on Github for more information.
Also, there is an example chat application you can run and play with.
Happy hacking!
Discuss this article on HackerNews: https://news.ycombinator.com/item?id=9335584