Using Ruby 1.9 Fibers to drive your apps (uWSGI >=0.9.7-dev)
In the uWSGI Rack plugin a LoopEngine for ruby 1.9 fibers is available.
Fibers are a form of coroutine, useful for implementing cooperative multitasking in your apps.
Quickstart
First of all we have to compile the uWSGI server core
python uwsgiconfig --build core
Then we will build the rack plugin
python uwsgiconfig --plugin plugins/rack core
(do not remove the latest 'core' arg, otherwise the world will be destroyed)
If your ruby 1.9 installation is not on the system path you can force a specific ruby interpreter with:
UWSGICONFIG_RUBYPATH=/opt/ruby/bin/ruby python uwsgiconfig.py --plugin plugins/rack core
Finally compile the fiber plugin
python uwsgiconfig --plugin plugins/fiber core
In debian sid you can use something like this:
python uwsgiconfig.py --build core UWSGICONFIG_RUBYPATH="ruby1.9.1" python uwsgiconfig.py --plugin plugins/rack core UWSGICONFIG_RUBYPATH="ruby1.9.1" python uwsgiconfig.py --plugin plugins/fiber core
Now we write a rack app (call it fibers.ru) that will print the first 10 numbers but suspends its execution after every iteration to allow other requests to be accepted
require 'fiber'
class SuspendingBody
def each
for i in 1..10
yield "number: #{i}\n"
Fiber.yield
end
end
end
class RackFoo
def call(env)
[200, { 'Content-Type' => 'text/plain'}, SuspendingBody.new]
end
end
run RackFoo.new
Now run uWSGI with 20 async cores (or use a higher value).
This time we will a use a yaml configuration file (call it fibers.yml)
uwsgi: plugins: rack, fiber socket: 127.0.0.1:3031 rack: fibers.ru post-buffering: 4096 async: 20 loop: fiber
Now run uWSGI
./uwsgi fibers.yml
Sleeping and non-blocking-io
You can use the uWSGI api to combien fibers with non-blocking io and sleeping
For sleep without blocking you use UWSGI.async_sleep(secs) function
require 'fiber'
class SuspendingBody
def each
for i in 1..10
yield "number: #{i}\n"
UWSGI.async_sleep(3)
Fiber.yield
end
end
end
class RackFoo
def call(env)
[200, { 'Content-Type' => 'text/plain'}, SuspendingBody.new]
end
end
run RackFoo.new
For non-blocking-io you can use the async_connect, wait_fd_read, wait_fd_write
The following example will async sleep 3 times then will connect to a http server and returns the output. (it is a bit complex, but should cover 99% of the uses)
require 'fiber'
class SuspendingBody
def each
for i in 1..3
yield "number: #{i}\n"
UWSGI.async_sleep(1)
puts "sleep..."
Fiber.yield
end
fd = UWSGI.async_connect("81.174.68.52:80")
UWSGI.wait_fd_write(fd, 3)
Fiber.yield
io = IO.new(fd)
puts "connected"
io.syswrite("GET /uwsgi/export/1081%3A362d695b2f25/plugins/fiber/fiber.c HTTP/1.0\r\n")
io.syswrite("Host: projects.unbit.it\r\n")
io.syswrite("\r\n")
UWSGI.wait_fd_read(fd, 3)
Fiber.yield
puts "data available"
begin
while body = io.sysread(fd)
yield body
Fiber.yield
end
rescue
end
io.close
end
end
class RackFoo
def call(env)
[200, { 'Content-Type' => 'text/plain'}, SuspendingBody.new]
end
end
run RackFoo.new
Framework support
Sinatra has been tested with fibers. You can stream response in this way (simply return an object that respond to "each" method):
require 'fiber'
require 'sinatra'
get '/hi' do
class Response
def each
for i in 1..10
yield "ciao<br/>"
Fiber.yield
end
end
end
Response.new
end
run Sinatra::Application
REMEMBER !!!
Coroutines are for increasing "concurrency" of your app, they do not allow you to execute code faster !!!
