Непрерывные запросы по каналу веб-сокета для того, что должно быть одним запросом

Цель состоит в том, чтобы позволить кому-либо обновлять статус онлайн-экзамена в режиме реального времени. (Например, нажатие «Активировать экзамен Чарли» меняет экран Чарли, позволяя ему начать сдавать экзамен. Отношения между проктором — один проктор ко многим экзаменам.

В настоящее время мы успешно активируем экзамен с помощью sente, но как только мы нажимаем кнопку (активировать экзамен), он продолжает снова и снова отправлять запросы по маршруту /chsk. (К чести Sente, это очень быстро.) Он успешно отправляет много (10+) таких запросов, прежде чем выдает следующую ошибку. Мы думали, что проблема будет в промежуточном программном обеспечении, но после настройки форматов обертки для обработки запросов веб-сокетов (и запросов, отправленных по маршруту /chsk), мы все еще получаем ошибку. Я не думаю, что проблема действительно связана с промежуточным программным обеспечением. , потому что информация проходит нормально с первого раза и активирует экзамен, как и ожидалось. Я не знаю, почему Sente вообще отправляет более одного запроса. Sente работает для наших целей, но нам нужно остановить все эти лишние запросы каким-то образом или моя машина/интернет увязнет.

Как мы можем убедиться, что запрос отправляется только один раз с бэкэнда?

(ПРОБЛЕМА) Я смог заморозить его, как только активировал экзамен, чтобы показать, что Интернет -socket действительно сделал то, что мы хотели.

Сразу после бесконечных запросов и вскоре после этого происходит сбой браузера.

Ошибка в REPL

(СТОРОНА КЛИЕНТА)

(ns flats.web-sockets
  (:require [goog.string :as gstring]
            [flats.shared :as shared]
            [reagent.core :as r]
            [re-frame.core :as rfc]
            [taoensso.encore :as encore :refer-macros (have)]
            [taoensso.sente  :as sente]
            [taoensso.timbre :as log :refer-macros (tracef)]))

(def ?csrf-token
  (when-let [el (.getElementById js/document "token")]
    (.getAttribute el "value")))

(def -chsk (r/atom nil))
(def -ch-chsk (r/atom nil))
(def -chsk-send! (r/atom nil))
(def -chsk-state (r/atom nil))

(defn sente-setup
  "Takes uid (exam-id, registration-id, user-id?) to use as unique client-id"
  [uid]
  (let [{:keys [chsk ch-recv send-fn state]}
        (sente/make-channel-socket-client! "/chsk" ; Note the same path as before
                                           ?csrf-token
                                           {:type :auto  ; e/o #{:auto :ajax :ws}
                                            :client-id uid})]
    (reset! -chsk       chsk) 
    (reset! -ch-chsk    ch-recv) ; ChannelSocket's receive channel
    (reset! -chsk-send! send-fn) ; ChannelSocket's send API fn
    (reset! -chsk-state state)   ; Watchable, read-only atom
    ))

(defmulti -event-msg-handler
  "Multimethod to handle Sente `event-msg`s"
  :id ; Dispatch on event-id
  )

(defn event-msg-handler
  "Wraps `-event-msg-handler` with logging, error catching, etc."
  [{:as ev-msg :keys [_id _?data _event]}]
  #_(log/info (str "\nin event-msg-handler: "ev-msg))
  (-event-msg-handler ev-msg))

(defmethod -event-msg-handler
  :default ; Default/fallback case (no other matching handler)
  [{:as _ev-msg :keys [event]}]
  (tracef "Unhandled event: %s" event))

(defmethod -event-msg-handler :chsk/state
  [{:as _ev-msg :keys [?data]}]
  #_(log/info "In chsk/state "_ev-msg)
  (let [[_old-state-map new-state-map] (have vector? ?data)]
    (if (:first-open? new-state-map)
      (tracef "Channel socket successfully established!: %s" new-state-map)
      (tracef "Channel socket state change: %s"              new-state-map))))

(defmethod -event-msg-handler :chsk/recv
  [{:as _ev-msg
    :keys [_?data]
    [event-id {:keys [exam-status]}]
    :?data}]
  (log/info (str ":chsk/recv payload: " _ev-msg))
  (when (= event-id :exam/status)
    (rfc/dispatch [:set-current-exam-status exam-status])))

(defmethod -event-msg-handler :chsk/handshake
  [{:as _ev-msg :keys [?data]}]
  (let [[_?uid _?csrf-token _?handshake-data] ?data]
    (tracef "Handshake: %s" ?data)))

;;;; Sente event router (our `event-msg-handler` loop)

(defonce router_ (atom nil))
(defn  stop-router! [] (when-let [stop-f @router_] (stop-f)))
(defn start-router! []
  (stop-router!)
  (reset! router_
          (sente/start-client-chsk-router!
           @-ch-chsk event-msg-handler)))

(СТОРОНА СЕРВЕРА)

(ns flats.web-sockets
  (:require [taoensso.timbre :as log]
            [taoensso.sente :as sente]
            [clojure.core.async :as async :refer [<!!]]
            [taoensso.sente.server-adapters.immutant :refer (get-sch-adapter)])
  (:import [java.util UUID]))

(def user-id (atom nil))

(let [chsk-server (sente/make-channel-socket-server!
                   (get-sch-adapter)
                   {:packer :edn
                    :user-id-fn (fn [request]
                                  (reset! user-id (:client-id request))
                                  (UUID/fromString (:client-id request))
                                  #_(:client-id request)
                                  )})
      {:keys [ch-recv send-fn connected-uids
              ajax-post-fn ajax-get-or-ws-handshake-fn]} chsk-server]
  (def ring-ajax-post                ajax-post-fn)
  (def ring-ajax-get-or-ws-handshake ajax-get-or-ws-handshake-fn)
  (def ch-chsk                       ch-recv) ; ChannelSocket's receive channel
  (def chsk-send!                    send-fn) ; ChannelSocket's send API fn
  (def connected-uids                connected-uids) ; Watchable, read-only atom
  )

;;;; Server>user async push

(defn broadcast!
  [registration-id exam-status]
  (chsk-send! registration-id
              [:exam/status {:exam-status exam-status}]
              5000))

(АКТИВАЦИЯ ЭКЗАМЕНА)

(defn create-exam-action
  "Creates a new `exam_action` with the given `exam-id` and the `action-type`"
  [{{:keys [exam-id action-type]} :path-params}]
  (let [exam-id (UUID/fromString exam-id)
        registration-id (dbx/READ exam-id [:registration-id])]
    (exams/create-exam-action exam-id action-type)
    (ws/broadcast! registration-id action-type) 
    (http-response/ok action-type)))

(МАРШРУТЫ)

["/chsk" {:get ws/ring-ajax-get-or-ws-handshake
             :post ws/ring-ajax-post}]

*Примечание. Мы запускаем маршрутизатор на стороне клиента после регистрации экзамена и используем идентификатор экзамена в качестве идентификатора пользователя для внешнего канала.


person ben.jamin2hard    schedule 25.11.2020    source источник


Ответы (1)


Получается, что старт-роутер! и функции sente-setup вызывались более одного раза. Таким образом, мы добавили флаг reframe, чтобы он был установлен после подключения sente, а затем chsk-send! отправлял запросы от BE только один раз. Аналогичная идея состоит в том, чтобы сделать так, чтобы функция вызывала синглтон, поэтому, если сессия уже запущена для экзамена, она не должна снова вызывать начальный маршрутизатор.

person ben.jamin2hard    schedule 02.12.2020