Code is at http://github.com/macournoyer/meshu
Category Archives: thin
Staying Alive with Thin!
Thin was the first Ruby server to be able to connect to UNIX domain sockets, giving you a little more speed, so you could spend less time browsing and more time dancing and eating ice creams with the people you love. But only Nginx (that I know of) supports UNIX domain sockets. Non-Nginx users might like to have time to dance and eat ice creams with the people they love too!
So that’s why Thin new release (0.7.0 codename Spherical Cow) supports persistent connections (aka Keep-Alive).
Apache & mod_proxy
Under Apache, if a client requests a persistent connection, the connection to the backend server (through mod_proxy) will also be persistent. That means the time spent opening the connection is saved for subsequent requests because all requests will be sent through the same connection. Even cooler is that you have nothing to do to set that up, unless you’ve turned Keep-Alive off.
I ran some benchmarks and I got an average gain of 200 req/sec with Keep-Alive on.
Sadly Nginx doesn’t support persistent connections to backends yet, but it seems to be a highly requested feature, and Igor Syseov (author of Nginx) said he’s working on it several times on the mailing list. Imagine that: UNIX sockets + persistent connections, oooh man!
Hey we develop too, sometimes!
Of course you’ll take advantage of that feature when it’s just you, Thin and your browser. And we can also benchmark it, just for fun, to get numbers that are sure to give you goose bumps: 7800 req/sec is just, ooooh, aaahh, hummm, yeah, that was good!
Swiftiply
If you’re looking for even more speed. Thin can now be used as a Swiftiply client. And it’s very easy to use:
thin start --servers 3 --port 8000 --swiftiply
Just add the --swiftiply
option. This also means that any Rack adapter can be run through Swiftiply (using the --rackup
option) including Ramaze, Camping, Merb, YourCrazyLittleFrameworkThatIsSoooBetterThenAllTheOtherAndThatSupportRack etc.
Speed with control
But what is speed with no control, right?
The main reason why Mongrel couldn’t support persistent connections was because of Ruby 1024 file/socket descriptors limitation. If you don’t close the connection (keep them alive) it’s one less descriptor you can use to process another connection or open a file.
Although EventMachine doesn’t have an infinite number of file descriptors it was reported to handle more then 20 000 concurrent connections.
You can now tune the number of connections a Thin server can handle.
--max-conns
: This sets the maximum number of concurrent connections your Thin server can handle. Setting higher then 1024 might require superuser privileges on some system.
--max-persistent-conns
: This sets the maximum number of persistent connections your Thin server can handle at the same time. If resource usage is important, you might want to turn that down. You can turn Keep-Alive support off by setting to 0.
Get it!
Spherical Cow also comes with a couple bug fixes and tweak.
As usual, you can get the latest version from RubyForge:
sudo gem install thin
If you have any question, join the Google Group or the #thin channel on freenode.
I hope you like it!
MoR7 Presentation
Here’s the code and slides of my presentation : http://github.com/macournoyer/mor7/
Hope you liked it!
Filed under montreal, rails, StandoutJobs, thin
Presenting Thin at Montreal on Rails
If you’re in Montreal next Tuesday, be sure to come to the next Montreal on Rails. I’ll be presenting Thin with a fanfare, dancers and pizza.
RSVP if not already done and do it fast, places are limited.
I’ll present some new features in the upcoming Spherical Cow release and recode Thin like I did at DempCampCUSEC, but this time, with one hand tied being my back and someone yelling at me in Russian. All this in 2 minutes (I don’t know yet what I’ll do with the remaining time).
Filed under conference, montreal, ruby, thin
The Flexible Thin Anorexic Gymnast that Democratized Deployment
Thin is fast, I don’t think I need to prove that again. But what I’d like to showcase now is Thin extensibility. Most of it is due to Thin being based on Rack. It’s also why lots of framework are supporting Thin already.
Can Thin replace all images with LOLCAT pics on my site when it’s my birthday of a leap year, plz, plz, plz? Cause it should!
Yeah, you’re not the first one to ask.
config.ru file
require 'open-uri' require 'hpricot' class LolCater def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) if iz_ma_burdae? && leap_year? doc = Hpricot(body) doc.search('img').set('src', lolcat_pic_url) body = doc.to_html end [status, headers, body] end private def iz_ma_burdae? Date.today.month == 11 && Date.today.day == 7 end def leap_year? Date.today.leap? end def lolcat_pic_url doc = Hpricot(open('http://icanhascheezburger.com/?random#top')) pic = (doc/'div.snap_preview img').first.attributes['src'] end end use LolCater run Rack::Adapter::Rails.new(:root => '/path/to/my/app')That’s called a Rack middleware. It must have a
call
method and receive a Rack app as the first argument ofnew
. You then tell Thin to use this middleware when running your Rails application. Save the file asconfig.ru
. .ru is for Rackup config file.You can now launch your application with the
thin
script:thin start --rackup config.ruWai-wai-wait that means I can built my own framework in like 30 LOC and use all of Thin goodness?
Right! You can start a cluster of your lolcat image replacer app like you would for a Rails app, but specify the
--rackup
option, which tell Thin to load your application from this file instead to go with the default Rails adapter.thin start --rackup config.ru --servers 3In fact, here’s a lil’ framework I built in 35 LOC:
# Start w/ thin start -r invisible.rb require 'tenjin' module ::Invisible class Adapter def initialize @template = Tenjin::Engine.new(:postfix=>'.rbhtml', :layout=>'../layout.rbhtml', :path=>'home') @file = Rack::File.new('public') end def call(env) path = env['PATH_INFO'] if path.include?('.') @file.call(env) else _, controller, action = env['PATH_INFO'].split('/') Invisible.const_get("#{(controller || 'home').capitalize}Controller").new(@template, env).call(action || 'index') end end end class Controller def initialize(template, env) @template, @status, @env, @headers = template, 200, env, {'Content-Type' => 'text/html'} end def call(action) send(action) render(action) unless @body [@status, @headers.merge('Content-Length'=>@body.size.to_s), [@body]] end protected def render(action=nil) @body = @template.render(action.to_sym, instance_variables.inject({}) {|h,v| h[v[1..-1]] = instance_variable_get(v); h}) end end end require 'app/controllers' run Invisible::Adapter.newFull code on my github repo: http://github.com/macournoyer/invisible/.
And you know I can’t help myself but benchmark it:
merb-core: 1865.19 req/sec
invisible: 2428.17 req/sec(Disclamer: it’s just for fun, don’t use that framework ever or I will come in your house when you sleep and steel all your left foot socks).
Democratizing deployment
I don’t know if you get what this means? It means, deploying a Rails or Merb, Ramaze, etc, app is just a matter of writing a simple Rack config file and playing w/ the –rackup option. It’s all the same script and tools now!
For example, there’s a Ramaze Rack config file in example/ramaze.ru, so to deploy your Ramaze application you would.
thin start --servers 3 --rackup ramaze.ruOr create a config file and install it as a runlevel script:
thin config --rackup ramaze.ru -C myapp.yml edit myapp.yml sudo thin install # Installs the runlevel script mv myapp.yml /etc/thin/myapp.ymlOr if you prefer to monitor your clusters with God, check out the sample God config file that is a drop-in replacement for Thin’s runlevel script.
Happy hacking & deploying!
Get intimate with your load balancer tonight!
The Thin Cheesecake release is out (v0.6.1)!
sudo gem install thin
That tasty and sweet Cheesecake release comes with some new sweet topping: config file support, uses less memory, some speed tweaks, but that’s nothing new regarding what we all know and use from other web servers. Nothing very innovative, breath taking, crazy, revolutionary or surprising you say.
You’re right!
Almost …
There’s another feature that as never been seen amongst Ruby web servers (indeed, haven’t found any that does that). But first, lets explore the typical deployment of a Rails app.
Let’s deploy Rails shall we?
Typically you’d deploy your rails application using mongrel like this:
mongrel_rails cluster::configure -C config/mongrel_cluster.yml --servers 3 --chdir /var/www/app/current ... mongrel_rails cluster::start -C config/mongrel_cluster.yml
Then on your web server / load balancer configuration file (Nginx in this case):
nginx.conf
upstream backend { server 127.0.0.1:5000; server 127.0.0.1:5001; server 127.0.0.1:5002; }
Now with Thin, it’s the same
thin config -C config/thin.yml --servers 3 --port 5000 --chdir ... thin start -C config/thin.yml
That will start 3 servers running on port 5000, 5001 and 5002.
And your web server configuration stays the same.
Really, between you and me, is Thin really really faster?
Simple “hello world” app, running one server
And uses less memory too:
Mesured after running: ab -n5000 -c3
What about that new, crazy, amazing feature you mentioned?
Ever wanted to keep closer to you web server? Sometimes connecting through a TCP port on 127.0.0.1 feels a bit … disconnected. What if we’d get closer to it, get intimate with it, share some feelings, exchange toothbrush?
Introducing UNIX socket connections
When using more then one server (a cluster) behind a load balancer, we need to connect those servers to the load balancer through dedicated ports like in the previous “Let’s deploy Rails” section. But this connect the 2 using a TCP socket which means, all requests have to go though the network interface stuff twice! Once from the client (browser) to the load balancer / web server and again to the backend server.
There’s a better approach to this. Some load balancer (like Nginx) support connecting to a UNIX domain socket.
nginx.conf
upstream backend { server unix:/tmp/thin.0.sock; server unix:/tmp/thin.1.sock; server unix:/tmp/thin.2.sock; }
Then start your Thin servers, like this:
thin start --server 3 --socket /tmp/thin.sock
And yes, it is faster:
3 servers running a simple Rails application, behind Nginx
DemoCampCUSEC follow-up
Yesterday I presented Thin at DemoCampCUSEC. Things went pretty well and the other presentations were very good too.
My demo was basically re-coding a simplified version of Thin live, here’s the final result:
%w(rubygems eventmachine thin thin_parser rack).each { |f| require f } class Connection < EventMachine::Connection attr_accessor :app def initialize @parser = Thin::HttpParser.new @data = '' @nparsed = 0 @env = {} end def receive_data(data) @data << data @nparsed = @parser.execute(@env, @data, @nparsed) process if @parser.finished? end def process status, headers, body = @app.call(@env) body_output = '' body.each { |l| body_output << l } send_data "HTTP/1.1 #{status} OK\r\n" + headers.inject('') { |h, (k,v)| h += "#k: #v\r\n" } + "\r\n" + body_output close_connection_after_writing end end welcome_app = proc do |env| [ 200, # Status {'Content-Type' => 'text/html'}, # Headers [ '<html><body>', '<h1>Welcome</h1>', '<p>Welcome to my server!</p>', # Body '<p><a href="/rails">My Rails app!</a></p>', '</body></html>' ] ] end rails_app = Rack::Adapter::Rails.new(:root => '/Users/marc/projects/refactormycode', :prefix => '/rails') app = Rack::URLMap.new('/' => welcome_app, '/rails' => rails_app) EventMachine.run do EventMachine.start_server '0.0.0.0', 3000, Connection do |con| con.app = app end endHope you enjoyed it!
Filed under conference, montreal, ruby, thin