Skip to content

Commit

Permalink
docs: Add usage example for multi proc behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
trallnag committed Mar 13, 2023
1 parent b5a7d4c commit 241229b
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 0 deletions.
61 changes: 61 additions & 0 deletions devel/examples/prom-multi-proc-gunicorn/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Example `prom-multi-proc-gunicorn`

Minimal example that shows integration of FastAPI (Gunicorn) with the Prometheus
client library in multi process mode without Prometheus FastAPI Instrumentator.
Highlights missing metrics that are not supported in multi process mode.

To run the example, you must have run `poetry install` and `poetry shell` in the
root of this repository. The following commands are executed relative to this
directory.

Set environment variable to an unused location:

```shell
export PROMETHEUS_MULTIPROC_DIR=/tmp/python-testing-pfi/560223ba-887f-429a-9c48-933df56a68ba
```

Start the app with Gunicorn using two Uvicorn workers:

```shell
rm -rf "$PROMETHEUS_MULTIPROC_DIR"
mkdir -p "$PROMETHEUS_MULTIPROC_DIR"
gunicorn main:app \
--config gunicorn.conf.py \
--workers 2 \
--worker-class uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8080
```

Interact with app:

```shell
for i in {1..5}; do curl localhost:8080/ping; done
curl localhost:8080/metrics
```

You should see something like this:

```txt
# TYPE ping_total counter
ping_total 5.0
# HELP metrics_total Number of metrics calls.
# TYPE metrics_total counter
metrics_total 1.0
# HELP main_total Counts of main executions.
# TYPE main_total counter
main_total 2.0
```

Check the returned metrics:

- `main_total` is `2`, because Gunicorn is using two workers.
- There are no `created_by` metrics. These are not supported by the Prometheus
client library in multi process mode.
- No metrics for things like CPU and memory. They come from components like the
`ProcessCollector` and `PlatformCollector` which are not supported by the
Prometheus client library in multi process mode.

Links:

- <https://github.com/prometheus/client_python#multiprocess-mode-eg-gunicorn>
- <https://docs.gunicorn.org/en/stable/settings.html>
5 changes: 5 additions & 0 deletions devel/examples/prom-multi-proc-gunicorn/gunicorn.conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from prometheus_client import multiprocess


def child_exit(server, worker):
multiprocess.mark_process_dead(worker.pid)
46 changes: 46 additions & 0 deletions devel/examples/prom-multi-proc-gunicorn/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import os

from fastapi import FastAPI
from prometheus_client import (
CONTENT_TYPE_LATEST,
CollectorRegistry,
Counter,
generate_latest,
multiprocess,
)
from starlette.responses import Response

if "PROMETHEUS_MULTIPROC_DIR" not in os.environ:
raise ValueError("PROMETHEUS_MULTIPROC_DIR must be set to existing empty dir.")


PING_TOTAL = Counter("ping", "Number of pings calls.")
METRICS_TOTAL = Counter("metrics", "Number of metrics calls.")

MAIN_TOTAL = Counter("main", "Counts of main executions.")
MAIN_TOTAL.inc()


app = FastAPI()


@app.get("/ping")
def get_ping():
PING_TOTAL.inc()
return "pong"


@app.get("/metrics")
def get_metrics():
METRICS_TOTAL.inc()

# Note the ephemeral registry being used here. This follows the Prometheus
# client library documentation. It comes with multiple caveats. Using a
# persistent registry might work on first glance but it will lead to issues.
# For a long time PFI used a persistent registry, which was wrong.
registry = CollectorRegistry()
multiprocess.MultiProcessCollector(registry)
resp = Response(content=generate_latest(registry))

resp.headers["Content-Type"] = CONTENT_TYPE_LATEST
return resp

0 comments on commit 241229b

Please sign in to comment.