事件總線,是我們在處理分佈式和微服務的時候經常需要用到的,用於分佈式事務解決方案。
事件總線基本就2個邏輯,1個發佈事件,1個是訂閱事件。
abp也提供了事件總線的處理機制
下面跟着學習本地事件總線
abp官網文檔地址:https://docs.abp.io/zh-Hans/abp/latest/Local-Event-Bus
本地事件總線允許服務發佈和訂閱進程內事件. 這意味着如果兩個服務(發佈者和訂閱者)在同一個進程中運行,那麼它是合適的。這點很重要,也就是
本地事件,適用於發佈者和訂閱者都在同一個項目同一個進程裏執行。他不適用於不同進程之間的協同。
事件可用於我們發生一個動作時,觸發多種處理的操作,以往,我們都是在發生動作的業務邏輯裏面寫觸發事件的調用函數。如果多個地方要觸發,就需要多處寫,不利於代碼的解耦。
用了事件,就完全解耦並且是異步。
下面我們上代碼,利用abp文檔上的代碼示例做訓練講解
ILocalEventBus
可以注入 ILocalEventBus
並且使用發佈本地事件.
我們新建一個.NET6.0 的Asp.net Core MVC項目EventBus,nuget上引用Volo.Abp.Core、Volo.Abp.Autofac、Volo.Abp.AspNetCore.Mvc 包
然後一樣創建一個EventBusModule.cs啓動類,代碼如下:
using Volo.Abp; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.Autofac; using Volo.Abp.Modularity; namespace EventBus { [DependsOn( typeof(AbpAutofacModule), typeof(AbpAspNetCoreMvcModule) )] public class EventBusModule:AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { } public override void OnApplicationInitialization(ApplicationInitializationContext context) { var app = context.GetApplicationBuilder(); var env = context.GetEnvironment(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); endpoints.MapControllerRoute( name: "areas", pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}" ); }); } } }
Program.cs文件如下:
using EventBus; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(); builder.Host.AddAppSettingsSecretsJson().UseAutofac(); //引入autofac作爲IOC await builder.AddApplicationAsync<EventBusModule>(); //將abp模塊入口初始化 var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); await app.InitializeApplicationAsync(); //初始化abp模塊應用 app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
以上是項目的基本配置。
下面開始事件總線相關代碼
示例: 產品的存貨數量發生變化時發佈本地事件
新建一個MyService.cs類,作爲我們的服務調用類,類中定義一個ChangeStockCountAsync方法,在方法中發佈一個事件
using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus.Local; namespace EventBus.Models { public class MyService : ITransientDependency { private readonly ILocalEventBus _localEventBus; public MyService(ILocalEventBus localEventBus) { _localEventBus = localEventBus; } public virtual async Task ChangeStockCountAsync(Guid productId, int newCount) { //TODO: IMPLEMENT YOUR LOGIC... //PUBLISH THE EVENT await _localEventBus.PublishAsync( new StockCountChangedEvent { ProductId = productId, NewCount = newCount } ); } } public class StockCountChangedEvent { public Guid ProductId { get; set; } public int NewCount { get; set; } } }
PublishAsync
方法需要一個參數:事件對象,它負責保持與事件相關的數據,是一個簡單的普通類:
StockCountChangedEvent 事件對象,用於參數傳遞。
接下來,我們定義兩個訂閱類,來訂閱處理這個事件
一個服務可以實現 ILocalEventHandler<TEvent>
來處理事件.
示例: 處理上面定義的StockCountChangedEvent
新建一個MyHandler.cs類
using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus; namespace EventBus.Models { public class MyHandler : ILocalEventHandler<StockCountChangedEvent>, ITransientDependency { public async Task HandleEventAsync(StockCountChangedEvent eventData) { //TODO: your code that does somthing on the event //這裏處理事件 var proId = eventData.ProductId; Console.WriteLine("MyHandler執行完畢"); } } }
我們再定義一個MyHandler2.cs 的訂閱類,用於驗證一個事件,是可以多個訂閱處理的
using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus; namespace EventBus.Models { public class MyHandler2 : ILocalEventHandler<StockCountChangedEvent>, ITransientDependency { public async Task HandleEventAsync(StockCountChangedEvent eventData) { //TODO: your code that does somthing on the event //這裏處理事件 var proId = eventData.ProductId; Console.WriteLine("MyHandler2執行完畢"); } } }
OK,以上我們就完成了1個發佈,2個訂閱。
那我們跑起來看下,我們在Home控制器的index中調用Myservice中的ChangeStockCountAsync方法,驗證2個訂閱是否都執行了。
public MyService myService { get; set; } public HomeController(ILogger<HomeController> logger) { _logger = logger; } public IActionResult Index() { myService.ChangeStockCountAsync(Guid.NewGuid(), 100).GetAwaiter(); Console.WriteLine("任務執行完畢"); return View(); }
運行起來看結果:
通過運行結果,我們可以確認以下2點
1: 2個訂閱都有執行,驗證了1個發佈,可以多個訂閱的處理方式
2:訂閱和發佈是異步的,發佈不需要等訂閱執行完業務邏輯才結束。
下面,我們再進一步驗證一個問題
一個訂閱的事件處理方法,能否訂閱多個事件。比如有多個動作,會觸發相同的行爲。比如入庫要增加庫存,退貨也要增加庫存。
那麼入庫定義爲一個事件,退貨也定義爲一個事件,但是訂閱事件是同一個,就是增加庫存。
我們對代碼進行優化一下來驗證。
我們在 myservice.cs中增加事件對象:InStockCountChangedEvent
同時,爲了簡便,我們直接在原有的ChangeStockCountAsync方法中,增加InStockCountChangedEvent事件的發佈
完整代碼如下:
using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus.Local; namespace EventBus.Models { public class MyService : ITransientDependency { private readonly ILocalEventBus _localEventBus; public MyService(ILocalEventBus localEventBus) { _localEventBus = localEventBus; } public virtual async Task ChangeStockCountAsync(Guid productId, int newCount) { //TODO: IMPLEMENT YOUR LOGIC... //PUBLISH THE EVENT await _localEventBus.PublishAsync( new StockCountChangedEvent { ProductId = productId, NewCount = newCount } ); await _localEventBus.PublishAsync( new InStockCountChangedEvent { ProductId = productId, NewCount = newCount } ); } } public class StockCountChangedEvent { public Guid ProductId { get; set; } public int NewCount { get; set; } } public class InStockCountChangedEvent { public Guid ProductId { get; set; } public int NewCount { get; set; } } }
在MyHandler訂閱事件處理中,我們增加InStockCountChangedEvent事件的訂閱
代碼如下:
using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus; namespace EventBus.Models { public class MyHandler : ILocalEventHandler<StockCountChangedEvent>, ILocalEventHandler<InStockCountChangedEvent>, ITransientDependency { public async Task HandleEventAsync(StockCountChangedEvent eventData) { //TODO: your code that does somthing on the event //這裏處理事件 var proId = eventData.ProductId; Console.WriteLine("MyHandler處理StockCountChangedEvent事件完畢"); } public async Task HandleEventAsync(InStockCountChangedEvent eventData) { //TODO: your code that does somthing on the event //這裏處理事件 var proId = eventData.ProductId; Console.WriteLine("MyHandler處理InStockCountChangedEvent事件完畢"); } } }
OK了,我們跑起來
根據運行結果,我們可以看到2個事件都有執行。
這也驗證了,一個訂閱事件處理類,可以同時訂閱多個事件並處理。