Repository navigation
How to use SockSite with a pre-connected socket? #14367
|
I have a oneshot systemd service defined as follows: post@.service[Unit]
Description=REDACTED
[Service]
Type=oneshot
User=REDACTED
Group=REDACTED
WorkingDirectory=REDACTED
ExecStart=python REDACTED/aio_sockserv.py
TimeoutStartSec=30
[Install]
WantedBy=default.targetAnd corresponding socket file: post.socket[Unit]
Description=REDACTED
[Socket]
ListenStream=0.0.0.0:9812
Accept=yes
TimeoutSec=1
[Install]
WantedBy=default.targetPreviously, I had managed to get this to work by grabbing code from different places, but the code below now shows this warning: aio_sockserv.pyimport asyncio
import socket
from aiohttp import web
from systemd.daemon import listen_fds
async def main():
fds = listen_fds()
with socket.fromfd(fds[0], socket.AF_INET, socket.SOCK_STREAM) as sock:
app = web.Application(middlewares=[set_no_keep_alive])
app.router.add_post(REDACTED)
loop = asyncio.get_running_loop()
transport, protocol = await loop.create_connection(
app.make_handler(tcp_keepalive=False), sock=sock
)
try:
await protocol.start()
finally:
await protocol.shutdown()
asyncio.run(main())
However, while trying to migrate to the new way: runner = web.AppRunner(app)
await runner.setup()
try:
site = web.SockSite(runner, sock)
await site.start()
while True:
await asyncio.sleep(10)
finally:
await runner.cleanup()it is crashing, because the socket is already connected: Is there a way to prevent aiohttp from "reconnecting" the socket? Thanks in advance. |
Replies: 3 comments 4 replies
This comment was marked as low quality.
This comment was marked as low quality.
This comment was marked as low quality.
This comment was marked as low quality.
|
I've actually just been playing with this myself, and was about to add documentation on how to serve with systemd. Do you have a reverse proxy? We highly recommend using a reverse proxy, at which point it is recommended to use a Unix socket for the application. It should then look roughly along the lines of: # /etc/systemd/system/myapp.socket
[Unit]
Description=myapp HTTP socket
[Socket]
ListenStream=/run/myapp/http.sock
SocketGroup=www-data
SocketMode=0660
[Install]
WantedBy=sockets.target# /etc/systemd/system/myapp.service
[Unit]
Description=myapp
Requires=myapp.socket
[Service]
User=myapp
ExecStart=python -m myapp
Restart=on-failure
[Install]
WantedBy=multi-user.target# myapp/__main__.py
from aiohttp import web
from systemd import daemon
from myapp.app import init_app
def main(sock: socket.socket) -> None:
"""Run the server in production environment."""
# Get the Unix socket FD
fds = daemon.listen_fds()
if len(fds) != 1:
raise RuntimeError(f"expected exactly one socket from systemd, got {len(fds)}")
if not daemon.is_socket_unix(fds[0], type=socket.SOCK_STREAM, listening=1):
raise RuntimeError(f"fd {fds[0]} is not a listening Unix stream socket")
app = init_app()
web.run_app(app, sock=socket.socket(fileno=fds[0]))
if __name__ == "__main__":
main()We highly recommend using run_app(), rather than trying to use AppRunner and SockSite directly (these are meant for weird edge cases, where the aiohttp app is not the primary app). |
I've actually just been playing with this myself, and was about to add documentation on how to serve with systemd.
Do you have a reverse proxy? We highly recommend using a reverse proxy, at which point it is recommended to use a Unix socket for the application.
It should then look roughly along the lines of:
# myapp/__m…