Skip to content

Latest commit

 

History

History
108 lines (71 loc) · 3.69 KB

ForkServer.rst

File metadata and controls

108 lines (71 loc) · 3.69 KB

The Fork Server (sponsored by Intellisurvey)

This is a really advanced (and complex) feature developed with sponsorship from Intellisurvey.com.

If you have dozens or even hundreds of applications built upon the same codebase you can setup your Emperor to fork vassals from an already running one (with the application core loaded).

Currently the feature is supported only in the PSGI plugin, and requires Linux kernel >= 3.4.

How it works

When in fork-server mode, the Emperor differentiates between two kind of vassals: base vassals and adopted vassals.

"base" vassals are pretty much classic vassals; generated by fork() + execve() by the Emperor. The only difference is that they are supposed to load as much of your application code as possible as soon as possible, then suspend themselves waiting for connections on a UNIX socket.

A "base" vassal will be something like this

[uwsgi]
; load myapp.pl as soon as possible
early-psgi = myapp.pl
; suspend and execution and bind on UNIX socket /tmp/fork_server.socket
fork-server = /tmp/fork_server.socket

"Adopted" vassals are the true "new thing".

Once an adopted vassal is requested, the Emperor connects to the specified fork server (instead of calling fork() + execve() itself).

The Emperor passes an uwsgi-serialized array of command line options of the new vassal and up to 3 file descriptors (since UNIX sockets allow passing file descriptors from one process to another).

Those 3 file descriptors are:

  • 1 -> the communication pipe with the Emperor (required)
  • 2 -> the config pipe (optional)
  • 3 -> on_demand socket (optional)

At this point, the fork server fork()s itself twice and continues the uWSGI startup using the supplied arguments array.

How can the Emperor wait() on an external process, then?

This is why a >= 3.4 kernel is required, as thanks to the prctl(PR_SET_CHILD_SUBREAPER, 1) call we can tell vassals to be re-parented to the Emperor when their parent dies (in fact the fork-server forks two times, so the vassal has no live parent, poor thing).

Now the Emperor has a new child and a communication pipe. And that's all.

Configuring the Emperor for fork-server mode

You need only two new options: --emperor-use-fork-serve <addr> and --vassal-fork-base <name>

Let's start with a slow-loading (10 seconds) Perl app:

# myapp.pl
print "I am the App\n";
sleep(10);
my $app = sub {
     return [200, ['Content-Type'=>'text/html'], ["Hello World"]];
};

Save it as myapp.pl and load it in perlbase.ini vassal file (this is a base vassal):

[uwsgi]
early-psgi = myapp.pl
fork-server = /var/run/fork_server.socket

Now create two vassals (one.ini and two.ini) that will fork() from the base one:

[uwsgi]
; one.ini
http-socket = :8181
processes = 4
uid = 1001
gid = 1001
[uwsgi]
; two.ini
http-socket = :8282
processes = 8
uid = 1002
gid = 1002

As you can see they are pretty different, even in privileges.

Now let's spawn the Emperor in fork-server mode allowing perlbase.ini as a "base" vassal:

[uwsgi]
emperor = /etc/uwsgi/vassals
emperor-use-fork-server = /var/run/fork_server.socket
vassal-fork-base = perlbase.ini
emperor-stats = 127.0.0.1:5000

The Emperor will start running perlbase.ini as a standard vassal, while for the non-base ones it will fork() from the base, where the app is already loaded.

You will note that instead waiting for 10 seconds, your new vassals will start immediately. Pretty cool, huh?