Falcon: A high-performance web server for Ruby, supporting HTTP/2 and HTTPS
github.com
github.com
Samuel is one of Ruby's best. He wrote RubyDNS as well, which was one of the best implementations of Celluloid, the asynchrony / concurrency / parallelism / distributed computing library written in Ruby, which has since come to EOL or hit a ceiling as trends shifted.
I am happy to see Samuel ( ioquatix ) continue his momentum personally, and contribute so much.
I program almost exclusively in Ruby (because it's the best, right ? :) ) and I'm always happy to see experts like you keep the Ruby flag flying by actively expanding the language.
Great work. Can't wait to try out Falcon.
I think it was his Fibre[1] works that caught my attention. No idea why it hasn't committed yet.
[1]https://bugs.ruby-lang.org/issues/14739
Edit: Wow He is now a member of Ruby Core !!!
# In this example, I use blueprint plugin
@bp.route('/', methods=['PUT'])
def create_resource():
return
vs class Resource(...):
def on_put(self, request, response):
return
Mirror: https://pastebin.com/50W8TJV2Flask's hooking logic [1] is nice but I much prefer Falcon's middleware logic [2]. This then ties into the fact that Flask's design requires working with `g` [3] (essentially a global object) where as the middleware design leads us to bake in data in the request object. The example blow demonstrates how middleware definition works and how "adding data" per request works in both of these frameworks.
# Flask; assuume `app` exists
from flask import g
@app.before_request
def before_request():
g.some_variable = 'foobar'
vs class ExampleComponent(object):
def process_request(self, req, resp):
req.some_variable = 'foobar'
Mirror: https://pastebin.com/Z3yGCfzFAdditionally, Falcon middleware API also exposes `process_resource` which is similar to above but also allows us to _pass_ arguments to the routed resource instance.
[1] http://flask.pocoo.org/docs/1.0/api/#flask.Flask.before_requ...
[2] https://falcon.readthedocs.io/en/stable/api/middleware.html
[3] http://flask.pocoo.org/docs/1.0/appcontext/#storing-data
Edit: Formatting
Each night before bed I hope this project EOLs.
Different languages, different frameworks. There is no namespace collision.
https://www.codeotaku.com/journal/2018-06/improving-ruby-fib...
This is a great article about the different Ruby web servers (from 2015): https://www.speedshop.co/2015/07/29/scaling-ruby-apps-to-100...
Would be great to have a comparison with Falcon. Also, speaking of performance, I still need to set up jemalloc. Have heard lots of good things about that.
Pragmatically speaking, it will be production ready when it reaches 1.0 - until then, make sure you test it well and keep an eye on how it behaves. I've literally run millions of requests against test servers, but... it's pretty wild out there!
I've previously been looking at https://github.com/ruby-concurrency/concurrent-ruby based on its use in Rails and was wondering if there's a comparison of the two approaches anywhere that would be newbie friendly?
I was also wondering will Falcon run on JRuby? (I see that nio4r does so hopefully that also means that things like Async-Container will.)
I'm guessing this is because process forking generally isn't supported on the JVM, is that correct?
Writeup: http://eileencodes.com/posts/http2-early-hints/ PR: https://github.com/puma/puma/pull/1403
EDIT: e.g. bottom of https://www.speedshop.co/2016/01/07/what-http2-means-for-rub...
Sometimes best practice for HTTP/1 becomes anti-pattern for HTTP/2. Some of these issues are discussed here: https://www.codeotaku.com/journal/2018-10/http-2-for-ruby-we...
This project must be fairly new as I was not able to find it mentioned and compared with other ruby servers. Does anyone know some comparison running puma vs falcon or passenger vs falcon?
And do you have advice / recommendations on database pool sizes when using Rails? I assume that each process will have access to as many connections as specified in the DB_POOL config? Does falcon need any pre/post fork re-creation of all connections?
Regarding the pool size, I'd suggest that it should correspond more to how many simultaneous connections you think you'd have per process, so maybe just choose something like 32 to start with? Benchmarks would be required to find the sweet spot.
To achieve true scalability you should use something like https://github.com/socketry/async-postgres (/mysql) but this is a work in progress.
That being said, it's actually named after the Peregrine Falcon, which is the fastest bird in the animal kingdom, and it was a friendly poke at Puma :)
Sometimes the new implementation was not only “faster” under certain benchmarks, but deemed superior for “philosophical” reasons… I think it was the case for unicorn that load balanced over a pool of unix processes.
Eventually I left the ruby/rails world behind. Not the main reason, but I remember performance problems being very frustrating.
To quote [an old, 2011 blog post from] antirez: “it is not ok that by default [ruby] is so damn slow”. [1]
I was expecting to find ruby state of affairs to be the same these days, but looking at TechEmpower’s recent benchmarks [2] (benchmark reading caveats apply), seems like ruby implementations are reasonably speedy nowadays: benchmarks reflect results within the same order of magnitude to the fastest ones, except for the plaintext benchamark. I extracted some interesting metrics below.
Still, Ruby consistently comes after the 100th entry for all benchmarks.
A lot of people will say that the reasons to use ruby are the gains in productivity and the expresibility, DSL capabilities, etc. If that’s the evaluation metric, maybe Clojure could be a good alternative. Also, it performs very well! (thanks to the JVM, no doutbts). I believe immutable by default is a better way to build apps, and in Clojure, the whole language is built around this concept. Ruby is the other way around: even parts of the language that shouldn't are open for modification!
I wonder if jRuby would be a good contender for performance.... but...
* I’m not sure why there are no jRuby implementations on the techempower benchmarks.. evidently, not a very popular technology?
* I’m guessing that startup time for tools like IRB must the one of the turnoffs for ruby people.
1: http://oldblog.antirez.com/post/scalability-and-speed-of-web...
2: https://www.techempower.com/benchmarks/
--
Numbers: max requests per second for an specific implementation in TechEmpower's benchmarks
JSON serialization.
1st Java: 1,197,864 20th Rust: 1,135,647 (1.05X slower) 21st Clojure, 1,126,256 (1.06X slower) 141st Ruby 173,905 (6.9X slower)
Single query:
1st Java: 656,262 20th Go: 309,728 (2.12X slower) 54th Clojure: 209,466 (3.13X slower) 120th Ruby: 96,174 (6.82X slower)
Multiple Queries
1st Java: 43,245 20th Kotlin: 22,643 (1.91X slower) 21st Clojure: 22,536 (1.92X slower) 100th: Ruby: 11,216 (3.86X slower)
Fortunes
1st C: 424,712 20th Go: 193,079 (2.2X slower) 78th Clojure: 98,056 (4.33X slower) 131st Ruby: 51,043 (8.32X slower)
Data Updates
1st Java: 18,152 20th Dart: 6,785 (2.68X slower) 65th Clojure: 3,707 (4.9X slower) 95th Ruby: 2,839 (6.39X slower)
Plaintext
1st: Rust: 7,040,642 18th Clojure: 4,178,241(1.69X slower) 20th Java: 3,858,510 (1.82X slower) 134th Ruby: 239,652 (29.38X slower)