Ainvain
@Ainvain

Как параллельно обрабатывать API запросы?

Пытаюсь написать сервис для классификации текста (для чат-бота). Есть желание сделать его многопоточным, чтобы поднять производительность.
Насколько понял, waitress создает несколько потоков для обработки запросов. Но в запросах используется один и тот же объект classific (модель для классификации). При этом внутри объекта хранятся словари для котроля контекста диалога в рамках сессии (по session_id). Допустим, их можно вынести отдельно.

import argparse
import yaml
from waitress import serve
from flask import Flask, request
from flask_restful import Api, Resource, reqparse
app = Flask(__name__)
api = Api(app)

class Detect(Resource):
    def post(self):
        parser = reqparse.RequestParser()
        parser.add_argument("session_id")
        parser.add_argument("phrase")
        params = parser.parse_args()

        return classific.detect_intent(
            phrase = params['phrase'], 
            session_id = params['session_id']), 200
    
    def get(self):
        phrase = request.args.get('phrase')
        session_id = request.args.get('session_id')
        if phrase:
            return classific.detect_intent(
                phrase = phrase, 
                session_id = session_id), 200
        else:
            return "No phrase in request", 400

api.add_resource(Detect, '/', '/detect')
# часть кода опущена

if __name__ == '__main__':    
    args = parser.parse_args()
    lin_model_path: str = args.liner_model_path
    regexp_path: str = args.regexp_path    
    agent_name: str =  args.agent_name

    model = nlu.model(lin_model_path) if lin_model_path else None
    regexp_data = None
    if regexp_path:
        with open(regexp_path, mode='r', encoding='UTF-8') as file:
            regexp_data = yaml.safe_load(file)
    classific = Classificator(linear_model=model, regexp_patterns=regexp_data, agent_name=agent_name)    

    
    print('Запуск сервера')
    serve(app, host=args.host, port=args.port)


Как можно сделать так, чтобы каждый из потоков waitress использовал отдельный, заранее заданный экземпляр Classificator (копия classific), а изменяемые данные (контекст диалога) хранились в разделяемой переменной? Ещё, как вариант, запросы с одинаковым session_id могли бы обрабатываться в одном классификаторе, а разные сессии могли параллельно обрабатываться в разных потоках.
  • Вопрос задан
  • 137 просмотров
Пригласить эксперта
Ответы на вопрос 1
Vindicar
@Vindicar
RTFM!
Я бы сделал несколько по другому.
Оформил бы отдельно рабочий поток/процесс, и заставил бы их общаться через пару очередей (классический паттерн producer-consumer). Подходящие классы очереди можно найти в multiprocessing (ну или в threading, если ты очень хочешь потоки вместо процессов).

Основной (корневой) процесс запускает рабочие процессы, потом слушает входящие запросы. Получив запрос, кидает его в очередь. Первый свободный процесс извлекает запрос из очереди, обрабатывает, отправляет ответ на запрос, и снова ждёт появления элемента в очереди. Проблема в том, что при использовании процессов придётся передавать между ними сложный объект Request - я не уверен, что это будет просто. Можешь попробовать потоки, но в питоне потоки не всегда хорошо работают.

Нужно будет подумать над тем, где будут узкие места. Я вижу два - собственно работа классификатора, и обмен данными с клиентами. Первое ты обходишь, второе можно отчасти обойти через асинхронную работу (flask это умеет).
Ответ написан
Ваш ответ на вопрос

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

Похожие вопросы