SSE закрывается, когда контроллер .net также обрабатывает POST.

Вопрос или проблема

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

Итак, у меня есть контроллер ASP.net core web api, который имеет конечную точку для обработки соединения SSE и дополнительную конечную точку для обработки POST-запроса (это позволяет пользователю отправлять сообщения).

Angular-приложение, которое я создал (обслуживается node и использует proxy.conf), устанавливает соединение EventSource с моим контроллером. Если я жестко закодирую первоначальный вопрос пользователя на сервере, чтобы мне не пришлось делать свой POST-запрос, SSE отправляет ответ LLM, как и ожидалось.

// Конечная точка для потоковой передачи сообщений в чате
[HttpGet("stream")]
public async Task GetChatMessagesAsStreamAsync()
{

    string prompt = """
        Вот мой чудесный запрос
        """;


    await _agentGroupChatService.AddChatMessage(new ChatRequest { Message= prompt });

    // Установить тип содержимого ответа для SSE
    HttpContext.Response.ContentType = "text/event-stream";

    // Потоковая передача сообщений из сервиса
    await foreach (var message in _agentGroupChatService.GetChatMessagesAsStreamAsync())
    {
        var messageJson = JsonSerializer.Serialize(message);
        // Записывать каждое сообщение в формате SSE
        await HttpContext.Response.WriteAsync($"data: {messageJson}\n\n");
        await HttpContext.Response.Body.FlushAsync(); // Сбросить, чтобы отправить сообщение немедленно


    }
}

НО… Если я создаю соединение SSE, а затем полагаюсь на POST-запрос для отправки начального сообщения, соединение SSE немедленно разрывается (отмечено как отмененное на клиенте).

Это отмена происходит до того, как выполнится конечная точка контроллера POST – она всё равно отвечает “ок”!

[HttpPost("message")]
public async Task<IActionResult> SendMessage([FromBody] ChatRequest chatRequest)
{
  
    return Ok(); // Ответ с кодом 200 OK
}

// Конечная точка для потоковой передачи сообщений в чате
[HttpGet("stream")]
public async Task GetChatMessagesAsStreamAsync()
{

    // Установить тип содержимого ответа для SSE
    HttpContext.Response.ContentType = "text/event-stream";

    // Потоковая передача сообщений из сервиса
    await foreach (var message in _agentGroupChatService.GetChatMessagesAsStreamAsync())
    {
        var messageJson = JsonSerializer.Serialize(message);
        // Записывать каждое сообщение в формате SSE
        await HttpContext.Response.WriteAsync($"data: {messageJson}\n\n");
        await HttpContext.Response.Body.FlushAsync(); // Сбросить, чтобы отправить сообщение немедленно


    }
}

На самом деле, если конечная точка контроллера POST просто возвращает OK, соединение SSE отменяется!

Кроме того, если данные сначала отправляются через поток событий, я могу отправить POST-запрос. Если никаких данных не было отправлено до POST, он не проходит. Есть идеи, что происходит?

Ответ или решение

Ваш вопрос касается проблемы с использованием однопоточного потокового SSE (Server-Sent Events) в микросервисе, реализованном на ASP.NET Core, где одновременно обрабатываются запросы Post для отправки сообщений. Давайте рассмотрим, почему происходит закрытие соединения SSE, когда вызывается POST запрос, и возможные способы решения этой проблемы.

Общее описание проблемы

Вы реализовали два контроллера в ASP.NET Core:

  1. GET запрос для SSE (GetChatMessagesAsStreamAsync), который использует потоковую передачу для отправки сообщений клиенту.
  2. POST запрос (SendMessage), который принимает сообщения от пользователя.

Основная проблема заключается в том, что при вызове отправки POST-запроса соединение SSE сразу закрывается, и клиент получает уведомление о том, что соединение отменено. Это происходит даже в тех случаях, когда вызов POST не возвращает никаких ошибок, а просто отдает статус 200 OK.

Возможные причины проблемы

  1. Конфликт потоков: SSE использует единый поток для передачи данных. Если во время установления соединения выполняется другой запрос, это может привести к конфликтам состояния, так как оба запроса могут пытаться взаимодействовать с одним и тем же ресурсом.

  2. Управление жизненным циклом соединения: Если сервер ожидает завершения одного запроса до начала другого, это может привести к закрытию одного из соединений. Особенно это касается ситуаций, когда сервер видит два активных дайджеста от одного и того же клиента.

  3. Настройки сервера: Некоторые настройки в конфигурации сервера или в рамках middleware могут вызывать отмену соединений при определенных условиях, например, при изменении состояния контекста HttpContext.

Рекомендации по решению проблемы

  1. Асинхронное ожидание: Убедитесь, что ваши методы обработки запросов реализованы как асинхронные, правильно используя ключевое слово await. Это позволит избежать блокировок и конфликтов в потоках.

  2. Проверка состояния соединения: Перед тем как выполнять POST-запрос, убедитесь, что соединение SSE действительно активно. Это поможет определить, не происходит ли автоматическое закрытие соединения из-за потери состояния.

  3. Использование Middleware для SSE: Попробуйте обернуть ваши потоковые запросы в middleware, который будет контролировать состояние соединения и управлять событиями. Это может помочь избежать неожиданных закрытий соединения.

  4. Изменение порядка событий: Рассмотрите возможность отправки первого сообщения через SSE перед отправкой POST-запроса. Вы уже упомянули, что это работает, и это может стать временным решением.

Заключение

Проблема с закрытием соединения SSE при отправке POST-запроса может быть связана с конфликтами в управлении потоками, состоянием соединения или неправильной конфигурацией сервера. Принятие мер, таких как асинхронная реализация ваших методов, лучшее управление состоянием соединения и использование middleware для контроля событий, может помочь в решении этой проблемы.

Рекомендуется протестировать каждое исправление на предмет того, принесло ли это улучшение, а также документировать изменения для будущих ссылок и для обеспечения более легкой отладки проблем в будущем.

Оцените материал
Добавить комментарий

Капча загружается...