在asp.net中完成服务间实时通信

在asp.net中完成服务间实时通信

写在开头:

最近遇到这样一个需求:在企业微信(webform)中完成出库入库操作,web后台管理(asp.net core + vue2)界面实时更新库存

最开始,我想的是前端通过短轮询方案,这样并不需要对后端进行任何的改动,简单粗暴。

可想一想,还有更好的解决方案吗,前端绑一个监听事件,触发完成页面更新操作,可是这样的好像对用户也不太友好,同时,如果有很多地方出现这种需求,那会对服务器带来额外的负载。

将库存变化看作是一个事件,然后企业微信端通知到web后端,再由web后端通知给前端,改变相关值即可,不需进行查询。

所以采用rabbitmq完成事件反馈,通过signalr完成实时通信,也就是下图这样

具体实现

1.企业微信端发送事件:

 //
var factory = new ConnectionFactory()
 {
     HostName = "服务器ip",
     UserName = "admin",
     Password = "123456",
     VirtualHost = "/"
 };
 _connection = factory.CreateConnection();
 _channel = _connection.CreateModel();
 _channel.ExchangeDeclare("SparepartsChangeEvent",
                      ExchangeType.Direct,
                       true, false, null);
 var sendedEvent = new SparepartsChangeEvent(SparepartsType.OutStock, code, count, Factory);
 var json = JsonConvert.SerializeObject(sendedEvent);
 var body = Encoding.UTF8.GetBytes(json);
 _channel.BasicPublish(exchange: "SparepartsChangeEvent",
                       routingKey: routekey,
                       basicProperties: null,
                       body: body);

2.web后端创建集线器:

public class SparepartsHub:Hub
{
    public async Task SendSparepartsChangeMessage(string user,string  code,int count,int type)
    {
        await Clients.All.SendAsync("ReceiveSparepartsChangeMessage", user, code, count,type);
    }
}

3.事件处理:

public class SparepartsChangeEventHandler
{
    private readonly IHubContext<SparepartsHub> _hubContext;
    private readonly IConfiguration _configuration;
    private string routeKey = "DP06";
    public SparepartsChangeEventHandler(IHubContext<SparepartsHub> hubContext,IConfiguration configuration)
    {
        _hubContext = hubContext;
        _configuration = configuration;
    }
    public async Task SendMsg()
    {
        var factory = new ConnectionFactory()
        {
            HostName = "服务器ip",
            UserName = "admin",
            Password = "123456",//此处建议放入配置文件
            VirtualHost = "/"
        };
            routeKey = _configuration["RouteKey:SparepartsChange"];//将routekey放入配置文件
        if(routeKey == null)
        {
            await Task.CompletedTask;
            return;
        }
        using (var connection = factory.CreateConnection())
        using (var channel = connection.CreateModel())
        {
            channel.QueueDeclare(queue: "SparepartsChangeEventHandler",
                                 durable: false,
                                 exclusive: false,
                                 autoDelete: false,
                                 arguments: null);
            channel.QueueBind(queue: "SparepartsChangeEventHandler", exchange: "SparepartsChangeEvent", routingKey: routeKey);
            var consumer = new EventingBasicConsumer(channel);
            consumer.Received += async (model, ea) =>
            {
                var body = ea.Body.ToArray();
                SparepartsChangeEvent scEvent = JsonConvert.DeserializeObject<SparepartsChangeEvent>(Encoding.UTF8.GetString(body));
                await Console.Out.WriteLineAsync(JsonConvert.SerializeObject(_hubContext));

                if (_hubContext.Clients != null)
                {
                    await _hubContext.Clients.All.SendAsync("ReceiveSparepartsChangeMessage", scEvent.Factory, scEvent.Code, scEvent.Count,scEvent.Type);
                }

            };
            channel.BasicConsume(queue: "SparepartsChangeEventHandler",
                                 autoAck: true,
                                 consumer: consumer);
            Console.ReadLine();

        }
    }
}

4.starup类配置:

services.AddSingleton<SparepartsHub>();
services.AddSingleton<SparepartsChangeEventHandler>();//依赖注入

var sparepartsChangeEventHandler = app.ApplicationServices.GetRequiredService<SparepartsChangeEventHandler>();
Task.Run(() => sparepartsChangeEventHandler.SendMsg());
//获取服务调用方法,需要另外开一个线程

如果前端signalr提示跨域问题,在此处配置:

 app.UseEndpoints(endpoints =>
 {
     endpoints.MapHub<SparepartsHub>("/sparepartsHub").RequireCors(t => t.WithOrigins(new string[] { "http://前端ip:端口号" }).AllowAnyMethod().AllowAnyHeader().AllowCredentials()); ;
     endpoints.MapControllers();
 });

5.前端:

init() {
      const connection = new signalR.HubConnectionBuilder()
        .withUrl("http://后端ip:端口号/SparepartsHub", {})
        .configureLogging(signalR.LogLevel.Error)
        .build();
      connection.on("ReceiveSparepartsChangeMessage", (factory,code,count,type) => {
        console.log(factory+'-'+code+'-'+count+'-'+type)
        console.log(code)
        let connectIndex=this.dataSource.findIndex(a=>a.bjbh==code)
        console.log(connectIndex)
        if(connectIndex==-1)
          return;
        if(type==0){
          this.dataSource[connectIndex].nowStock += count;
        }else{
          this.dataSource[connectIndex].nowStock -= count;
        }
        console.log()
      });
      connection.start(this.dataSource)
    .then(() => {
        connection.invoke("SendSparepartsChangeMessage", "Alice", "Hello from Vue!",1,0);
    })
    .catch(error => {
        console.error(error);
    });
    },
  },
  created(){
    this.init()
  }

写在最后

我希望rabbitmq发布事件后,web后端根据事件可以自动调用某个EventHandler方法,就像这样:

public class SparepartsChangeEventHandler
{
    private readonly ILogger<SparepartsChangeEvent> _logger;
    public SparepartsChangeEventHandler(ILogger<SparepartsChangeEventHandler> logger)
    {
        _logger = logger;
    }
    public Task Receive()
    {
        _logger.LogInformation($"Received SparepartsChangeEvent 事件");
        return Task.CompletedTask;
    }
}

这样使代码更具有可扩展性及可读性,业务与业务之间有更好的隔离。

posted @   ssz0312  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示