Skip to main content

granian-multiport

granian-multiport runs each Granian worker on a different TCP port. It is intended for deployments where a reverse proxy such as nginx performs load-aware dispatch (least_conn) across workers.

Every listening socket is bound once in the parent process and inherited by exactly one worker.

Granian itself is not patched. This package reuses Granian's complete Click CLI and supplies an external MPServer subclass that binds a distinct listener for every worker.

Why

Granian uses SO_REUSEPORT to load-balance multiple workers, but Linux distributes connections randomly with SO_REUSEPORT. This means that when distributing 16 connections across 8 workers, there is an approximately 80% chance that one worker will receive 4 or more connections. This can lead to issues with 99%-tile latency and CPU utilization efficiency.

By assigning different ports to each worker process and allowing a reverse proxy such as nginx to implement a better load-balancing algorithm, 99%-tile latency and CPU utilization efficiency can be significantly improved.

In addition, Granian relies on net.ipv4.tcp_migrate_req for graceful restarts when SO_REUSEPORT is enabled. In environments other than Linux, or in environments where this system setting is not configured, connections remaining in the backlog of old workers will be discarded and errors will occur when restarting workers.

This project aims to demonstrate that better load balancing can be achieved through multi-port support.

Install

python -m pip install .

The initial release supports Granian 2.8.x and Python 3.12 or newer on POSIX systems. It intentionally pins the Granian minor series because it uses the private multiprocessing server API.

Run on consecutive ports

The usual Granian --port becomes the first worker port:

granian-multiport \
  --interface asgi \
  --host 127.0.0.1 \
  --port 8000 \
  --workers 4 \
  myproject.asgi:application

Workers listen on ports 8000, 8001, 8002, and 8003. Every other Granian CLI option remains available.

Run on explicit ports

granian-multiport \
  --interface asgi \
  --ports 8000,8010,8020,8030 \
  myproject.asgi:application

When both --ports and --workers are given, their counts must agree. The explicit list can also be supplied through GRANIAN_MULTIPORT_PORTS. Unix domain sockets are intentionally unsupported because the command's purpose is to expose one TCP endpoint per worker.

nginx

upstream app_workers {
    least_conn;
    server 127.0.0.1:8000;
    server 127.0.0.1:8001;
    server 127.0.0.1:8002;
    server 127.0.0.1:8003;

    keepalive 32;
}

server {
    listen 80;

    location / {
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_pass http://app_workers;
    }
}

With upstream keep-alive enabled, nginx's least_conn choice occurs when it opens or selects an upstream connection; benchmark with the connection reuse policy that matches production.

Development

python -m pip install -e '.[test]'
pytest

The integration test starts three workers, requests every port, and verifies that each port consistently reaches a different worker process. ll

Release files for granian-multiport 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for granian-multiport 0.1.0
File Size Uploaded
granian_multiport-0.1.0.tar.gz 9.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for granian-multiport 0.1.0
File Interpreter ABI Platform
granian_multiport-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 17.0 kB

Release files / granian_multiport-0.1.0.tar.gz

Download URL granian_multiport-0.1.0.tar.gz
Size 9.4 kB
Tags Source
SHA-256 checksum
How to use checksums
2e2503ac4f4a22a7203a074f6d72034a77d940294ea1a74918414c03a6df5de4
BLAKE2b-256 checksum
How to use checksums
3ae67bf77755cf8c913c5210add3cec63423f98dc1a2f8b1d5ef8a1f3b11cf62
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / granian_multiport-0.1.0-py3-none-any.whl

Download URL granian_multiport-0.1.0-py3-none-any.whl
Size 7.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2d96f613be6f603395db55fc3fb8d1a338c1e411d6b3894b0579253628fa02eb
BLAKE2b-256 checksum
How to use checksums
49865fb41c5e9cddab977befe689d1d9d2bf8c14fa8f4bd67203d831c08daf75
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

0.1.1

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page