@LakeForest

Почему gRPC сервис при нагрузке выдает 1 RPS, при том что сервис запущен с max_workers=10?

Я написал тестовый сервис для того, чтобы убедиться, что сервис выполняет запросы параллельно:

class MyService(test_pb2_grpc.MyService):
    async def Method(
        self, request, context: grpc.ServicerContext
    ):
        time.sleep(1)
        return test_pb2.Response()

async def run_serve():
    server = grpc.aio.server(
        futures.ThreadPoolExecutor(max_workers=10)
    )
    test_pb2_grpc.add_MyServiceServicer_to_server(
        MyService(), server
    )
    server.add_insecure_port("0.0.0.0:9000")
    await server.start()
    await server.wait_for_termination()

if __name__ == "__main__":
    asyncio.run(run_serve())

Тестировал через locust с нагрузкой 50u/s ,50 секунд.
Результаты:
RPS=1, средняя скорость ответа = 25 секунд, запросов обработал 50.
Ожидал:
RPS=10, запросов обработано >~500.

Подскажите, пожалуйста, в чем проблема? Почему так?
UPD:
А понял, это для задач отличных от async-io, но как тогда правильно написать time.sleep(1), чтобы ощущался прирост скорости за счет async?
  • Вопрос задан
  • 121 просмотр
Решения вопроса 1
@bacon
но как тогда правильно написать time.sleep(1), чтобы ощущался прирост скорости за счет async?
открой доки по asyncio, и узнаешь что там есть неблокирующий вариант time.sleep, это вообще базовые знания, которые ты почему-то не хочешь изучить.
Но именно в данном конкретном случае, это может и не помочь, потому что кроме всего прочего, надо разбираться в этой конкретной библиотеки для gRPC, контекста и нюансов там может быть много.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@rPman
Совершенно непонятно, как именно отрабатывает асинхронный сервер asyncio с вашим thread pool, я так понимаю это скрывается в дебрях класса объекта test_pb2_grpc? Нужно больше информации.

если к правильно настроенному 10-поточному серверу попытаться одновременно сделать больше 10 подключений, то все лишние будут откладываться в очередь (длина которой отдельный разговор, но при ее превышении сервер должен возвращать ошибки) и копиться там, т.е. с какой бы скоростью запросы к серверу не шли бы, отрабатывать они должны именно так - 10 запросов, каждый по 1 секунде, т.е. 10 запросов в секунду.

Для начала смотри логи и временные метки ответов клиента и запуска sleep на сервере (добавь строчку вывода в лог - sleep запущен/завершен с отладочным идентификатором запроса клиента, который добавь в http запрос), тогда ты будешь видеть время жизни каждого подключения клиента, сколько он висел в очереди asyncio и когда завершился на сервере, возможно тут что то прояснится.

Тут будет что то типа такого - первые 10 запросов (в течении первых 0.2 секунды) запустятся на обработку и завершатся каждый ровно через 1 сек, все последующие тупо будут складываться в очередь, по завершению очередного запроса, т.е. каждый следующий исполненный запрос будет завершаться через 0.02..0.2 секунды,группами, (при условии что запросы равномерны по времени), в порядке отправки этих запросов.

Вести отладку многопоточных приложений напрямую сложно (ведь пауза вносит изменения в структуру и порядок запросов), только тщательным логированием
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы