Tipi - the All-in-one Web Server for Ruby Apps
Tipi is an integrated, feature-complete HTTP/S server for Ruby applications.
Tipi is built on top of
Polyphony, a robust,
high-performance library for building highly-concurrent applications in Ruby.
Tipi can be used to serve any Rack application or set of static files directly
without having to employ a reverse-proxy such as Nginx.
Caveat emptor: the following results were obtained with an ad-hoc, manual
process. I am not really familiar with the servers I compared Tipi against,
and I ran them in their default configuration (apart from setting the number
of workers). Take these results with a bunch of salt.
Tipi | Puma | Falcon | Unicorn | |
---|---|---|---|---|
HTTP/1.1 | 138629 | 34573 | 40714 | 7438 |
HTTPS/2 | 56762 | n/a | 34226 | n/a |
wrk -d60 -t4 -c64 <url>
To run Tipi, run the included tipi
command. Alternatively you can add tipi as
a dependency to your Gemfile, then run bundle exec tipi
. By default
Tipi can be used to drive Rack apps or alternatively any app using the
Qeweney request-response interface.
Use the tipi
command to start your app:
$ bundle exec tipi myapp.ru
$ bundle exec tipi myapp.rb
The app script file should define an app
method that returns a proc/lambda
taking a single Qeweney::Request
argument. Here’s an example:
# frozen_string_literal: true
def app
->(req) { req.respond('Hello, world!', 'Content-Type' => 'text/plain') }
end
By default, Tipi serves plain HTTP on port 1234, but you can easily change that
by providing command line options as follows:
To listen for plain HTTP, use the -l
/--listen
option and specify a port
number:
$ bundle exec tipi -l9292 myapp.ru
To listen for HTTPS connections, use the -s
/--secure
option and specify a
host name and a port:
$ bundle exec tipi -sexample.com:9292 myapp.ru
The Tipi full service listens for both HTTP and HTTPS and supports automatic
certificate provisioning. To use the full service, use the -f
/--full
option,
and specify the domain name, the HTTP port, and the HTTPS port, e.g.:
$ bundle exec tipi -fmysite.org:10080:10443 myapp.ru
#If serving multiple domains, you can use * as place holder
$ bundle exec tipi -f*:10080:10443 myapp.ru
If localhost
is specified as the domain, Tipi will automatically generate a
localhost certificate.
By default, the tipi
command starts a single controller and uses
Polyphony to run each connection
on its own fiber. This means that you will have a single process running on a
single thread (on a single CPU core). In order to parallelize your app and
employ multiple CPU cores, you can tell Tipi to fork multiple worker processes
to run your app. The number of workers is controlled using the -w
/--workers
option:
# fork 4 worker processes
$ bundle exec tipi -w4 myapp.ru
You can also set Tipi to spawn multiple threads in each worker when in
compatibility mode (see below.)
Note: compatibility mode is still being developed, and currently only supports
HTTP/1 connections.
In some apps, using Polyphony is not possible, due to incompatibilities between
it and other third-party dependencies. In order to be able to run these apps,
Tipi provides a compatibility mode that does not use Polyphony for concurrency,
but instead uses a thread-per-connection concurrency model. You can also fork
multiple workers, each running multiple threads, if so desired. Note that the
concurrency level is the maximum number workers multiplied by the number of
threads per worker:
concurrency = worker_count * threads_per_worker
To run Tipi in compatibility mode, use the -c
/--compatibility
option, e.g.:
# 4 workers * 8 threads = 32 max concurrency
$ bundle exec tipi -c -w4 -t8 myapp.ru
Tipi employs a supervisor-controller-worker process supervision model, which
minimizes the memory consumption of forked workers, and which facilitates
graceful reloading after updating the application code.
This supervision model is made of three levels:
(If the worker count is 1, the Controller and Worker roles are merged into a
single process.)
This model allows Tipi to fork workers after loading the app code, and use a
much simpler way to perform graceful restarts:
A graceful restart performed by sending SIGUSR2
to the supervisor process.
Documentation for Tipi’s API is coming soon…