@bitwheeze

Spring Webflux Как побороть 502 bad gateway?

У меня приложение, карточная игра. Фронтед next.js, Бэкэенд spring webflux. Между ними ngnix. На каждый клик UI обращается к серверу, что бы проверить ход игрока, получить новое состояние игры. Все вроде бы работало до сих пор. Могло свободно 4-5 игроков обслуживать. Больше игроков и нету. Я решил серверу добавить server side events, что бы UI могло с помощью eventSource получать сообщения от сервера и отображать их. Для начала добавил только два эвента - "начало игры" и "конец игры". Вообщем мелочи.

Для этого добавил такой эндпоинт

@GetMapping(path = "/events/{gamer}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> streamFlux(@PathVariable String gamer) {
        log.info("subscribe for {}", gamer);
        return service.subscribe(gamer);
    }


Ну и источник эвентов

final Sinks.Many<CloudEvent> sink = Sinks.many().replay().limit(0);

    @EventListener
    @SneakyThrows
    public void eventListener(GameEvent gameEvent) {
        log.info("occured an event {} {}", gameEvent, mapper.writeValueAsString(gameEvent));

        var event = CloudEventBuilder.v1()
                .withId(UUID.randomUUID().toString())
                .withType(gameEvent.getClass().getSimpleName())
                .withSource(URI.create("sol://some/uri"))
                .withSubject(gameEvent.getGamer())
                .withTime(OffsetDateTime.now(ZoneOffset.UTC))
                .withData ("application/json", mapper.writeValueAsString(gameEvent).getBytes(StandardCharsets.UTF_8))
                .build();

        sink.emitNext(event, Sinks.EmitFailureHandler.FAIL_FAST);
    }

    public Flux<String> subscribe(String gamer) {
        return sink.asFlux()
                .doOnNext((e) -> log.info("sent next event {}", e))
                .filter(event -> "*".equals(event.getSubject()) || event.getSubject().equals(gamer))
                .map(e -> new String(EventFormatProvider
                            .getInstance()
                            .resolveFormat(JsonFormat.CONTENT_TYPE)
                            .serialize(e)));
    }


По сути сервис реагирует на ApplicationEvent спринга, заворачивает их в CloudEvent, конвертирует к строке и пихает в синк. А с другой стороны синка эти эвенты получают подписчики, кто запустил /api/events/{gamer}.

У меня на локальной машине все вроде бы работает, никаких проблем. А вот на продуктивном сервере стали часто появляться 502 BadRequest ошибки. Их возвращает ngnix прокси. В бэкенде я никаких ошибок не вижу. Даже если никто не играет, просто открыт сайт и подключен eventSource.

Все запросы к /api периодически начинают возвращать 502, чуть ли не каждую минуту случается, потом секунды через три опять начинает само работать.

Пробовал запускать с трассировкой, ничего подозрительного не увидел. 502 и раньше случались, я просто не обращал внимания. Ну 100 в день, не больше. А после этого каждую минуту практически.

Вот конфигурация ngnix. Раньше было только /api, добавил /api/events. Добавил всяких опций, все, что нашел в интернетах

location /api/events {
                #limit_req zone=mylimit;
                proxy_pass http://localhost:9494;
                proxy_buffering off;
                proxy_cache off;
                proxy_set_header Connection '';
                proxy_http_version 1.1;
                chunked_transfer_encoding off;
        }

        location /api {
                #limit_req zone=mylimit;
                proxy_pass http://localhost:9494;
        }


netty специально не настраивал, только порт через server.port установлен. На сервере у меня есть всякие процессы, которые по Scheduled с разной периодичностью запускаются. Вот я на них грешу. Могут они как то netty блокировать? Или у netty connection pool как то забивается.

Спасибо за внимание

update

Я вообщем в UI клиенте закомментировал создание EventSource к точке /api/events/{gamer} и ошибки ушли. Почему запросы к это точке всё api блокируют, ума не приложу?

Вот так создаю EventSource

useEffect(() => {

        if(!account) {
          return;
        }
        console.log("read events", account, timestamp);
        const eventSource = new EventSource(sol_api + `/events/${account}`);
        eventSource.onerror = (e) => {console.log("Event Source Error!", e);eventSource.close();setError(true);};
        eventSource.onmessage = (e) => addEvent(JSON.parse(e.data));
        return () => {
          console.log("close eventSource", eventSource);
          eventSource.close();
        };
      }, [account, timestamp]);
  • Вопрос задан
  • 241 просмотр
Пригласить эксперта
Ваш ответ на вопрос

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

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