在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;
}
}
这样使代码更具有可扩展性及可读性,业务与业务之间有更好的隔离。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)