其目标是允许用户实时更新在线考试的状态。(即按下激活查利的考试,改变查理斯的屏幕,允许他开始参加考试。监考者之间的关系是一个监考者对多个考试的关系。
目前我们使用sente成功地激活了考试,但是一旦我们点击了(activate exam)按钮,它就会一次又一次地通过"/chsk“路由发送请求。(值得称赞的是,它非常快。)在遇到以下错误之前,它成功地发送了许多这样的请求(10+)。我们认为问题出在中间件上,但在调整包装格式以处理websocket请求(以及通过"/chsk“路由发送的请求)后,我们仍然收到错误。我不认为问题真的出在中间件上,因为信息在第一次就可以很好地通过,并如预期的那样激活考试。我不知道为什么sente会发送多个请求。sente是为我们的目的而工作的,但我们需要以某种方式停止所有这些额外的请求,否则我的机器/互联网就会陷入困境。
如何确保一个请求只从后端发送一次?
Immediately after endless requests are made and the browser crashes soon after.
(客户端)
(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}]*注意:一旦注册了考试,我们就启动客户端路由器,并使用考试的id作为前端通道的用户id。
发布于 2020-12-03 04:30:39
原来是那个启动路由器!并且发送设置函数被多次调用。因此,我们添加了一个重帧标志,以便在发送连接后设置,然后执行chsk-send!仅从BE发送了一次请求。一个类似的想法是将函数调用设置为单例,这样如果考试会话已经启动,它就不会再次调用start路由器。
https://stackoverflow.com/questions/65013873
复制相似问题