Rails7.1+SolidCable实时聊天消息重复发送问题排查
Rails 7.1 + Solid Cable 实时聊天重复消息问题
我基于Rails 7.1开发项目,使用solid_cable gem实现实时聊天功能。测试时发现:当user1发送消息时,自身聊天窗口会显示两条重复消息,而user2仅收到一条。查看日志发现Turbo::StreamsChannel重复传输了消息,这应该是user1端消息重复显示的原因。
相关信息
- 已确认DOM及代码中仅存在一个
<%= turbo_stream_from "booking_#{@booking.id}_messages" %> - 在Message模型中通过
after_create_commit钩子执行消息广播:
class Message < ApplicationRecord belongs_to :user belongs_to :booking after_create_commit :broadcast_message private def broadcast_message puts "Broadcasting message...from message model" broadcast_append_to "booking_#{booking.id}_messages", partial: "messages/message", locals: { message: self, user: user }, target: "messages" end end
日志信息
SolidCable::Message Load (0.3ms) SELECT "solid_cable_messages".* FROM "solid_cable_messages" WHERE "solid_cable_messages"."channel" = $1 AND "solid_cable_messages"."id" >= $2 ORDER BY "solid_cable_messages"."id" ASC [["channel", "booking_39_messages"], ["id", 23]] Turbo::StreamsChannel transmitting "<turbo-stream action=\"append\" target=\"messages\"><template><div class=\"mb-4 text-end\">\n <div\n id=\"message-32\"\n class=\"message sent\"\n data-controller=\"message\"\n data-chat-target=\"message-content\"\n data-message-user-id-value=\"34\">\n <p class=\"mb-0\">test14<... (via streamed from booking_39_messages) Turbo::StreamsChannel transmitting "<turbo-stream action=\"append\" target=\"messages\"><template><div class=\"mb-4 text-end\">\n <div\n id=\"message-32\"\n class=\"message sent\"\n data-controller=\"message\"\n data-chat-target=\"message-content\"\n data-message-user-id-value=\"34\">\n <p class=\"mb-0\">test14<... (via streamed from booking_39_messages)
界面情况
- user1界面(显示重复消息):

- user2界面(显示单条消息):

期望
消息仅广播一次,发送消息的用户窗口仅显示一条消息。
相关代码
MessagesController
class MessagesController < ApplicationController def create @booking = Booking.find(params[:booking_id]) @message = Message.new(message_params) @message.booking = @booking @message.user = current_user authorize @message if @message.save respond_to do |format| format.turbo_stream do render turbo_stream: turbo_stream.append(:messages, partial: "messages/message", locals: { message: @message, user: current_user }) end format.html { redirect_to booking_path(@booking) } end else render "bookings/show", status: :unprocessable_entity end end private def message_params params.require(:message).permit(:content) end end
视图代码
booking#show
.... <%= render "chat", booking:@booking, message: @message %> ....
chat 局部视图
<div class="col-lg-6"> <%= turbo_stream_from "booking_#{@booking.id}_messages" %> <div class="my-3 p-5 bg-white rounded shadow-sm position-sticky" style="top: 1rem;"> <!-- 聊天头部 --> <div class="border-bottom pb-3 mb-4"> <h2 class="fs-5 mb-1"><%= @flat.host.name if @flat.present? %></h2> <p class="text-muted small mb-0">房东</p> </div> <div id="messages" style="height: 400px; overflow-y: auto;"> <% @booking.messages.each do |message| %> <%= render "messages/message", message: message, user: current_user %> <% end %> </div> <!-- 消息输入区域 --> <%= simple_form_for [@booking, @message], html: {class: "d-flex", data: { controller: "reset-form", action: "turbo:submit-end->reset-form#reset" }} do |f| %> <%= f.input :content, label: false, placeholder: t("booking.chat.send_message"), wrapper_html: {class: "flex-grow-1"} %> <%= f.submit "发送", class: "btn btn-primary mb-3" %> <% end %> </div> </div>
message 局部视图(原版本)
<div class="mb-4 <%= message.user === user ? "text-end": "text-start" %>"> <div id="message-<%= message.id %>" class="message <%= message.user === user ? 'sent' : 'received' %>" data-controller="message" data-chat-target="message-content" data-message-user-id-value="<%= message.user.id %>"> <p class="mb-0"><%= message.content %></p> </div> <small class="text-muted"><%= message.created_at.strftime("%a %b %e at %l:%M %p") %></small> </div>
message 局部视图(修改后)
<div id="message-<%= message.id %>" class="mb-4 message <%= message.user === user ? 'sent' : 'received' %> <%= message.user === user ? "text-end": "text-start" %>", data-controller="message" data-chat-target="message-content" data-message-user-id-value="<%= message.user.id %>"> <p class="mb-0"><%= message.content %></p> <small class="<%= message.user === user ? 'text-gray-400' : 'text-gray-600' %> font-weight-light fs-9"><%= message.created_at.strftime("%a %b %e at %l:%M %p") %></small> </div>
内容的提问来源于stack exchange,提问作者Kaline
相关产品推荐
相关产品推荐

