You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Phoenix PubSub shout事件重复触发问题咨询

解决Phoenix频道shout事件多次触发的问题

问题根源

你的代码逻辑存在重复推送问题:

  • 每个客户端连接到world:lobby频道时,都会订阅zone_1 PubSub话题
  • 当任意客户端发送shout消息,handle_in会通过PubSub把消息广播给所有订阅zone_1的客户端进程
  • 每个客户端进程的handle_info收到消息后,又执行broadcast(socket, "shout", ...),将消息再次广播到整个world:lobby频道
  • 最终每个客户端会收到N次消息(N等于当前连接的客户端总数),导致shout事件多次触发

解决方案

你不需要在handle_info里广播到频道,而是直接把PubSub收到的消息推送给当前客户端即可。因为PubSub已经把消息分发到了每个订阅的客户端进程,每个进程只需要把消息推送给自己的前端,无需再广播给整个频道:

def join("world:lobby", _message, socket) do
  PubSub.subscribe(Rotb.PubSub, "zone_1")
  {:ok, socket}
end

def handle_info(%{message: message}, socket) do
  # 用push代替broadcast,仅推送给当前客户端
  push(socket, "shout", %{"socket_id" => socket.id, "message" => message})
  {:noreply, socket}
end

def handle_in("shout", payload, socket) do
  PubSub.broadcast(Rotb.PubSub, "zone_1", %{
    message: payload["message"]
  })
  {:noreply, socket}
end

更优简化写法(仅频道内广播场景)

如果你的消息只需要在world:lobby频道内传递,完全可以跳过PubSub中转,直接在handle_in里广播到频道,逻辑更简洁且不会出现重复问题:

def join("world:lobby", _message, socket) do
  {:ok, socket}
end

def handle_in("shout", payload, socket) do
  broadcast(socket, "shout", %{"socket_id" => socket.id, "message" => payload["message"]})
  {:noreply, socket}
end

# 移除多余的handle_info和PubSub订阅逻辑

内容的提问来源于stack exchange,提问作者bezzoon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 19:42:52