Deploy Ruby on Rails app to Heroku while using Action Cable (listening on Puma port)

I have an Action Cable running on my host local environment and in this situation I start the Puma server with a simple file containing

# /bin/bash
bundle exec puma -p 28080 cable/config.ru

      

Once that happens, the puma server starts up and listens on this port 28080 and the port that the local server is running on. Through an online hunt, I couldn't find any places that would tell me a way to mimic this on a hero or a way to get my server to always start on the same port (although I don't know if it would give me the desired result either)

I have a javascript file configured to create a user associated with this port.

//= require cable
//= require_self
//= require_tree .


this.App = {};

App.cable = Cable.createConsumer('ws://127.0.0.1:28080');

      

I guess I will need to change the 127.0.0.1 part and also for the deployment for heroku to work, but I'm not sure. I tried to cut off part 28080 and replace it with ENV ['PORT'] but he said it was an unknown variable even though I have a puma.rb file that has its port set as

... (only part of the file)
rackup      DefaultRackup
port        ENV['PORT']     || 3000
environment ENV['RACK_ENV'] || 'development'
...

      

It seemed to me that ENV ['PORT'] is defined as when checking heroku logs, the puma server will

2015-07-26T06:50:25.278030+00:00 heroku[web.1]: Starting process with command `bin/rails server -p 48875 -e production`
2015-07-26T06:50:30.760680+00:00 app[web.1]: => Booting Puma
2015-07-26T06:50:30.760714+00:00 app[web.1]: => Rails 4.2.1 application starting in production on http://0.0.0.0:48875
2015-07-26T06:50:30.760716+00:00 app[web.1]: => Run `rails server -h` for more startup options
2015-07-26T06:50:30.760718+00:00 app[web.1]: => Ctrl-C to shutdown server
2015-07-26T06:50:31.578843+00:00 app[web.1]: Puma 2.12.2 starting...
2015-07-26T06:50:31.578851+00:00 app[web.1]: * Min threads: 0, max threads: 16
2015-07-26T06:50:31.578859+00:00 app[web.1]: * Environment: production
2015-07-26T06:50:31.578861+00:00 app[web.1]: * Listening on tcp://0.0.0.0:48875

      

We apologize if anything is unclear, and will gladly provide you with more information if nothing comes of it.

EDIT

Here is the updated code at /app/assets/javascripts/channels/index.js.erb

//= require cable
//= require_self
//= require_tree .

this.App = {};

App.cable = Cable.createConsumer('<%= ENV["CABLE_SERVER"] %>');

      

where ENV ["CABLE_SERVER"] points to ws://the-action-cable-server.herokuapp.com

. This variable is stored in the env variables of the env server.

+3


source to share


1 answer


Problem

There are some restrictions for Heroku router : it will only listen on ports 80 and 443. In other words, you can Do not open a fixed port in any Heroku application. In the case of an ActionCable server, there is no way to open a fixed port and receive websocket traffic directed to it. So either Geroku allows things like this (which I doubt) or we use a workaround.

Bypass

As of version 0.0.3 actioncable this is the workaround I used.

The idea is to not have any Heroku app, but two: one for the main rails server and one for the ActionCable server. Both will work on port 80 (or 443).

To run two different servers from the same codebase, you just need to have two Procfiles: one for the rails and one for the action cable. There is a buildpack to handle this. To use it, you also need multi buildpack .

Let's say you have your two Heroku applications called rails

and actioncable

.

Create a file .buildpacks

at the root of your project with this:

https://github.com/cantino/heroku-selectable-procfile
https://github.com/heroku/heroku-buildpack-ruby

      

In rails

and actioncable

create env var BUILDPACK_URL

withhttps://github.com/heroku/heroku-buildpack-multi.git

Now for Procfiles, I want to leave Procfile

to run everything locally with a wizard and create two custom ones: Procfile.rails

and Procfile.actioncable

.

In Procfile.rails

you describe all the required dynos other than the action cable server, for example:

web: bundle exec puma -C config/puma/config.rb
clockwork: bundle exec clockwork lib/clockwork.rb
worker: bundle exec rake jobs:work

      

In Procfile.actioncable

you are describing the server only :

web: bundle exec puma -p $PORT cable/config.ru

      

Note that we are using a speaker web

that will mount the action cable server on port 80 (or 443).



CAUTION You need to move the puma config file config/puma.rb

to your custom location. I choose config/puma/config.rb

. config/puma.rb

is the default location when you start puma without any specific configuration file that we have in Procfile.actioncable

. This can lead to unexpected behavior (see comments below).

Now, on rails

, create env var PROCFILE_PATH

with Procfile.rails

and on actioncable

, create env var PROCFILE_PATH

with Procfile.actioncable

.

Speaking of env vars, it actioncable

needs all the env vars from rails

that needed to run the server actioncable like DATABASE_URL or any credentials.

Now the crucial step: how do we connect rails

and actioncable

together? This is simply done using a Redis instance of the same . rails

will send messages to Redis and actioncable

listen to them and act accordingly. This is why both should target the same Redis instance. If you are using Heroku Redis you just need to install REDIS_URL

with the same value on rails

and actioncable

. Here is the config file for the cable server cable.yml

:

production: &production
  :url: <%= ENV["REDIS_URL"] %>
  :timeout: 1
development: &development
  :url: <%= ENV["REDIS_URL"] %>
  :timeout: 1
  :inline: true
test: *development

      

The last step is to modify the javascript file so that we can control where the Action Action server is located. We can do this using env var.

Change the suffix .js

to, if necessary .js.erb

.

//= require cable
//= require_self
//= require_tree .


this.App = {};

App.cable = Cable.createConsumer('<%= ENV["CABLE_SERVER"] %>');

      

This variable CABLE_SERVER

can now point to ws://127.0.0.1:28080

locally and to rails

, the value will be the url actioncable

.

You are now ready to deploy the code to rails

and actioncable

.

Caveat / Disadvantages

  • on actioncable

    , if you have client authentication you cannot use cookies

    as in examples . You now have two Heroku apps and they cannot share a cookie. I think you can still workaround using cookies for multiple subdomains.
  • you have two deployment targets to support.
  • You have several Procfiles to support.
  • hopefully over time it will be easier to work around :)

Alternatives

  • We can have the same server that handles regular web and websocket traffic using simple middleware. Here 's how.
  • As of rails 5 beta2, the action cable server can now be side-mounted along with your main rail. Having two different servers can make sense: you can scale them individually.

HTH

+8


source







All Articles