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

Blazor中InstallServiceInfoReceived触发后OnServiceInfoReceived偶发未调用问题

相关代码片段

program.cs

builder.Services.AddScoped<ServiceInfoReceiveHandler>();

message_broker.cs

public MessageBroker(ServiceInfoReceiveHandler serviceInfoReceiveHandler)
{
    _handlers = new Dictionary<string, IReceiveHandler>
    {
        { "SVCMGMT/INSTALL/SVCINFO", serviceInfoReceiveHandler },
    };
    ReceiveMessage();
}
            
private void ReceiveMessage()
{           
    try
    {
        if (_handlers.ContainsKey(contentType))
        {
            _handlers[contentType].Receive(ea);
        }
        else
        {
            throw new Exception($"No handler for content type '{contentType}'");
        }
    }
}

receive.cs

if (contentType == "")
{
  InstallServiceInfoReceived?.Invoke(this, (contentType, serverName, message));
}

ServiceInfoReceiveHandler.cs

public class ServiceInfoReceiveHandler : IReceiveHandler
{
  public event EventHandler<(string contentType, string serverName, string message)> InstallServiceInfoReceived;

  public void Receive(BasicDeliverEventArgs eventArgs)
  {
    var body = eventArgs.Body.ToArray();
    var message = Encoding.UTF8.GetString(body);
    var serverName = eventArgs.BasicProperties.AppId;
    var contentType = eventArgs.BasicProperties.ContentType;

    if (contentType == "SVCMGMT/INSTALL/SVCINFO")
    {
      InstallServiceInfoReceived?.Invoke(this, (contentType, serverName, message));
    }
  }
}

install.razor

@inject ServiceInfoReceiveHandler _serviceInfoReceiveHandler

protected override async Task OnInitializedAsync()
{
  _serviceInfoReceiveHandler.InstallServiceInfoReceived += OnServiceInfoReceived;
}

public void Dispose()
{
  _serviceInfoReceiveHandler.InstallServiceInfoReceived -= OnServiceInfoReceived;
}

private async void OnServiceInfoReceived(object sender, (string contentType, string serverName, string message) e)
{
...
}

问题描述

我正在开发Blazor项目,采用上述事件处理机制。大部分情况下该机制运行正常,但偶发出现OnServiceInfoReceived未被调用的情况。经调试发现,InstallServiceInfoReceived?.Invoke()始终会执行,但后续OnServiceInfoReceived有时会触发、有时不会。

该偶发情况似乎在我与其他开发者同时进行本地测试时出现,我们各自运行本地localhost实例,但共享消息队列和数据库。请问这种共享环境是否会导致该问题?

问题分析与解答

共享消息队列环境确实是导致该偶发现象的核心原因之一,结合你的代码设计,具体原因如下:

  1. 消息队列的消费分发逻辑
    如果共享的消息队列采用轮询消费模式(比如RabbitMQ默认模式),消息会被轮流分配到不同的消费者实例(即你和同事的本地进程)。当一条消息被同事的实例消费时,你的Blazor应用对应的ServiceInfoReceiveHandler不会处理这条消息,自然不会触发你组件中的OnServiceInfoReceived。

  2. Scoped服务的实例隔离问题
    你将ServiceInfoReceiveHandler注册为Scoped服务,意味着每个用户会话(Blazor Server)或组件生命周期(Blazor WebAssembly)都会创建独立实例。如果MessageBroker是单例服务,它注入的ServiceInfoReceiveHandler是应用启动时创建的单例实例,和你Blazor组件中注入的Scoped实例并非同一个。这会导致:

    • 消息被MessageBroker持有的Handler实例处理并触发事件,但组件订阅的是另一个Handler实例的事件,收不到通知。
    • 多实例运行时,这种实例不匹配的问题会被放大,偶发概率提升。
  3. 事件订阅时机的随机性
    多人测试时消息产生时机更随机,如果组件的OnInitializedAsync执行晚于MessageBroker开始消费消息,事件已经触发但组件还没完成订阅,也会出现漏接收的情况。

解决方案建议

  • 调整服务生命周期:将ServiceInfoReceiveHandler注册为Singleton,确保MessageBroker和Blazor组件使用同一个实例:
    builder.Services.AddSingleton<ServiceInfoReceiveHandler>();
    
  • 修改消息队列消费模式:如果需要每个实例都接收同一条消息,将消息队列设置为发布/订阅模式(比如RabbitMQ的Fanout交换机),确保所有消费者都能收到消息。
  • 优化事件订阅时机:在Blazor应用启动阶段就完成事件订阅,避免组件初始化延迟导致的漏接收。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 06:52:46