Today we're happy to release Einhorn, the
language-independent shared socket manager. Einhorn makes it easy to
have multiple instances of an application server listen on the same
port. You can also seamlessly restart your workers without dropping
any requests. Einhorn requires minimal application-level support,
making it easy to use with an existing project.

Motivation

The main alternatives for achieving this functionality are FastCGI (and related options such as
Phusion Passenger) and Unicorn (and
derivatives such as Rainbows!). In our case
using either would have required significant application changes. As
well, we could only use them for applications speaking HTTP. So we
decided to build a general solution.

Unicorn's architecture has a lot going for it, though. It uses a
shared socket opened by a master process and then inherited by
workers. This means all concurrency is handled by your operating
system's scheduler. At
any time, you can ask Unicorn to upgrade your workers, and it will
spin up a new pool of workers before killing off the old. Unicorn can
also preload your application, meaning it loads everything prior to
forking so that your code is only stored in memory
once.

We decided to take the best features of Unicorn and roll them into
a language-independent shared socket manager, which we dubbed Einhorn
(the German word for Unicorn).

Let's break down the arguments here. The -m manual
flag indicates that Einhorn should wait for an explicit
acknowledgement from the time_server before considering
it "up". (By default, Einhorn will consider a worker up if it's been
alive for one second.) When it's ready, the time_server
worker connects to the Einhorn master and sends an ACK command.

The remaining arguments serve as a template of the program to
run. Einhorn scans for server socket specifications of the form
srv:(IP:PORT)[<,OPT>...]. When it finds one, it
configures a corresponding socket and replaces the specification with
the socket's file
descriptor number. The specification
srv:127.0.0.1:2345,so_reuseaddr is taken to mean "create
a socket listening on 127.0.0.1:2345 with the SO_REUSEADDR
flag set". In the above case, the opened socket had file descriptor
number 6. See the README
for more details on specifying server sockets.

Features

Einhorn lets you spin up any number of worker processes (the
number can be adjusted on the fly) each possessing one or more shared
sockets. Einhorn can spawn a new pool of workers and gracefully kill
off the old ones, allowing seamless upgrades to new versions of your
code. As well, Einhorn gets out of your application's way — the
shared sockets are just file descriptors which your application
manipulates directly or manages with an existing framework. You can
introspect a running Einhorn's state or send it administrative
commands using its command shell, einhornsh.

If you happen to be using Ruby, Einhorn can also preload your
application. Just pass a -p PATH_TO_CODE and define a
method einhorn_main as your workers' entry point:

As in Unicorn, this reduces memory usage and makes spawning
additional workers very lightweight. Preloading is Einhorn's only
language-dependent feature (and was easy to implement because Einhorn
is itself written in Ruby). Adding preloading for other languages
would require some architectural changes, but we might do it in the
future.

Though Einhorn requires very little cooperation from your code, we
still had to do some work to make our API servers compatible. In
particular, we use Thin and EventMachine, both of which
needed patching to support the use of an existing file descriptor. The
relevant patches are on the master branch of our public forks of the
respectiveprojects.

These days, we use Einhorn to run all of our application
servers. We also use it to run our non-web processes where we want to
spawn and keep alive multiple instances. We run Einhorn under a
process manager (we use daemontools, but any will
work) — adding Einhorn into your existing infrastructure should
just require adding an einhorn into the command-line
arguments of your managed processes.

We've been using Einhorn in production for a number of months
now. We hope you'll find it useful as well. If you want to run a web
app but can't use Unicorn, or if you have a worker process that you
want to start pooling, you should check Einhorn out and let us know what you think!