Сервер SSE отправляет события в пакете при окончательном закрытии

У меня есть сервер в Джерси, работающий локально, он предоставляет ресурс SSE, как в приведенных здесь примерах: https://jersey.github.io/documentation/latest/sse.html. У меня есть локальное приложение Angular для webpack, которое привязывается к открытой конечной точке GET и прослушивает данные. На GET я запускаю поток для отправки уведомлений с регулярными интервалами в течение 6-8 секунд. Я ничего не вижу на клиенте ДО закрытия объекта EventOutput.

Что я делаю не так и как это исправить?

Код сервера РАБОТАЕТ только с простым завитком, то есть: curl http://localhost:8002/api/v1/notify

Но как в Chrome, так и в Safari следующий код демонстрирует поведение

Клиент (TypeScript):

this.evSource = new EventSource('http://localhost:8002/api/v1/notify');
this.evSource.addEventListener(
  'event',
  (x => console.info('we have ', x))
);
this.evSource.onmessage = (data => console.info(data));
this.evSource.onopen = (data => console.info(data));
this.evSource.onerror = (data => {
  console.info(data);
  this.evSource.close();
});

Сервер (Java):

// cache callback
public void eventCallback(Iterable<CacheEntryEvent<? extends Integer, ? extends Integer>> events) {
    for (CacheEntryEvent<? extends Integer, ? extends Integer> x : events) {
        LOGGER.info("{} Sending the following value: " + x.getValue(), Thread.currentThread().getId());
        final OutboundEvent sseEvent = new OutboundEvent.Builder().name("event")
                .data(Integer.class, x.getValue()).build();
        this.broadcaster.broadcast(sseEvent);
    }
}

@GET
@Produces(SseFeature.SERVER_SENT_EVENTS)
@ApiOperation(value = "Setup SSE pipeline", notes = "Sets up the notification pipeline for clients to access")
@ApiResponses(value = {
        @ApiResponse(code = HttpURLConnection.HTTP_UNAUTHORIZED,
                message = "Missing, bad or untrusted cookie"),
        @ApiResponse(code = HttpURLConnection.HTTP_OK,
                message = "Events streamed successfully")
})
@Timed
@ResponseMetered
public EventOutput registerNotificationEvents(
        @HeaderParam(SseFeature.LAST_EVENT_ID_HEADER) String lastEventId,
        @QueryParam(SseFeature.LAST_EVENT_ID_HEADER) String lastEventIdQuery) {
    if (!Strings.isNullOrEmpty(lastEventId) || !Strings.isNullOrEmpty(lastEventIdQuery)) {
        LOGGER.info("Found Last-Event-ID header: {}", !Strings.isNullOrEmpty(lastEventId) ? lastEventId : lastEventIdQuery );
    }
    LOGGER.info("{} Received request", Thread.currentThread().getId());
    this.continuation = true;
    final EventOutput output = new EventOutput();

    broadcaster.add(output);

    Random rand = new Random();
    IntStream rndStream = IntStream.generate(() -> rand.nextInt(90));
    List<Integer> lottery = rndStream.limit(15).boxed().collect(Collectors.toList());
    IgniteCache<Integer, Integer> cache = this.ignite.cache(topic_name);

    executorService.execute(() -> {
        try {
            lottery.forEach(value -> {
                try {
                    TimeUnit.MILLISECONDS.sleep(500);
                    LOGGER.info("{} Sending the following value to Ignite: " + value + " : " + count++, Thread.currentThread().getId());
                    if (!cache.isClosed()) {
                        cache.put(1, value);
                    }
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            });
            TimeUnit.MILLISECONDS.sleep(500);
            continuation = false;
            TimeUnit.MILLISECONDS.sleep(500);
            if (!output.isClosed()) {
                // THIS is where the client sees ALL the data broadcast
                // in one shot
                output.close();
            }
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    });
    LOGGER.info("{} Completing request", Thread.currentThread().getId());
    return output;
    }
}
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
252
1

Ответы 1

Похоже, http://github.com/dropwizard/dropwizard/issues/1673 фиксирует проблему. По умолчанию GZip не сбрасывается, даже если этого требуют верхние уровни. Решение что-то вроде

((AbstractServerFactory)configuration.getServerFactory()).getGzipFilterFactory().setSyncFlush(true);

включит сброс для синхронизации с GZip, если отключение GZip all up не является вариантом

Другие вопросы по теме