Skip to content

Commit

Permalink
Implemented trio backend (#337)
Browse files Browse the repository at this point in the history
  • Loading branch information
francis-clairicia authored Aug 3, 2024
1 parent 94f5e72 commit fa15bff
Show file tree
Hide file tree
Showing 99 changed files with 9,049 additions and 1,741 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,6 @@
"reportUnsupportedDunderAll": "warning",
"reportShadowedImports": "none"
},
"python.analysis.autoImportCompletions": true
"python.analysis.autoImportCompletions": true,
"css.format.spaceAroundSelectorSeparator": true
}
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ class EchoRequestHandler(AsyncStreamRequestHandler[RequestType, ResponseType]):
self,
client: AsyncStreamClient[ResponseType],
) -> AsyncGenerator[None, RequestType]:
data: Any = yield # A JSON request has been sent by this client
# A JSON request has been sent by this client
data: Any = yield

self.logger.info(f"{client!r} sent {data!r}")

Expand All @@ -107,14 +108,14 @@ async def main() -> None:
)

async with AsyncTCPNetworkServer(host, port, protocol, handler) as server:
try:
await server.serve_forever()
except asyncio.CancelledError:
pass
await server.serve_forever()


if __name__ == "__main__":
asyncio.run(main())
try:
asyncio.run(main())
except* KeyboardInterrupt:
pass
```

### TCP Echo client with JSON data
Expand Down Expand Up @@ -145,8 +146,7 @@ if __name__ == "__main__":
main()
```

<details markdown="1">
<summary>Asynchronous version ( with <code>async def</code> )</summary>
#### Asynchronous version ( with `async def` )

```py
import asyncio
Expand All @@ -158,7 +158,7 @@ from easynetwork.clients import AsyncTCPNetworkClient
async def main() -> None:
async with AsyncTCPNetworkClient(("localhost", 9000), JSONProtocol()) as client:
await client.send_packet({"data": {"my_body": ["as json"]}})
response = await client.recv_packet() # response should be the sent dictionary
response = await client.recv_packet()
print(response) # prints {'data': {'my_body': ['as json']}}


Expand All @@ -171,6 +171,6 @@ if __name__ == "__main__":
## License
This project is licensed under the terms of the [Apache Software License 2.0](https://github.com/francis-clairicia/EasyNetwork/blob/main/LICENSE).

### `easynetwork.lowlevel.typed_attr`
### AnyIO's typed attributes

AnyIO's typed attributes incorporated in `easynetwork.lowlevel.typed_attr` from [anyio 4.2](https://github.com/agronholm/anyio/tree/4.2.0), which is distributed under the [MIT license](https://github.com/agronholm/anyio/blob/4.2.0/LICENSE).
AnyIO's typed attributes is incorporated in `easynetwork.lowlevel.typed_attr` from [anyio 4.2](https://github.com/agronholm/anyio/tree/4.2.0), which is distributed under the [MIT license](https://github.com/agronholm/anyio/blob/4.2.0/LICENSE).
188 changes: 186 additions & 2 deletions benchmark_server/run_benchmark
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,22 @@ BENCHMARKS_DEF: Final[Sequence[_BenchmarkDef]] = (
},
"client": _tcp_echoclient,
},
{
"name": "tcpecho-easynetwork-trio",
"title": "TCP echo server (easynetwork+trio)",
"server": (
*_python_cmd,
"/usr/src/servers/easynetwork_tcp_echoserver.py",
f"--port={EXPOSED_PORT}",
"--trio",
),
"ping": {
"server_address": _tcp_server_address,
"ping_request": b"ping\n",
"socket_type": SOCK_STREAM,
},
"client": _tcp_echoclient,
},
{
"name": "tcpecho-easynetwork-buffered-asyncio",
"title": "TCP echo server (easynetwork+buffered+asyncio)",
Expand Down Expand Up @@ -181,6 +197,23 @@ BENCHMARKS_DEF: Final[Sequence[_BenchmarkDef]] = (
},
"client": _tcp_echoclient,
},
{
"name": "tcpecho-easynetwork-buffered-trio",
"title": "TCP echo server (easynetwork+buffered+trio)",
"server": (
*_python_cmd,
"/usr/src/servers/easynetwork_tcp_echoserver.py",
f"--port={EXPOSED_PORT}",
"--buffered",
"--trio",
),
"ping": {
"server_address": _tcp_server_address,
"ping_request": b"ping\n",
"socket_type": SOCK_STREAM,
},
"client": _tcp_echoclient,
},
{
"name": "tcpecho-asyncio-sockets",
"title": "TCP echo server (asyncio/sockets)",
Expand Down Expand Up @@ -245,6 +278,37 @@ BENCHMARKS_DEF: Final[Sequence[_BenchmarkDef]] = (
},
"client": _tcp_echoclient,
},
{
"name": "tcpecho-trio-sockets",
"title": "TCP echo server (trio/sockets)",
"server": (
*_python_cmd,
"/usr/src/servers/trio_tcp_echoserver.py",
f"--port={EXPOSED_PORT}",
),
"ping": {
"server_address": _tcp_server_address,
"ping_request": b"ping\n",
"socket_type": SOCK_STREAM,
},
"client": _tcp_echoclient,
},
{
"name": "tcpecho-trio-streams",
"title": "TCP echo server (trio/streams)",
"server": (
*_python_cmd,
"/usr/src/servers/trio_tcp_echoserver.py",
f"--port={EXPOSED_PORT}",
"--streams",
),
"ping": {
"server_address": _tcp_server_address,
"ping_request": b"ping\n",
"socket_type": SOCK_STREAM,
},
"client": _tcp_echoclient,
},
##############################################################################
################################ TCP readline ################################
##############################################################################
Expand Down Expand Up @@ -281,6 +345,23 @@ BENCHMARKS_DEF: Final[Sequence[_BenchmarkDef]] = (
},
"client": _tcp_readline_client,
},
{
"name": "readline-easynetwork-trio",
"title": "TCP readline server (easynetwork+trio)",
"server": (
*_python_cmd,
"/usr/src/servers/easynetwork_tcp_echoserver.py",
f"--port={EXPOSED_PORT}",
"--readline",
"--trio",
),
"ping": {
"server_address": _tcp_server_address,
"ping_request": b"ping\n",
"socket_type": SOCK_STREAM,
},
"client": _tcp_readline_client,
},
{
"name": "readline-easynetwork-buffered-asyncio",
"title": "TCP readline server (easynetwork+buffered+asyncio)",
Expand Down Expand Up @@ -316,6 +397,24 @@ BENCHMARKS_DEF: Final[Sequence[_BenchmarkDef]] = (
},
"client": _tcp_readline_client,
},
{
"name": "readline-easynetwork-buffered-trio",
"title": "TCP readline server (easynetwork+buffered+trio)",
"server": (
*_python_cmd,
"/usr/src/servers/easynetwork_tcp_echoserver.py",
f"--port={EXPOSED_PORT}",
"--readline",
"--buffered",
"--trio",
),
"ping": {
"server_address": _tcp_server_address,
"ping_request": b"ping\n",
"socket_type": SOCK_STREAM,
},
"client": _tcp_readline_client,
},
{
"name": "readline-asyncio-streams",
"title": "TCP readline server (asyncio/streams)",
Expand Down Expand Up @@ -387,6 +486,24 @@ BENCHMARKS_DEF: Final[Sequence[_BenchmarkDef]] = (
},
"client": _ssl_over_tcp_echoclient,
},
{
"name": "sslecho-easynetwork-trio",
"title": "TCP+SSL echo server (easynetwork+trio)",
"server": (
*_python_cmd,
"/usr/src/servers/easynetwork_tcp_echoserver.py",
f"--port={EXPOSED_PORT}",
"--ssl",
"--trio",
),
"ping": {
"server_address": _tcp_server_address,
"ping_request": b"ping\n",
"socket_type": SOCK_STREAM,
"ssl": True,
},
"client": _ssl_over_tcp_echoclient,
},
{
"name": "sslecho-easynetwork-buffered-asyncio",
"title": "TCP+SSL echo server (easynetwork+buffered+asyncio)",
Expand Down Expand Up @@ -424,6 +541,25 @@ BENCHMARKS_DEF: Final[Sequence[_BenchmarkDef]] = (
},
"client": _ssl_over_tcp_echoclient,
},
{
"name": "sslecho-easynetwork-buffered-trio",
"title": "TCP+SSL echo server (easynetwork+buffered+trio)",
"server": (
*_python_cmd,
"/usr/src/servers/easynetwork_tcp_echoserver.py",
f"--port={EXPOSED_PORT}",
"--ssl",
"--buffered",
"--trio",
),
"ping": {
"server_address": _tcp_server_address,
"ping_request": b"ping\n",
"socket_type": SOCK_STREAM,
"ssl": True,
},
"client": _ssl_over_tcp_echoclient,
},
{
"name": "sslecho-asyncio-streams",
"title": "TCP+SSL echo server (asyncio/streams)",
Expand Down Expand Up @@ -461,6 +597,24 @@ BENCHMARKS_DEF: Final[Sequence[_BenchmarkDef]] = (
},
"client": _ssl_over_tcp_echoclient,
},
{
"name": "sslecho-trio-streams",
"title": "TCP+SSL echo server (trio/streams)",
"server": (
*_python_cmd,
"/usr/src/servers/trio_tcp_echoserver.py",
f"--port={EXPOSED_PORT}",
"--ssl",
"--streams",
),
"ping": {
"server_address": _tcp_server_address,
"ping_request": b"ping\n",
"socket_type": SOCK_STREAM,
"ssl": True,
},
"client": _ssl_over_tcp_echoclient,
},
##########################################################################
################################ UDP echo ################################
##########################################################################
Expand Down Expand Up @@ -495,6 +649,22 @@ BENCHMARKS_DEF: Final[Sequence[_BenchmarkDef]] = (
},
"client": _udp_echoclient,
},
{
"name": "udpecho-easynetwork-trio",
"title": "UDP echo server (easynetwork+trio)",
"server": (
*_python_cmd,
"/usr/src/servers/easynetwork_udp_echoserver.py",
f"--port={EXPOSED_PORT}",
"--trio",
),
"ping": {
"server_address": _udp_server_address,
"ping_request": b"ping",
"socket_type": SOCK_DGRAM,
},
"client": _udp_echoclient,
},
{
"name": "udpecho-asyncio-sockets",
"title": "UDP echo server (asyncio/sockets)",
Expand Down Expand Up @@ -561,6 +731,21 @@ BENCHMARKS_DEF: Final[Sequence[_BenchmarkDef]] = (
},
"client": _udp_echoclient,
},
{
"name": "udpecho-trio-sockets",
"title": "UDP echo server (trio/sockets)",
"server": (
*_python_cmd,
"/usr/src/servers/trio_udp_echoserver.py",
f"--port={EXPOSED_PORT}",
),
"ping": {
"server_address": _udp_server_address,
"ping_request": b"ping",
"socket_type": SOCK_DGRAM,
},
"client": _udp_echoclient,
},
)


Expand Down Expand Up @@ -604,7 +789,6 @@ def _start_docker_instance(
server_cmd,
name=container_name,
remove=True,
tty=True,
detach=True,
ports=ports,
),
Expand Down Expand Up @@ -829,7 +1013,7 @@ def main() -> None:

print("Warming up server...")
warmup_cmd = benchmark["client"] + warmup
print(" ".join(warmup_cmd))
print(shlex.join(warmup_cmd))
subprocess.check_output(warmup_cmd)
print()

Expand Down
20 changes: 15 additions & 5 deletions benchmark_server/servers/asyncio_tcp_echoserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,14 @@ async def _echo_client(loop: asyncio.AbstractEventLoop, client: socket.socket, a
except (OSError, NameError):
pass

lock = asyncio.Lock()
with client:
while True:
data = await loop.sock_recv(client, 102400)
if not data:
break
await loop.sock_sendall(client, data)
async with lock:
await loop.sock_sendall(client, data)
LOGGER.info(f"{addr}: Connection closed")


Expand All @@ -55,14 +57,18 @@ async def echo_client_streams(reader: asyncio.StreamReader, writer: asyncio.Stre
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True)
except (OSError, NameError):
pass
writer.transport.set_write_buffer_limits(0)
LOGGER.info(f"Connection from {addr}")

lock = asyncio.Lock()
with contextlib.closing(writer):
while True:
data = await reader.read(102400)
if not data:
break
writer.write(data)
await writer.drain()
async with lock:
writer.write(data)
await writer.drain()
LOGGER.info(f"{addr}: Connection closed")


Expand All @@ -73,14 +79,18 @@ async def readline_client_streams(reader: asyncio.StreamReader, writer: asyncio.
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True)
except (OSError, NameError):
pass
writer.transport.set_write_buffer_limits(0)
LOGGER.info(f"Connection from {addr}")

lock = asyncio.Lock()
with contextlib.closing(writer):
while True:
data = await reader.readline()
if not data:
break
writer.write(data)
await writer.drain()
async with lock:
writer.write(data)
await writer.drain()
LOGGER.info(f"{addr}: Connection closed")


Expand Down
Loading

0 comments on commit fa15bff

Please sign in to comment.