2026, Jan 12 06:01
Как переиспользовать одну requests.Session в Flask 3.0 без утечек
Flask 3.0: как переиспользовать одну requests.Session для внешнего REST API и корректно закрывать её при остановке через before_serving/after_serving без утечек
Создавая микросервис на Flask 3.0 и Python 3.12, который почти при каждом запросе обращается к внешнему REST API, легко наткнуться на проблемы с производительностью и стабильностью. Если в каждом роуте создавать собственную requests.Session, ответы замедляются, а под нагрузкой появляются утечки сокетов. Переход на простую глобальную сессию кажется заманчивым, но вызывает предупреждение о ресурсах и всё равно не проясняет управление жизненным циклом. Задача очевидна: переиспользовать одну requests.Session для всех входящих запросов и закрыть её ровно один раз при остановке приложения.
Проблемный код
Шаблон ниже создаёт новую сессию на каждый запрос — это медленно и под нагрузкой приводит к утечкам сокетов.
from flask import Flask, jsonify
import requests
svc = Flask(__name__)
@svc.get("/info")
def fetch_info():
with requests.Session() as http:
resp = http.get("https://api.example.com/info")
return jsonify(resp.json())
Что на самом деле происходит
Каждый вызов роута поднимает новую requests.Session и тут же её уничтожает — это неэффективно и под нагрузкой приводит к утечкам сокетов. Замена на обычный глобальный объект сессии вызывает предупреждение о ресурсах. Иными словами, ни создание на каждый запрос, ни неуправляемая глобальная переменная не решают задачу безопасного переиспользования и предсказуемого завершения.
Решение
Используйте хуки жизненного цикла сервера Flask, которые срабатывают в нужные моменты. Хук @app.before_serving выполняется один раз на воркер перед обработкой первого запроса — это подходящее время создать общую requests.Session. Хук @app.after_serving запускается один раз при корректном завершении — именно тогда следует закрыть эту сессию ровно один раз. Храните сессию на объекте приложения, чтобы она была доступна всем роутам в том же воркере.
Исправленный код
from flask import Flask, jsonify
import requests
service = Flask(__name__)
@service.before_serving
def prepare_http_client():
service.shared_session = requests.Session()
@service.after_serving
def teardown_http_client():
service.shared_session.close()
@service.get("/info")
def read_info():
reply = service.shared_session.get("https://api.example.com/info")
return jsonify(reply.json())
Почему это важно
Такой подход переиспользует одну сессию для всех запросов в пределах воркера, избавляет от накладных расходов на каждый запрос, замедляющих ответы, и предотвращает утечки сокетов под нагрузкой. Кроме того, он устраняет предупреждение о ресурсах, возникающее при неуправляемом глобальном объекте: срок жизни сессии привязан к жизненному циклу приложения — создаётся один раз перед началом обслуживания и закрывается один раз при корректной остановке.
Итоги
Если сервис на Flask 3.0 часто обращается к внешнему REST API, не создавайте новую requests.Session в каждом роуте и избегайте глобальных объектов без области ответственности. Разместите сессию на объекте приложения и управляйте ею через @app.before_serving и @app.after_serving. Это гарантирует предсказуемое переиспользование и единственное корректное закрытие при остановке процесса.