Лучшая практика push-уведомлений в vaadin и весенней загрузке

Я работаю над проектом, который использует vaadin в качестве внешнего интерфейса и Spring-boot для внутреннего интерфейса. В этом приложении у меня есть определенный вариант использования, когда группа пользователей (операторов) работает на разных станциях, где они могут генерировать события, о которых должна быть уведомлена другая другая группа пользователей (супевизоров), но только для станций, которым они назначены. Как правило, это не имеет большого значения, но я хочу использовать веб-push-уведомления, чтобы уведомлять руководителей, когда происходит новое событие.

Я начал немного читать об API уведомлений и удалось создать представление в vaadin, которое проверяет, было ли браузеру предоставлено разрешение на отправку уведомлений (в моем примере в Windows 10), и если разрешение еще не предоставлено, отобразить кнопку, чтобы запросить это. если разрешение было предоставлено, кнопка будет скрыта, а метка будет подтверждать, что разрешение было предоставлено. одновременно отправляется тестовое уведомление (чтобы я мог проверить, работает ли оно). Пока все работает отлично.

Я реализовал все, используя смесь java / vaadin и собственного java-скрипта

window.sendNotification = function(element, body, icon, title, type, id) {
    var options = {
        body: body,
        icon: icon
    }
    var notification = new Notification(title, options);
    notification.onclick = (e) => element.$server.notificationClicked(type, id);
}


window.checkNotificationPermission = function() {
    return Notification.permission;
}

window.askNotificationPermission = function(element) {
    function handlePermission(permission) {
        element.$server.permissionAsked(permission);
    }

    // Let's check if the browser supports notifications
    if (!('Notification' in window)) {
        console.log("This browser does not support notifications.");
    } else {
        if (checkNotificationPromise()) {
            Notification.requestPermission()
                .then((permission) => {
                    handlePermission(permission);
                })
        } else {
            Notification.requestPermission(function(permission) {
                handlePermission(permission);
            });
        }
    }
}
function checkNotificationPromise() {
    // safari supported permission or all other browsers?
    try {
        Notification.requestPermission().then();
    } catch (e) {
        return false;
    }

    return true;
}

Это сторона js, в java / vaadin я аннотировал представление с помощью @PWA (используя сгенерированный sw.js и манифест), @Push и @JsModule для включения кода js. Кроме того, я вызываю каждую функцию js с помощью page.executeJs (), например, так:

    Page page = UI.getCurrent().getPage();
    page.executeJs("askNotificationPermission($0)", this.getElement());

Я также предоставляю методы обратного вызова, которые обрабатывают, когда разрешение было предоставлено или когда было щелкнуто уведомление. Например:

@ClientCallable
private void permissionAsked(String permission) {
    sendSuccessNotification();
    permissionChecked(permission);
}

пока здесь все работало нормально.

Теперь моя большая проблема заключается в том, как я могу автоматически уведомлять каждого пользователя (некоторые из них могут даже войти в систему на разных машинах, например, на ПК и смартфоне). Я подумал о том, чтобы запустить другой поток в представлении и позволить ему опрашивать серверную часть на предмет новых уведомлений:

нравится:

@Override
protected void onAttach(AttachEvent attachEvent) {
    // Start the data feed thread
    thread = new NotificationThread();
    thread.start();
}

@Override
protected void onDetach(DetachEvent detachEvent) {
    // Cleanup
    thread.interrupt();
    thread = null;
}

private class NotificationThread extends Thread {

    @Override
    public void run() {
        try {
            while (true) {
                Thread.sleep(10000); // update every 10 seconds

                List<ResponseNotification> findNewNotifications = notificationController.findNewNotifications();
                if (findNewNotifications.size() > 1) {
                    sendGeneralNotification("You have new notifications", "New Notifications!", NavigationTarget.NOTIFICATIONS, "");
                } else if (findNewNotifications.size() == 1) {
                    sendGeneralNotification("An operator requires your attention", "Operator", NavigationTarget.OPERATOR, notification.operator.id);
                }
            }
        } catch (InterruptedException e) {
            // ...
            // handle 
        }
    }
}

Самая большая проблема в этом подходе заключается в том, что аутентификация больше не доступна в бэкэнде, так как она была вызвана из другого потока, и поэтому SecurityContextHolder.getContext().getAuthentication() вернет null. Но я также не уверен, что общий подход является разумным, поскольку представление всегда должно быть открытым, и я чувствую, что опрос каждые 10 секунд может привести к ненужной нагрузке на серверную часть, когда будет больше, чем просто группа супервизоров.

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

заранее спасибо!


person tagtraeumer    schedule 04.05.2021    source источник
comment
Вы проверили этот другой вопрос: stackoverflow.com / questions / 67356031 /   -  person Tatu Lund    schedule 04.05.2021
comment
@TatuLund, спасибо за подсказку, и нет, во время моего исследования этого не произошло. это могло быть первое решение, но будет ли оно правильным? что, если vaadin будет службой без сохранения состояния, работающей отдельно от серверной части и обращающейся к ней через rest. как можно подтвердить логин?   -  person tagtraeumer    schedule 04.05.2021
comment
Vaadin Flow (не Fusion) не может работать без состояния - он всегда будет запускать сеанс, потому что он содержит граф сцены для каждого пользовательского интерфейса. У вас всегда должна быть связь между вашим пользовательским интерфейсом / клиентом / пользователем и источником события. Если вы проводите опрос, вы можете создать для них токен с длительным сроком действия. Или вы можете доверять своей службе vaadin и отправлять все обновления или разрешить опрос их всех на предмет эффективности, а затем отправить, например, имя пользователя, например сверните свой собственный паб / саб. Тогда, конечно, вы можете использовать какой-нибудь установленный pub / sub. Нет никаких серебряных пуль.   -  person cfrick    schedule 04.05.2021
comment
Фактически, Vaadin push использует веб-сокет.   -  person Jean-Christophe Gueriaud    schedule 05.05.2021
comment
Websocket - это протокол с отслеживанием состояния. REST не имеет состояния, я не думаю, что вы можете отправлять уведомления с помощью служб REST.   -  person Jean-Christophe Gueriaud    schedule 05.05.2021


Ответы (1)


Если вам нужно уведомить пользователей, даже когда приложение не открыто, вам нужно заглянуть в Web Push. К сожалению, для этого требуется специальное (но похожее) решение в Safari, и оно недоступно в iOS, что основная причина, по которой у Vaadin пока нет встроенного решения. Маркус Хеллберг несколько лет назад сделал прототип, который может дать некоторые подсказки.

Чтобы уведомить пользователей, у которых в настоящее время открыты приложения, подумайте, применимо ли это в вашем случае: вместо того, чтобы каждый пользователь опрашивал соответствующие события каждые 10 секунд, подумайте о «подписке» на события, касающиеся каждой станции, которая интересует пользователя, а затем отправьте «Событие» для всех подписчиков по мере того, как событие происходит. Пользователи будут оставаться подписанными, пока они вошли в систему, поэтому поток публикации не должен отслеживать разрешения.

Это требует, чтобы вы использовали какую-то шину событий для механизма pub / sub, и для этого вы можете посмотреть Collaboration Engine - в основном потому, что он интегрирован с Vaadin и предоставляет другие функции совместной работы, которые могут оказаться полезными (например, журнал событий или чат на каждой станции, чтобы каждый оператор мог добавить некоторые дополнительные сведения) . Он наиболее эффективен, когда вы используете высокоуровневые API и компоненты, но вы можете использовать низкоуровневый API, чтобы делать много изящных вещей.

Есть несколько способов настроить это, но, например, создайте тему под названием «уведомления», используйте CollaborativeMap для обмена обновлениями для каждой станции. (Или вы можете создать отдельную тему для каждой станции, в зависимости от ваших потребностей.)

Примерно так для каждого пользователя:

CollaborationEngine.getInstance().openTopicConnection(this, "notifications", localUser, topic -> {
            CollaborationMap stations = topic.getNamedMap("stations");
            return stations.subscribe(event -> {
                if (MY_STATIONS.contains(event.getKey())) {
                    Notification notification = new Notification(event.getValue(String.class));
                    notification.setDuration(5000);
                    notification.open();
                }
            });
        });

Затем, когда есть обновление о станции, получите карту станции таким же образом, но обновите ее:

stationMap.put(STATION_ID,  STATION_ID + “: An update at " + new Date());

Это, конечно, граничит с кратким псевдокодом, поскольку это может быть не совсем то, что вы ищете, но код действительно создал рабочий пример:  Демонстрация: отправка уведомлений Collaboration Engine

person Marc Englund    schedule 05.05.2021
comment
Я также только что понял, что в Поваренной книге есть два подходящих примера: cookbook.vaadin.com/notify-users и cookbook.vaadin.com/native-notification - person Marc Englund; 05.05.2021
comment
спасибо за отличный ответ. Я определенно собираюсь провести дополнительные исследования в этом направлении. очень полезно, спасибо - person tagtraeumer; 06.05.2021