Apache HTTP Server Version 2.5

| Description: | A lean, fast, self-contained event-driven Multi-Processing Module built on the APR pollset and thread pool especially suited as a reverse proxy |
|---|---|
| Status: | MPM |
| Module Identifier: | mpm_motorz_module |
| Source File: | motorz.c |
The motorz Multi-Processing Module (MPM) is an
asynchronous, event-driven implementation. It combines a
prefork-style fixed pool of child processes with an event core built on
APR's pollset and a shared thread pool. Each child
runs one or more dedicated poller threads that watch sockets
and timers, dispatching ready I/O events and expired timers to a pool of
worker threads. The workers never poll; they only process the
connection/request work pushed to them.
The design goal is a fast, efficient, single, compact MPM that runs on modern Unix platforms by leaning on APR as much as possible, while still supporting the asynchronous connection handling needed for efficient keep-alive and HTTP/2.
To use the motorz MPM, add
--with-mpm=motorz to the configure
script's arguments when building the httpd, or build
it as a loadable module with
--enable-mpms-shared=motorz.
When built as a DSO module, it can be loaded with:
LoadModule mpm_motorz_module modules/mod_mpm_motorz.so
How it Works
Asynchronous connection handling
Graceful stop and restart
Scoreboard and mod_status
Platforms and limitations
Admission control
Relationship to other MPMs
CoreDumpDirectory
EnableExceptionHook
GracefulShutdownTimeout
Group
Listen
ListenBacklog
MaxConnectionsPerChild
MaxMemFree
PidFile
PollersPerChild
ScoreBoardFile
SendBufferSize
ServerLimit
StartServers
ThreadLimit
ThreadsPerChild
Usermotorz uses prefork as the framework for process
management and an event core for connection handling. A single control
process (the parent) launches a fixed number of child processes, as set
by the StartServers directive.
Unlike worker and event, the number of
children does not float with load: motorz maintains a
static pool, replacing children one-for-one as they exit. Concurrency
within a host is scaled by adding worker threads
(ThreadsPerChild) and, where
the poll/dispatch path is the bottleneck, poller threads
(PollersPerChild), rather than by spawning more
processes.
Each child process runs:
PollersPerChild.ThreadsPerChild) that run
the actual connection and request processing pushed to them. Workers
never poll.MaxConnectionsPerChild
and the pipe-of-death / generation, signals the pollers to wind down,
and then joins them on exit.A connection is sharded to one poller at accept time (round-robin) and
bound to it for its whole lifetime: it re-arms in, and times out on, that
poller's pollset and timer ring. Using multiple pollers lifts the
single-poll-thread throughput ceiling, so accept, event dispatch and timer
expiry scale with PollersPerChild instead of being
serialized on one thread.
While the parent process is usually started as root under
Unix in order to bind to port 80, the child processes and threads are
launched by the server as a less-privileged user. The
User and
Group directives are used to set
the privileges of the Apache HTTP Server child processes. The child
processes must be able to read all the content that will be served, but
should have as few privileges beyond that as possible.
MaxConnectionsPerChild
controls how frequently the server recycles processes by retiring old ones
and launching new ones.
motorz reports itself as an asynchronous MPM. When a
worker finishes the active phase of a connection (for example, an
HTTP keep-alive connection between requests, or a connection waiting on
further I/O), it hands the socket back to its poller rather than holding a
worker thread idle. The poller waits for the next event on that socket,
bounded by the configured Timeout,
and re-dispatches the connection to a worker only when there is work to do.
This frees worker threads from idle keep-alive connections and is what
allows efficient HTTP/2 handling, where the master connection is handed
back to the MPM between requests.
Lingering close is also non-blocking: instead of blocking a worker for the duration of the lingering-close timeout, the draining socket is handed back to the poll loop with a bounded linger timeout, so the worker is returned to the pool immediately.
Modules that take a connection fully asynchronous (suspending it and resuming it later) are supported; a suspended connection is parked and re-armed on its owning poller when resumed.
When a child is asked to stop gracefully (a graceful restart or stop,
or reaching MaxConnectionsPerChild)
it stops accepting connections, closes idle keep-alive connections, and
lets all other connections, including responses still being written,
finish before it exits. A previous generation therefore keeps its process
slots until its connections are done, which is why
ServerLimit must leave room for
both generations. GracefulShutdownTimeout
bounds how long the parent waits on a graceful stop.
A child does not enforce a deadline of its own while it drains. A
connection that is still writing a response, or is waiting on I/O for a
module that handles it asynchronously, is given until it completes or the
configured Timeout expires, so a
graceful restart can take as long as the longest such connection.
A connection is not bound to a thread, so the scoreboard's per-thread
slots represent the worker threads. A worker reports the connection it is
processing through its slot and sets the slot back to ready when it is done
with it; a connection parked in a poller (keep-alive, write completion,
lingering close, waiting on I/O) does not occupy a slot. The busy and idle
worker counts in mod_status therefore reflect the worker
threads, as they do for the other threaded MPMs.
motorz runs on Unix-like platforms whose APR supports
threads and which can use a thread-safe pollset (kqueue, event ports or
epoll). It is not available on Windows, NetWare or OS/2.
ThreadStackSize
directive is ignored (a warning is logged): the worker threads come from
an APR thread pool, which does not allow their stack size to be set.Mutex accept setting is accepted but
has no effect.MinSpareThreads,
MaxSpareThreads or
MaxRequestWorkers controls,
as described above.To keep a child safe under overload, motorz applies
listener backpressure. When the worker pool saturates, the poller that
owns the listening sockets removes them from its pollset and stops
accepting; it re-adds them once the backlog drains. This keeps the work
queue and per-connection memory bounded rather than growing without limit.
The decision is based on the worker pool's idle, pending and active-thread
counts, with hysteresis to avoid flapping the listeners on and off.
Because the admission-control low-water mark is a fraction of
ThreadsPerChild, very small
values (in particular ThreadsPerChild 1) cause the listeners
to re-enable only when the work queue is completely empty, which severely
degrades throughput. A value of ThreadsPerChild of at least 4
is strongly recommended; the server emits a warning otherwise.
motorz uses prefork for process management and an APR
thread pool for workers, with pollers dispatching work to that pool. This
is distinct from event's listener/worker/fdqueue design,
in which the worker threads themselves re-arm a shared, thread-safe
pollset.
Whether additional pollers help depends on the workload. If the worker
threads are the CPU bottleneck—typical for real request
processing—the poller threads are not the limiting factor, and a
PollersPerChild beyond one or two yields little. The
multiple-poller design removes motorz's
structural single-thread ceiling, but per-host throughput is still
governed by worker CPU.
Unlike worker and event,
motorz does not scale the number of child processes with
load. The process pool is fixed at
StartServers.
ServerLimit only bounds the
number of process slots, including those held by old children that are
still finishing connections after a graceful restart; it defaults to twice
StartServers and, like
ThreadLimit, cannot be changed
during a restart. There are no
MinSpareThreads /
MaxSpareThreads /
MaxRequestWorkers controls.
Scale concurrency with
ThreadsPerChild (and, if the
poll path saturates, PollersPerChild).
| Description: | Number of poll threads per child process |
|---|---|
| Syntax: | PollersPerChild number |
| Default: | PollersPerChild 0 |
| Context: | server config |
| Status: | MPM |
| Module: | motorz |
The PollersPerChild directive sets the number of
poller threads created in each child process. Each poller owns its own
pollset, timer ring and connection-recycle list, and handles a shard of
the child's connections. Because each poller thread independently
accepts connections and dispatches ready I/O events and timer
expiries to the worker pool, adding pollers raises the rate at which a
single child process can handle these operations in parallel rather than
serializing them on one poll thread.
A value of 0 (the default) means auto: the number
of pollers is derived from the number of online CPUs, capped at a built-in
maximum. In all cases the number of pollers is clamped so that it never
exceeds ThreadsPerChild and is
never less than one.
Because event dispatch is rarely the bottleneck for real request
processing—worker CPU usually is—values beyond one or two
seldom improve throughput. Raising PollersPerChild
is mainly useful for workloads dominated by very high connection churn or
large numbers of idle, event-driven connections, where the poll/accept
path itself becomes the limit.
StartServers 2 ThreadsPerChild 64 ThreadLimit 64 PollersPerChild 2