

















ABP提供两种事件总线模式:
| 类型 | 作用域 | 特点 |
|---|---|---|
| 本地事件总线 | 单进程内 | 与事务绑定,异常会触发回滚 |
| 分布式事件总线 | 跨进程/服务 | 解耦服务间通信,支持微服务架构 |
IDistributedEventBus可同时兼容本地和分布式模式ABP CLI方式:
abp add-package Volo.Abp.EventBus.RabbitMQ
手动安装:
<PackageReference Include="Volo.Abp.EventBus.RabbitMQ" Version="8.1.0" />
在HttpApi.Host项目的模块类中添加依赖:
[DependsOn(
// ... 其他依赖
typeof(AbpEventBusRabbitMqModule)
)]
public class YourHttpApiHostModule : AbpModule
{
// 模块配置
}
{
"RabbitMQ": {
"Connections": {
"Default": {
"HostName": "localhost",
"Port": 5672,
"UserName": "guest",
"Password": "guest"
}
},
"EventBus": {
"ClientName": "YourAppName",
"ExchangeName": "YourExchangeName"
}
}
}
在Application.Contracts项目中创建事件传输对象:
using Volo.Abp.EventBus;
namespace YourApp.Application.Contracts.Etos
{
[EventName("YourApp.EventName")]
public class YourEventEto
{
public Guid Id { get; set; }
public string Name { get; set; }
public DateTime EventTime { get; set; }
// 其他业务属性
}
}
EventName特性说明:
YourApp.Application.Contracts.Etos.YourEventEtonamespace YourApp.Application.Contracts.Events
{
public static class AppEvents
{
private const string Prefix = "YourApp";
public const string YourEvent = $"{Prefix}.YourEvent";
}
}
一个服务可以实现 IDistributedEventHandler<TEvent> 来处理事件.
YourEventHandler 由ABP框架自动发现,并在发生 YourEventEto 事件时调用 HandleEventAsync.你可以在处理程序注入任何服务来执行所需的逻辑. 一个事件处理程序可以订阅多个事件,但是需要为每个事件实现 IDistributedEventHandler<TEvent> 接口.
事件处理程序类必须注册到依赖注入(DI),示例中使用了
ITransientDependency.
在Application项目中创建处理器:
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using YourApp.Application.Contracts.Etos;
namespace YourApp.Application.Services
{
public class YourEventHandler :
IDistributedEventHandler<YourEventEto>,
ITransientDependency
{
private readonly ILogger<YourEventHandler> _logger;
public YourEventHandler(ILogger<YourEventHandler> logger)
{
_logger = logger;
}
public async Task HandleEventAsync(YourEventEto eventData)
{
// 处理业务逻辑
_logger.LogInformation(
"处理事件: {EventName}, ID: {Id}, 时间: {EventTime}",
nameof(YourEventEto), eventData.Id, eventData.EventTime);
// 异步业务处理
await ProcessBusinessAsync(eventData);
}
private async Task ProcessBusinessAsync(YourEventEto eventData)
{
// 具体的业务逻辑实现
await Task.CompletedTask;
}
}
}
在任意服务中注入并使用分布式事件总线:
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using YourApp.Application.Contracts.Etos;
namespace YourApp.Application.Services
{
public class YourService : ITransientDependency
{
private readonly IDistributedEventBus _distributedEventBus;
public YourService(IDistributedEventBus distributedEventBus)
{
_distributedEventBus = distributedEventBus;
}
public async Task PerformActionAsync()
{
// 业务逻辑...
// 发布事件
await _distributedEventBus.PublishAsync(new YourEventEto
{
Id = Guid.NewGuid(),
Name = "示例事件",
EventTime = DateTime.Now
});
_logger.LogInformation("事件发布成功");
}
}
}[实体]不能通过依赖注入注入服务,但是在实体/聚合根类中发布分布式事件是非常常见的.
示例: 在聚合根方法内发布分布式事件
using System;
using Volo.Abp.Domain.Entities;
namespace AbpDemo
{
public class Product : AggregateRoot<Guid>
{
public string Name { get; set; }
public int StockCount { get; private set; }
private Product() { }
public Product(Guid id, string name)
: base(id)
{
Name = name;
}
public void ChangeStockCount(int newCount)
{
StockCount = newCount;
//ADD an EVENT TO BE PUBLISHED
AddDistributedEvent(
new StockCountChangedEto
{
ProductId = Id,
NewCount = newCount
}
);
}
}
}AggregateRoot 类定义了 AddDistributedEvent 来添加一个新的分布式事件,事件在聚合根对象保存(创建,更新或删除)到数据库时发布.
如果实体发布这样的事件,以可控的方式更改相关属性是一个好的实践,就像上面的示例一样 -
StockCount只能由保证发布事件的ChangeStockCount方法来更改.
实际上添加分布式事件并不是 AggregateRoot 类独有的. 你可以为任何实体类实现 IGeneratesDomainEvents. 但是 AggregateRoot 默认实现了它简化你的工作.
不建议为不是聚合根的实体实现此接口,因为它可能不适用于此类实体的某些数据库提供程序. 例如它适用于EF Core,但不适用于MongoDB.
调用 AddDistributedEvent 不会立即发布事件. 当你将更改保存到数据库时发布该事件;
DbContext.SaveChanges 中发布.InsertAsync, UpdateAsync 或 DeleteAsync 方法时发由 (因为MongoDB没有更改跟踪系统).你可以把这个功能理解为:ABP 帮你写好了 “实体增删改时自动发事件” 的代码,你不用手动写发布逻辑,只需要配置 + 订阅就能用。
AddDistributedEvent(...)发布事件;| 术语 | 通俗解释 |
|---|---|
| 预定义事件类型(EntityCreated/Updated/DeletedEto) | ABP 内置的 3 个事件模板,T 是 “事件传输对象(ETO)”,不是实体本身 |
| ETO(Event Transfer Object) | 专门用来跨服务传输的 “实体快照”,因为实体对象可能包含循环引用、数据库连接等无法序列化的内容,所以要定义轻量的 ETO 类 |
| AbpDistributedEntityEventOptions | 配置类,用来告诉 ABP:“要为哪些实体自动发事件?用哪个 ETO 传输?” |
| EtoMappings | 配置 “实体→ETO” 的映射关系,ABP 会自动把实体数据复制到 ETO 里 |
假设你有一个Product实体(商品),想在商品被修改时,自动发布分布式事件,并且让其他服务能收到这个事件。
// 你的核心业务实体(存在数据库里的)
public class Product : AggregateRoot<Guid>
{
public string Name { get; set; } // 商品名称
public decimal Price { get; set; } // 商品价格
}
因为实体不能直接跨服务传输,所以要定义一个 “精简版” 的 ETO:
using AutoMapper;
using Volo.Abp.Domain.Entities.Events.Distributed;
// [AutoMap(typeof(Product))]:告诉AutoMapper自动把Product的属性映射到ProductEto
[AutoMap(typeof(Product))]
public class ProductEto : EntityEto // 继承EntityEto,自带基础属性
{
public Guid Id { get; set; } // 商品ID(和实体一致)
public string Name { get; set; } // 商品名称(只传需要的字段)
// 注意:可以只传需要的字段,不用把实体的所有字段都放进来
}
在你的模块类(比如AbpDemoModule)的ConfigureServices方法里配置:
public override void ConfigureServices(ServiceConfigurationContext context)
{
// 配置分布式实体事件
Configure<AbpDistributedEntityEventOptions>(options =>
{
// 第一步:指定为Product实体启用自动事件(增删改都发)
options.AutoEventSelectors.Add<Product>();
// 第二步:指定Product实体用ProductEto作为传输对象
options.EtoMappings.Add<Product, ProductEto>();
});
}
AutoEventSelectors.Add<Product>():只给 Product 实体开自动事件(如果想给所有实体开,用AddAll());EtoMappings.Add<Product, ProductEto>():ABP 在发事件时,会自动把 Product 实体的数据映射到 ProductEto 里。写一个事件处理器,监听 “Product 被更新” 的事件:
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus.Distributed;
namespace AbpDemo
{
// 实现IDistributedEventHandler<EntityUpdatedEto<ProductEto>>:订阅“Product更新事件”
// ITransientDependency:让ABP自动注册这个处理器到依赖注入容器
public class ProductUpdateHandler :
IDistributedEventHandler<EntityUpdatedEto<ProductEto>>,
ITransientDependency
{
// 当Product被更新时,ABP会自动调用这个方法
public async Task HandleEventAsync(EntityUpdatedEto<ProductEto> eventData)
{
// eventData.Entity:就是ABP自动映射好的ProductEto对象
var updatedProductId = eventData.Entity.Id; // 获取被修改的商品ID
var updatedProductName = eventData.Entity.Name; // 获取修改后的商品名称
// 这里写你的业务逻辑,比如:
// 1. 记录日志
// 2. 通知其他服务(比如库存服务更新商品名称)
// 3. 发送消息给前端
await Task.CompletedTask;
}
}
}
当你修改 Product 并保存到数据库时,ABP 会自动发布事件:
// 假设你在应用服务里修改商品
public class ProductAppService : ApplicationService
{
private readonly IRepository<Product, Guid> _productRepository;
public ProductAppService(IRepository<Product, Guid> productRepository)
{
_productRepository = productRepository;
}
public async Task UpdateProduct(Guid id, string newName)
{
// 1. 获取商品
var product = await _productRepository.GetAsync(id);
// 2. 修改属性
product.Name = newName;
// 3. 保存到数据库(关键!ABP会在SaveChanges时自动发布事件)
await _productRepository.UpdateAsync(product);
// 你不需要手动调用PublishAsync!ABP自动帮你做了
}
}
List<Order>)、私有字段等,这些内容无法序列化(跨服务传输需要序列化);ABP 会用默认的EntityEto作为传输对象,这个类只有两个属性:
EntityType:实体的完整类名(比如AbpDemo.Product);
KeysAsString:实体的主键(比如00000000-0000-0000-0000-000000000001);
这种情况下,你只能拿到主键,拿不到商品名称、价格等具体数据,所以建议为每个实体定义专属 ETO。
Add<Product>())Configure<AbpDistributedEntityEventOptions>(options =>
{
// 方式1:为指定命名空间下的所有实体开自动事件(比如Volo.Abp.Identity下的用户/角色)
options.AutoEventSelectors.AddNamespace("Volo.Abp.Identity");
// 方式2:自定义条件(比如只给名称以"Order"开头的实体开)
options.AutoEventSelectors.Add(type => type.Name.StartsWith("Order"));
});
支持多个RabbitMQ服务器连接:
{
"RabbitMQ": {
"Connections": {
"Primary": {
"HostName": "rabbitmq1.example.com",
"Port": 5672
},
"Secondary": {
"HostName": "rabbitmq2.example.com",
"Port": 5672
}
},
"EventBus": {
"ClientName": "YourApp",
"ExchangeName": "YourExchange",
"ConnectionName": "Primary" // 指定使用哪个连接
}
}
}
也可通过代码配置选项:
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpRabbitMqOptions>(options =>
{
options.Connections.Default.HostName = "localhost";
options.Connections.Default.Port = 5672;
options.Connections.Default.UserName = "user";
options.Connections.Default.Password = "pass";
});
Configure<AbpRabbitMqEventBusOptions>(options =>
{
options.ClientName = "YourApp";
options.ExchangeName = "YourExchange";
});
}
发布服务配置:
{
"RabbitMQ": {
"EventBus": {
"ClientName": "ServiceA",
"ExchangeName": "SharedExchange"
}
}
}
消费服务配置:
{
"RabbitMQ": {
"EventBus": {
"ClientName": "ServiceB",
"ExchangeName": "SharedExchange" // 必须相同
}
}
}
建议创建共享类库包含ETO定义,或在不同服务中定义相同的ETO结构。
RabbitMQ分布式事件总线具备以下可靠性保证:
在RabbitMQ管理界面可监控:
访问 http://localhost:15672 (默认账号:guest/guest):
启用详细日志以调试事件流:
{
"Logging": {
"LogLevel": {
"Volo.Abp.EventBus.RabbitMQ": "Information"
}
}
}
原因:配置不一致或处理器未注册
解决:检查ExchangeName一致性和处理器依赖注入
原因:RabbitMQ服务未启动或网络问题
解决:验证RabbitMQ状态和连接配置
原因:大量事件积压
解决:优化处理器逻辑,考虑批量处理
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。