rainbows.git  about / heads / tags
Unicorn for sleepy apps and slow clients
blob 29f10f0f5951143270458756b141d54f31a63a34 3791 bytes (raw)
$ git show HEAD:t/async_examples/async_app.ru	# shows this blob on the CLI

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
 
#!/usr/bin/env rackup -s thin
#
#  async_app.ru
#  raggi/thin
#
#   A second demo app for async rack + thin app processing!
#   Now using http status code 100 instead.
#
#  Created by James Tucker on 2008-06-17.
#  Copyright 2008 James Tucker <raggi@rubyforge.org>.
#
#--
# Benchmark Results:
#
# raggi@mbk:~$ ab -c 100 -n 500 http://127.0.0.1:3000/
# This is ApacheBench, Version 2.0.40-dev <$Revision: 1.146 $> apache-2.0
# Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
# Copyright 2006 The Apache Software Foundation, http://www.apache.org/
#
# Benchmarking 127.0.0.1 (be patient)
# Completed 100 requests
# Completed 200 requests
# Completed 300 requests
# Completed 400 requests
# Finished 500 requests
#
#
# Server Software:        thin
# Server Hostname:        127.0.0.1
# Server Port:            3000
#
# Document Path:          /
# Document Length:        12 bytes
#
# Concurrency Level:      100
# Time taken for tests:   5.263089 seconds
# Complete requests:      500
# Failed requests:        0
# Write errors:           0
# Total transferred:      47000 bytes
# HTML transferred:       6000 bytes
# Requests per second:    95.00 [#/sec] (mean)
# Time per request:       1052.618 [ms] (mean)
# Time per request:       10.526 [ms] (mean, across all concurrent requests)
# Transfer rate:          8.55 [Kbytes/sec] received
#
# Connection Times (ms)
#               min  mean[+/-sd] median   max
# Connect:        0    3   2.2      3       8
# Processing:  1042 1046   3.1   1046    1053
# Waiting:     1037 1042   3.6   1041    1050
# Total:       1045 1049   3.1   1049    1057
#
# Percentage of the requests served within a certain time (ms)
#   50%   1049
#   66%   1051
#   75%   1053
#   80%   1053
#   90%   1054
#   95%   1054
#   98%   1056
#   99%   1057
#  100%   1057 (longest request)

class DeferrableBody
  include EventMachine::Deferrable

  def call(body)
    body.each do |chunk|
      @body_callback.call(chunk)
    end
  end

  def each &blk
    @body_callback = blk
  end

end

class AsyncApp

  # This is a template async response. N.B. Can't use string for body on 1.9
  AsyncResponse = [-1, {}, []].freeze

  def call(env)

    body = DeferrableBody.new

    # Get the headers out there asap, let the client know we're alive...
    EventMachine::next_tick { env['async.callback'].call [200, {'Content-Type' => 'text/plain'}, body] }

    # Semi-emulate a long db request, instead of a timer, in reality we'd be
    # waiting for the response data. Whilst this happens, other connections
    # can be serviced.
    # This could be any callback based thing though, a deferrable waiting on
    # IO data, a db request, an http request, an smtp send, whatever.
    EventMachine::add_timer(1) {
      body.call ["Woah, async!\n"]

      EventMachine::next_tick {
        # This could actually happen any time, you could spawn off to new
        # threads, pause as a good looking lady walks by, whatever.
        # Just shows off how we can defer chunks of data in the body, you can
        # even call this many times.
        body.call ["Cheers then!"]
        body.succeed
      }
    }

    # throw :async # Still works for supporting non-async frameworks...

    AsyncResponse # May end up in Rack :-)
  end

end

# The additions to env for async.connection and async.callback absolutely
# destroy the speed of the request if Lint is doing it's checks on env.
# It is also important to note that an async response will not pass through
# any further middleware, as the async response notification has been passed
# right up to the webserver, and the callback goes directly there too.
# Middleware could possibly catch :async, and also provide a different
# async.connection and async.callback.

# use Rack::Lint
run AsyncApp.new

git clone https://yhbt.net/rainbows.git