tractor/examples/multihost/server.py

92 lines
3.0 KiB
Python

import httpx
import tractor
import trio
log = tractor.log.get_console_log(
_root_name='my_app',
name='server_thingy',
)
@tractor.context
async def proxy_request(
ctx: tractor.Context,
address: str,
):
log.info(
'Rxed client request\n'
f'{address}\n'
)
async with httpx.AsyncClient() as client:
await ctx.started() # signal the remote task has started its client
log.info(
'Opened `httpx` client..'
)
resp: httpx.Response = await client.get(address) # do the proxied request, get response.
log.info(
'Got response..\n'
f'{resp}\n'
)
# only breaking this up to clarify that you didn't have to only return a single result you could have opened
# a long lived stream to avoid task spawning overhead in this service actor.. but more on that later..
#
# NOTEs, cast to `str` here since we can't serialize the
# response type for the wire directly, at least no without
# a custom `msgspec.Decoder`!!
return str(resp)
# return resp
# ^TODO, various typed msging options:
# -[ ] try returning just the `resp` verbatim => should raise
# an MTE
# -[ ] try defining a custom `Response` msg to proxy the orig
# types fields and/or a decoder to serialize it?
async def main():
# enable console logging for our custom app's logger
tractor.log.get_console_log(
level='info',
_root_name='my_app',
name='server_thingy',
)
# since (originally) this is run as a script, we will end up with
# `__name__ == '__main__'` so to ensure the rpc request from the
# client isn't blocked by `tractor.ModuleNotFound`, we want to just
# use the explicit file-as-module name.. why u ask?
this_mod: str = 'server'
# WELP, when the `Portal.open_context()` api (used in
# `client.py`) requests the RPC-ctx ep it will send
# a `str`-like-ptr encoding the func-ref in form expected by
# `pkgutil.resolve_name()`.
#
# Since the client's local namespace reference/path to this
# `.server.py` mod will be from a direct manual import, that
# `proxy_request()`-ref will render as `'server:proxy_request'`
# (as delivered from `NamespacePath.from_ref()` since that's how
# `.open_context()` serializes the func's-ref for IPC transit).
# SO, we need to be sure we "enable" this module name so that the
# nsp maps to an enabled module in the `Actor._mods: dict`.
async with tractor.open_root_actor(
name='web_proxier',
registry_addrs=[('127.0.0.1', 1616)],
enable_modules=[this_mod],
loglevel='info',
):
# just block waiting for a peer actor to connect and open an
# RPC context using the above proxy endpoint.
log.info(
'proxy server up bby!\n'
'waiting to serve some requests..\n'
)
await trio.sleep_forever()
if __name__ == '__main__':
trio.run(main)