A simple demonstration of streaming Redis pub/sub data

Véase:
  1. A simple demonstration of streaming Redis pub/sub data over HTTP via Sinatra's streaming capabilities.
  2. A simple demonstration of streaming Redis pub/sub data over HTTP via Sinatra's streaming capabilities. forked version crguezl
  3. Véa el capítulo Redis y Sinatra 43
  4. redis gem en GitHub
  5. redis documentación de la gema
  6. Redis Pub/Sub
  7. Redis::Subscription:
  8. Heroku addon Redis To Go

web.rb

[~/sinatra/sinatra-streaming/sinatra-streaming-example(master)]$ cat web.rb
require 'redis'
require 'sinatra'

configure do
  redis_url = ENV["REDISTOGO_URL"] || "redis://localhost:6379"
  uri = URI.parse(redis_url)
  set :redis, Redis.new(:host => uri.host, :port => uri.port, :password => uri.password)
end

get '/' do
  "<pre>curl -v https://sinatra-streaming-example.herokuapp.com/stream</pre>"
end

get '/stream' do
  puts "connection made"

  stream do |out|
    settings.redis.subscribe 'time' do |on|
      on.message do |channel, message|
        out << "#{message}\n"
      end
    end
  end
end
Redis is a key-value store; it supports lists, hashes, sets, and ordered sets.

El código

  redis_url = ENV["REDISTOGO_URL"] || "redis://localhost:6379"
decide si estamos en Heroku o no. Si estamos en Heroku deberemos haber instalado el Heroku addon Redis To Go.

La llamada:

  set :redis, Redis.new(:host => uri.host, :port => uri.port, :password => uri.password)
conecta con el servidor Redis.

The Redis class exports methods that are named identical to the commands they execute. The arguments these methods accept are often identical to the arguments specified on the Redis website.

In Redis, SUBSCRIBE, UNSUBSCRIBE and PUBLISH implement the Publish/Subscribe messaging paradigm where

  1. In the Publish/Subscribe pattern, senders (publishers) are not programmed to send their messages to specific receivers (subscribers).
  2. Rather, published messages are characterized into channels, without knowledge of what (if any) subscribers there may be.
  3. Subscribers express interest in one or more channels, and only receive messages that are of interest, without knowledge of what (if any) publishers there are.
  4. This decoupling of publishers and subscribers can allow for greater scalability and a more dynamic network topology.
For instance in order to subscribe to channels foo and bar the client issues a SUBSCRIBE providing the names of the channels:
SUBSCRIBE foo bar

Messages sent by other clients to these channels will be pushed by Redis to all the subscribed clients.

A client subscribed to one or more channels should not issue commands, although it can subscribe and unsubscribe to and from other channels.

The reply of the SUBSCRIBE and UNSUBSCRIBE operations are sent in the form of messages, so that the client can just read a coherent stream of messages where the first element indicates the type of message.

El objeto on pasado al bloque está en la clase Redis::Subscription:

    settings.redis.subscribe 'time' do |on|
      on.message do |channel, message|
        out << "#{message}\n"
      end
el objeto on dispone del método message que establece una callback que será ejecutada cada vez que se produzca un mensaje.

worker.rb

[~/sinatra/sinatra-streaming/sinatra-streaming-example(master)]$ cat worker.rb 
require 'uri'
require 'redis'

redis_url = ENV["REDISTOGO_URL"] || "redis://localhost:6379"
uri = URI.parse(redis_url)
r = Redis.new(:host => uri.host, :port => uri.port, :password => uri.password)

while true do
  puts "publishing..."
  r.publish "time", Time.now.utc
  sleep 1  
end

Procfile

[~/sinatra/sinatra-streaming/sinatra-streaming-example(master)]$ cat Procfile 
web: bundle exec ruby web.rb -p $PORT
worker: bundle exec ruby worker.rb
The web process type, describes to Heroku how to start the web application server.

An interesting thing to mention here is that Heroku only auto-launches the web process in your Procfile when deploying, whereas foreman launches everything.

Gemfile

[~/sinatra/sinatra-streaming/sinatra-streaming-example(master)]$ cat Gemfile
source 'https://rubygems.org'

gem 'foreman'
gem 'redis'
gem 'sinatra'
gem 'thin'

Ejecución: Arranca el Servidor Redis

[~/sinatra/sinatra-streaming/sinatra-streaming-example(master)]$ redis-server 
[5604] 04 Dec 10:18:58.126 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
[5604] 04 Dec 10:18:58.129 * Max number of open files set to 10032
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 2.6.14 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in stand alone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 5604
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

[5604] 04 Dec 10:18:58.129 # Server started, Redis version 2.6.14
[5604] 04 Dec 10:18:58.129 * The server is now ready to accept connections on port 6379

Ejecución 2: Arranca la aplicación Sinatra y el Worker

Obsérvese como bundle exec foreman start arranca tanto a worker como a web:

[~/sinatra/sinatra-streaming/sinatra-streaming-example(master)]$ bundle exec foreman start
10:19:04 web.1     | started with pid 5606
10:19:04 worker.1  | started with pid 5607
10:19:05 worker.1  | publishing...
10:19:06 web.1     | == Sinatra/1.3.0 has taken the stage on 5000 for development with backup from Thin
10:19:06 web.1     | >> Thin web server (v1.2.11 codename Bat-Shit Crazy)
10:19:06 web.1     | >> Maximum connections set to 1024
10:19:06 web.1     | >> Listening on 0.0.0.0:5000, CTRL+C to stop
10:19:06 worker.1  | publishing...
10:19:07 worker.1  | publishing...
10:19:08 worker.1  | publishing...
..................................
10:19:37 worker.1  | publishing...
10:19:38 web.1     | connection made
10:19:39 worker.1  | publishing...
..................................

Ejecución 3: Arranca un Cliente

[~/sinatra/sinatra-streaming/sinatra-streaming-example(master)]$ curl -v http://localhost:5000/stream
* Adding handle: conn: 0x7fc71280aa00
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x7fc71280aa00) send_pipe: 1, recv_pipe: 0
* About to connect() to localhost port 5000 (#0)
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET /stream HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:5000
> Accept: */*
> 
< HTTP/1.1 200 OK
< X-Frame-Options: sameorigin
< X-XSS-Protection: 1; mode=block
< Content-Type: text/html;charset=utf-8
< Connection: close
* Server thin 1.2.11 codename Bat-Shit Crazy is not blacklisted
< Server: thin 1.2.11 codename Bat-Shit Crazy
< 
2013-12-04 13:24:13 UTC
2013-12-04 13:24:14 UTC
2013-12-04 13:24:15 UTC
2013-12-04 13:24:16 UTC
2013-12-04 13:24:17 UTC
.......................



Subsecciones
Casiano Rodriguez León 2015-01-07