mirror of https://github.com/abpframework/abp.git
5 changed files with 486 additions and 6 deletions
@ -0,0 +1,134 @@ |
|||
# 分布式事件总线RabbitMQ集成 |
|||
|
|||
> 本文解释了**如何配置[RabbitMQ](https://www.rabbitmq.com/)**做为分布式总线提供程序. 参阅[分布式事件总线文档](Distributed-Event-Bus.md)了解如何使用分布式事件总线系统. |
|||
|
|||
## 安装 |
|||
|
|||
使用ABP CLI添加[Volo.Abp.EventBus.RabbitMQ[Volo.Abp.EventBus.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.EventBus.RabbitMQ)NuGet包到你的项目: |
|||
|
|||
* 安装[ABP CLI](https://docs.abp.io/en/abp/latest/CLI),如果你还没有安装. |
|||
* 在你想要安装 `Volo.Abp.EventBus.RabbitMQ` 包的 `.csproj` 文件目录打开命令行(终端). |
|||
* 运行 `abp add-package Volo.Abp.EventBus.RabbitMQ` 命令. |
|||
|
|||
如果你想要手动安装,安装[Volo.Abp.EventBus.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.EventBus.RabbitMQ) NuGet 包到你的项目然后添加 `[DependsOn(typeof(AbpEventBusRabbitMqModule))]` 到你的项目[模块](Module-Development-Basics.md)类. |
|||
|
|||
## 配置 |
|||
|
|||
可以使用配置使用标准的[配置系统](Configuration.md),如 `appsettings.json` 文件,或[选项](Options.md)类. |
|||
|
|||
### `appsettings.json` 文件配置 |
|||
|
|||
这是配置RabbitMQ设置最简单的方法. 它也非常强大,因为你可以使用[由AspNet Core支持的](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/)的任何其他配置源(如环境变量). |
|||
|
|||
**示例:最小化配置与默认配置连接到本地的RabbitMQ服务器** |
|||
|
|||
````json |
|||
{ |
|||
"RabbitMQ": { |
|||
"EventBus": { |
|||
"ClientName": "MyClientName", |
|||
"ExchangeName": "MyExchangeName" |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* `ClientName` 是应用程序的名称,用于RabbitMQ的**队列名称**. |
|||
* `ExchangeName` 是 **交换机名称**. |
|||
|
|||
参阅[RabbitMQ文档](https://www.rabbitmq.com/dotnet-api-guide.html#exchanges-and-queues)更好的了解这些选项. |
|||
|
|||
#### 连接 |
|||
|
|||
如果需要连接到本地主机以外的另一台服务器,需要配置连接属性. |
|||
|
|||
**示例: 指定主机名 (如IP地址)** |
|||
|
|||
````json |
|||
{ |
|||
"RabbitMQ": { |
|||
"Connections": { |
|||
"Default": { |
|||
"HostName": "123.123.123.123" |
|||
} |
|||
}, |
|||
"EventBus": { |
|||
"ClientName": "MyClientName", |
|||
"ExchangeName": "MyExchangeName" |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
允许定义多个连接. 在这种情况下,你可以指定用于事件总线的连接. |
|||
|
|||
**示例: 声明两个连接并将其中一个用于事件总线** |
|||
|
|||
````json |
|||
{ |
|||
"RabbitMQ": { |
|||
"Connections": { |
|||
"Default": { |
|||
"HostName": "123.123.123.123" |
|||
}, |
|||
"SecondConnection": { |
|||
"HostName": "321.321.321.321" |
|||
} |
|||
}, |
|||
"EventBus": { |
|||
"ClientName": "MyClientName", |
|||
"ExchangeName": "MyExchangeName", |
|||
"ConnectionName": "SecondConnection" |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
这允许你可以在你的应用程序使用多个RabbitMQ服务器,但将其中一个做为事件总线. |
|||
|
|||
你可以使用任何[ConnectionFactry](http://rabbitmq.github.io/rabbitmq-dotnet-client/api/RabbitMQ.Client.ConnectionFactory.html#properties)属性作为连接属性. |
|||
|
|||
**示例: 指定连接端口** |
|||
|
|||
````csharp |
|||
{ |
|||
"RabbitMQ": { |
|||
"Connections": { |
|||
"Default": { |
|||
"HostName": "123.123.123.123", |
|||
"Port": "5672" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
### 选项类 |
|||
|
|||
`AbpRabbitMqOptions` 和 `AbpRabbitMqEventBusOptions` 类用于配置RabbitMQ的连接字符串和事件总线选项. |
|||
|
|||
你可以在你的[模块](Module-Development-Basics.md)的 `ConfigureServices` 方法配置选项. |
|||
|
|||
**示例: 配置连接** |
|||
|
|||
````csharp |
|||
Configure<AbpRabbitMqOptions>(options => |
|||
{ |
|||
options.Connections.Default.UserName = "user"; |
|||
options.Connections.Default.Password = "pass"; |
|||
options.Connections.Default.HostName = "123.123.123.123"; |
|||
options.Connections.Default.Port = 5672; |
|||
}); |
|||
```` |
|||
|
|||
**示例: 配置客户端和交换机名称** |
|||
|
|||
````csharp |
|||
Configure<AbpRabbitMqEventBusOptions>(options => |
|||
{ |
|||
options.ClientName = "TestApp1"; |
|||
options.ExchangeName = "TestMessages"; |
|||
}); |
|||
```` |
|||
|
|||
使用这些选项类可以与 `appsettings.json` 组合在一起. 在代码中配置选项属性会覆盖配置文件中的值. |
|||
@ -1 +1,227 @@ |
|||
TODO.... |
|||
# 本地事件总线 |
|||
|
|||
本地事件总线允许服务发布和订阅**进程内事件**. 这意味着如果两个服务(发布者和订阅者)在同一个进程中运行,那么它是合适的. |
|||
|
|||
## 发布事件 |
|||
|
|||
以下介绍了两种发布本地事件的方法. |
|||
|
|||
### ILocalEventBus |
|||
|
|||
可以[注入](Dependency-Injection.md) `ILocalEventBus` 并且使用发布本地事件. |
|||
|
|||
**示例: 产品的存货数量发生变化时发布本地事件** |
|||
|
|||
````csharp |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.EventBus.Local; |
|||
|
|||
namespace AbpDemo |
|||
{ |
|||
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 |
|||
} |
|||
); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
`PublishAsync` 方法需要一个参数:事件对象,它负责保持与事件相关的数据,是一个简单的普通类: |
|||
|
|||
````csharp |
|||
using System; |
|||
|
|||
namespace AbpDemo |
|||
{ |
|||
public class StockCountChangedEvent |
|||
{ |
|||
public Guid ProductId { get; set; } |
|||
|
|||
public int NewCount { get; set; } |
|||
} |
|||
} |
|||
```` |
|||
|
|||
即使你不需要传输任何数据也需要创建一个类(在这种情况下为空类). |
|||
|
|||
### 实体/聚合根类 |
|||
|
|||
[实体](Entities.md)不能通过依赖注入注入服务,但是在实体/聚合根类中发布本地事件是非常常见的. |
|||
|
|||
**示例: 在聚合根方法内发布本地事件** |
|||
|
|||
````csharp |
|||
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 |
|||
AddLocalEvent( |
|||
new StockCountChangedEvent |
|||
{ |
|||
ProductId = Id, |
|||
NewCount = newCount |
|||
} |
|||
); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
`AggregateRoot` 类定义了 `AddLocalEvent` 来添加一个新的本地事件,事件在聚合根对象保存(创建,更新或删除)到数据库时发布. |
|||
|
|||
> 如果实体发布这样的事件,以可控的方式更改相关属性是一个好的实践,就像上面的示例一样 - `StockCount`只能由保证发布事件的 `ChangeStockCount` 方法来更改. |
|||
|
|||
#### IGeneratesDomainEvents 接口 |
|||
|
|||
实际上添加本地事件并不是 `AggregateRoot` 类独有的. 你可以为任何实体类实现 `IGeneratesDomainEvents`. 但是 `AggregateRoot` 默认实现了它简化你的工作. |
|||
|
|||
> 不建议为不是聚合根的实体实现此接口,因为它可能不适用于此类实体的某些数据库提供程序. 例如它适用于EF Core,但不适用于MongoDB. |
|||
|
|||
#### 它是如何实现的? |
|||
|
|||
调用 `AddLocalEvent` 不会立即发布事件. 当你将更改保存到数据库时发布该事件; |
|||
|
|||
* 对于 EF Core, 它在 `DbContext.SaveChanges` 中发布. |
|||
* 对于 MongoDB, 它在你调用仓储的 `InsertAsync`, `UpdateAsync` 或 `DeleteAsync` 方法时发由 (因为MongoDB没有更改跟踪系统). |
|||
|
|||
## 订阅事件 |
|||
|
|||
一个服务可以实现 `ILocalEventHandler<TEvent>` 来处理事件. |
|||
|
|||
**示例: 处理上面定义的`StockCountChangedEvent`** |
|||
|
|||
````csharp |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.EventBus; |
|||
|
|||
namespace AbpDemo |
|||
{ |
|||
public class MyHandler |
|||
: ILocalEventHandler<StockCountChangedEvent>, |
|||
ITransientDependency |
|||
{ |
|||
public async Task HandleEventAsync(StockCountChangedEvent eventData) |
|||
{ |
|||
//TODO: your code that does somthing on the event |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
这就是全部,`MyHandler` 由ABP框架**自动发现**,并在发生 `StockCountChangedEvent` 事件时调用 `HandleEventAsync`. |
|||
|
|||
* 事件可以由**0个或多个处理程序**订阅. |
|||
* 一个事件处理程序可以**订阅多个事件**,但是需要为每个事件实现 `ILocalEventHandler<TEvent>` 接口. |
|||
|
|||
> 事件处理程序类必须注册到依赖注入(DI),示例中使用了 `ITransientDependency`. 参阅[DI文档](Dependency-Injection.md)了解更多选项. |
|||
|
|||
## 事务和异常行为 |
|||
|
|||
当一个事件发布,订阅的事件处理程序将立即执行.所以; |
|||
|
|||
* 如果处理程序**抛出一个异常**,它会影响发布该事件的代码. 这意味着它在 `PublishAsync` 调用上获得异常. 因此如果你想隐藏错误,在事件处理程序中**使用try-catch**. |
|||
*如果在一个[工作单元](Unit-Of-Work.md)范围内执行的事件发布的代码,该事件处理程序也由工作单元覆盖. 这意味着,如果你的UOW是事务和处理程序抛出一个异常,事务会回滚. |
|||
|
|||
## 预定义的事件 |
|||
|
|||
**发布实体创建,更新,删除事件**是常见的操作. ABP框架为所有的实体**自动**发布这些事件. 你只需要订阅相关的事件. |
|||
|
|||
**示例: 订阅用户创建事件** |
|||
|
|||
````csharp |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Identity; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Domain.Entities.Events; |
|||
using Volo.Abp.EventBus; |
|||
|
|||
namespace AbpDemo |
|||
{ |
|||
public class MyHandler |
|||
: ILocalEventHandler<EntityCreatedEventData<IdentityUser>>, |
|||
ITransientDependency |
|||
{ |
|||
public async Task HandleEventAsync( |
|||
EntityCreatedEventData<IdentityUser> eventData) |
|||
{ |
|||
var userName = eventData.Entity.UserName; |
|||
var email = eventData.Entity.Email; |
|||
//... |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
这个类订阅 `EntityCreatedEventData<IdentityUser>`,它在用户创建后发布. 你可能需要向新用户发送一封"欢迎"电子邮件. |
|||
|
|||
这些事件有两种类型:过去时态的事件和进行时态的事件. |
|||
|
|||
### 用过去时态事件 |
|||
|
|||
当相关工作单元完成且实体更改成功保存到数据库时,将发布带有过去时态的事件. 如果在这些事件处理程序上抛出异常,则**无法回滚**事务,因为事务已经提交. |
|||
|
|||
事件类型; |
|||
|
|||
* `EntityCreatedEventData<T>` 当实体创建创建成功后发布. |
|||
* `EntityUpdatedEventData<T>` 当实体创建更新成功后发布. |
|||
* `EntityDeletedEventData<T>` 当实体创建删除成功后发布. |
|||
* `EntityChangedEventData<T>` 当实体创建,更新,删除后发布. 如果你需要监听任何类型的更改,它是一种快捷方式 - 而不是订阅单个事件. |
|||
|
|||
### 用于进行时态事件 |
|||
|
|||
带有进行时态的事件在完成事务之前发布(如果数据库事务由所使用的数据库提供程序支持). 如果在这些事件处理程序上抛出异常,它**会回滚**事务,因为事务还没有完成,更改也没有保存到数据库中. |
|||
|
|||
事件类型; |
|||
|
|||
* `EntityCreatingEventData<T>` 当新实体保存到数据库前发布. |
|||
* `EntityUpdatingEventData<T>` 当已存在实体更新到数据库前发布. |
|||
* `EntityDeletingEventData<T>` 删除实体前发布. |
|||
* `EntityChangingEventData<T>` 当实体创建,更新,删除前发布. 如果你需要监听任何类型的更改,它是一种快捷方式 - 而不是订阅单个事件. |
|||
|
|||
#### 它是如何实现的? |
|||
|
|||
在将更改保存到数据库时发布预构建事件; |
|||
|
|||
* 对于 EF Core, 他们在 `DbContext.SaveChanges` 发布. |
|||
* 对于 MongoDB, 在你调用仓储的 `InsertAsync`, `UpdateAsync` 或 `DeleteAsync` 方法发布(因为MongoDB没有更改追踪系统). |
|||
Loading…
Reference in new issue