ABP-VNEXT 學習筆記(六)事件總線--本地事件總線

事件總線,是我們在處理分佈式和微服務的時候經常需要用到的,用於分佈式事務解決方案。

事件總線基本就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個事件都有執行。

這也驗證了,一個訂閱事件處理類,可以同時訂閱多個事件並處理。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章