Usage of the Client object #2191
-
Hi, I'm trying to understand how to use the Client object in the best way to minimize overhead with handshakes. As far as I understand the Client has a connection pool, which I made an analogy to SQLAlchemy In the examples the Client is used with a context manager to clean up the pool after the usage. Is it okay use a global (or class attribute) Client object that will be used multiple times for all request to a given host? WEATHER_SERVICE_CLIENT = Client(base_url="https://...")
class WeatherRepository:
def get_by_city_name(self, name: str) -> Weather:
response = WEATHER_SERVICE_CLIENT.get(f"/city?name={name}")
...
return Weather(response.json()) |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
At least in an async scenario we found using the context manager to wrap each request caused slowdown and noticeable cpu usage. Instantiating a single client and reusing it increased the app's performance many times over. I haven't used httpx in non-async but I would assume the same to be true. |
Beta Was this translation helpful? Give feedback.
-
Exactly correct, and a good analogy yes. Both cases involve a pool of network connections which are more efficient if they are maintained and reused.
Absolutely, yes. In a different universe Python web applications would have a structure that allows for global context. Perhaps something like this... @contextmanager
def application_lifespan():
"""
Handle any resources that are required across the lifespan of the application running.
"""
engine = sqlalchemy.create_engine(...)
with engine.connect() as database:
with httpx.Client() as client:
with MyWebAppFramework() as app:
app.database = database
app.client = client
yield app The benefit here is that you've got an explicit point in the codebase at which you can see where the resource setup/teardown occurs. However we don't actually see that pattern across the Python web app ecosystem, and instead we end up with global instances that have implicit setup and teardown cases. (Ie. the database connection pool will be established the first time we make a query, and will be closed on exit from the Python interpreter.) engine = sqlalchemy.create_engine(...)
client = httpx.Client()
... Which is, well... actually okay enough really. But it's less clear when the database connection pool actually gets established. And there's no explicit closing of the database connections or HTTP connection pool. We just rely on the Python environment to tear those down on exit. |
Beta Was this translation helpful? Give feedback.
Exactly correct, and a good analogy yes. Both cases involve a pool of network connections which are more efficient if they are maintained and reused.
Absolutely, yes.
In a different universe Python web applications would have a structure that allows for global context.
Perhaps something like this...