ABP vNext框架文档解读33-分布式事件总线
ccwgpt 2024-09-20 13:35 29 浏览 0 评论
分布式事件总线系统允许发布和订阅跨应用/服务边界传输的事件. 你可以使用分布式事件总线在微服务或应用程序之间异步发送和接收消息.
提供程序(Provider)
分布式事件总线系统提供了一个可以被任何提供程序实现的抽象. 有两种开箱即用的提供程序:
- LocalDistributedEventBus 是默认实现,实现作为进程内工作的分布式事件总线. 如果没有配置真正的分布式提供程序,默认实现的工作方式与本地事件总线一样.
- RabbitMqDistributedEventBus 通过RabbitMQ实现分布式事件总线. 请参阅RabbitMQ集成文档了解如何配置它.
- KafkaDistributedEventBus 通过Kafka实现分布式事件总线. 请参阅Kafka集成文档了解如何配置它.
- RebusDistributedEventBus 通过Rebus实现分布式事件总线. 请参阅Rebus集成文档了解如何配置它.
使用本地事件总线作为默认具有一些重要的优点. 最重要的是:它允许你编写与分布式体系结构兼容的代码. 您现在可以编写一个整体应用程序,以后可以拆分成微服务. 最好通过分布式事件而不是本地事件在边界上下文之间(或在应用程序模块之间)进行通信.
例如,预构建的应用模块被设计成在分布式系统中作为服务工作,同时它们也可以在独立应用程序中作为模块工作,而不依赖于外部消息代理.
发布事件
以下介绍了两种发布分布式事件的方法.
IDistributedEventBus
可以注入 IDistributedEventBus 并且使用发布分布式事件.
示例: 产品的存货数量发生变化时发布分布式事件
using System;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
namespace AbpDemo
{
public class MyService : ITransientDependency
{
private readonly IDistributedEventBus _distributedEventBus;
public MyService(IDistributedEventBus distributedEventBus)
{
_distributedEventBus = distributedEventBus;
}
public virtual async Task ChangeStockCountAsync(Guid productId, int newCount)
{
await _distributedEventBus.PublishAsync(
new StockCountChangedEvent
{
ProductId = productId,
NewCount = newCount
}
);
}
}
}
PublishAsync 方法需要一个参数:事件对象,它负责保持与事件相关的数据,是一个简单的普通类:
using System;
namespace AbpDemo
{
[EventName("MyApp.Product.StockChange")]
public class StockCountChangedEto
{
public Guid ProductId { get; set; }
public int NewCount { get; set; }
}
}
即使你不需要传输任何数据也需要创建一个类(在这种情况下为空类).
Eto 是我们按照约定使用的Event Transfer Objects(事件传输对象)的后缀. 虽然这不是必需的,但我们发现识别这样的事件类很有用(就像应用层上的DTO 一样).
事件名称
EventName attribute是可选的,但建议使用. 如果不声明,事件名将事件名称将是事件类的全名. 这里是 AbpDemo.StockCountChangedEto.
关于序列化的事件对象
事件传输对象必须是可序列化的,因为将其传输到流程外时,它们将被序列化/反序列化为JSON或其他格式.
避免循环引用,多态,私有setter,并提供默认(空)构造函数,如果你有其他的构造函数.(虽然某些序列化器可能会正常工作),就像DTO一样.
实体/聚合根类
实体不能通过依赖注入注入服务,但是在实体/聚合根类中发布分布式事件是非常常见的.
示例: 在聚合根方法内发布分布式事件
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 方法来更改.
IGeneratesDomainEvents 接口
实际上添加分布式事件并不是 AggregateRoot 类独有的. 你可以为任何实体类实现 IGeneratesDomainEvents. 但是 AggregateRoot 默认实现了该以简化你的工作.
不建议为不是聚合根的实体实现此接口,因为它可能不适用于此类实体的某些数据库提供程序. 例如它适用于EF Core,但不适用于MongoDB.
它是如何实现的?
调用 AddDistributedEvent 不会立即发布事件. 当你将更改保存到数据库时发布该事件;
- 对于 EF Core, 它在 DbContext.SaveChanges 中发布.
- 对于 MongoDB, 它在你调用仓储的 InsertAsync, UpdateAsync 或 DeleteAsync 方法时发由 (因为MongoDB没有更改跟踪系统).
订阅事件
一个服务可以实现 IDistributedEventHandler<TEvent> 来处理事件.
示例: 处理上面定义的StockCountChangedEto
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
namespace AbpDemo
{
public class MyHandler
: IDistributedEventHandler<StockCountChangedEto>,
ITransientDependency
{
public async Task HandleEventAsync(StockCountChangedEto eventData)
{
var productId = eventData.ProductId;
}
}
}
- MyHandler 由ABP框架自动发现,并在发生 StockCountChangedEto 事件时调用 HandleEventAsync.
- 如果你使用的是分布式消息代理,比如RabbitMQ,ABP会自动订阅消息代理上的事件,获取消息执行处理程序.
- 如果事件处理程序成功执行(没有抛出任何异常),它将向消息代理发送确认(ACK).
你可以在处理程序中注入任何服务来执行所需的逻辑. 一个事件处理程序可以订阅多个事件,但是需要为每个事件实现 IDistributedEventHandler<TEvent> 接口.
事件处理程序类必须注册到依赖注入(DI),示例中使用了 ITransientDependency.
预定义的事件
ABP框架会为实体自动发布创建,更新和删除分布式事件.
事件类型
有三种预定义的事件类型:
- EntityCreatedEto<T> 是实体 T 创建后发布.
- EntityUpdatedEto<T> 是实体 T 更新后发布.
- EntityDeletedEto<T> 是实体 T 删除后发布.
这些都是泛型的, T 实际上是Event Transfer Object (ETO)的类型,而不是实体的类型,因为实体对象不能做为事件数据传输,所以通常会为实体类定义一个ETO类,如为 Product 实体定义 ProductEto.
订阅自动事件
订阅自动事件与订阅常规分布式事件相同.
示例: 产品更新后获取通知
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus.Distributed;
namespace AbpDemo
{
public class MyHandler :
IDistributedEventHandler<EntityUpdatedEto<ProductEto>>,
ITransientDependency
{
public async Task HandleEventAsync(EntityUpdatedEto<ProductEto> eventData)
{
var productId = eventData.Entity.Id;
//TODO
}
}
}
- MyHandler 实现了 IDistributedEventHandler<EntityUpdatedEto<ProductEto>>.
配置
你可以在模块的 ConfigureServices 中配置 AbpDistributedEntityEventOptions添加选择器.
示例: 配置示例
Configure<AbpDistributedEntityEventOptions>(options =>
{
//Enable for all entities
options.AutoEventSelectors.AddAll();
//Enable for a single entity
options.AutoEventSelectors.Add<IdentityUser>();
//Enable for all entities in a namespace (and child namespaces)
options.AutoEventSelectors.AddNamespace("Volo.Abp.Identity");
//Custom predicate expression that should return true to select a type
options.AutoEventSelectors.Add(type => type.Namespace.StartsWith("MyProject.")
);
});
- 最后一个提供了灵活性来决定是否应该针对给定的实体类型发布事件. 返回 true 代表为该 Type 发布事件.
你可以添加多个选择器. 如果选择器之一与实体类型匹配,则将其选中.
事件传输对象
一旦你为一个实体启用了自动事件,ABP框架就会为实体上的更改发布事件. 如果你没有为实体指定对应的Event Transfer Object(ETO), ABP框架会使用一个标准类型 EntityEto,它只有两个属性:
- EntityType (string): 实体类的全名(包括命令空间).
- KeysAsString (string): 已更改实体的主键.如果它只有一个主键,这个属性将是主键值. 对于复合键,它包含所有用,(逗号)分隔的键.
因此可以实现 IDistributedEventHandler<EntityUpdatedEto<EntityEto>> 订阅事件. 但是订阅这样的通用事件不是一个好方法,你可以为实体类型定义对应的ETO.
示例: 为 Product 声明使用 ProductDto
Configure<AbpDistributedEntityEventOptions>(options =>
{
options.AutoEventSelectors.Add<Product>();
options.EtoMappings.Add<Product, ProductEto>();
});
在这个示例中;
- 添加选择器允许发布 Product 实体的创建,更新和删除事件.
- 配置为使用 ProductEto 作为事件传输对象来发布与 Product 相关的事件.
分布式事件系统 使用对象到对象的映射系统, 映射 Product 对象到 ProductEto 对象,需要你配置映射.
示例: 使用AutoMapper配置 Product 到 ProductEto 映射
using System;
using AutoMapper;
using Volo.Abp.Domain.Entities.Events.Distributed;
namespace AbpDemo
{
[AutoMap(typeof(Product))]
public class ProductEto : EntityEto
{
public Guid Id { get; set; }
public string Name { get; set; }
}
}
此示例使用AutoMapper的 AutoMap 属性配置的映射. 你可以创建一个配置文件类代替.
相关推荐
- css布局方案汇总(28个实例图文并茂)
-
简介布局在我们前端日常开发来说是非常重要的,一个好的布局能简化代码的同时还能提高网页的性能。常见的布局方法有浮动(float)布局、绝对定位(position)布局、表格布局(table)、弹性(fl...
- 十款免费的CSS框架加速Web开发
-
Pure这是Yahoo最新推出的一款CSS框架,它只有HTML和CSS,没有使用任何JavaScript语言。总大小只有4.4kb,但功能却非常丰富,支持响应式样式和各种导航、表格、表单、按钮、网格和...
- Tailwind CSS 是不是目前世上最好的CSS框架?
-
转载说明:原创不易,未经授权,谢绝任何形式的转载今天看了一篇国外大佬对TailwindCSS的看法,在这里分享给大家,看看大家是否赞同,以下是其相关内容的整理,由于翻译水平有限,欢迎大家讨论和指...
- 下一代 CSS 框架:Mojo CSS,为何如此受欢迎?
-
TailwindCSS推出即受到广大开发者的欢迎,当前Githubstar数已达77.8k。它是一个功能类优先(utility-first)的CSS框架,它提供了一系列功能类,让开发者...
- 常见的几种摄影构图方式
-
摄影构图,是一种在摄影画面中表现结构美、形式美的方式。构图能让摄影主体更加突出,画面更加有序。所以说,构图在摄影中是非常重要的一个环节。无论是前期构图还是后期构图,摄影者都要对构图有一个比较深的了解。...
- 风光摄影10大构图技巧,会用构图,照片更容易好看
-
风光摄影10大构图技巧,会用构图,照片更容易好看先解释一下,为什么会使用构图之后,照片更容易好看?因为,构图是根据很多好看的照片,总结出来的技巧,使用这些构图技巧,就相当于站在了巨人的肩膀上,也就是用...
- 掌握框式构图的摄影技巧,会让摄影爱好者的作品更有魅力!
-
很多摄影爱好者都知道摄影构图中有个框式构图,但大多数人对框式构图的摄影技巧,却一知半解。所以摄影爱好者们有必要更全面、深入的了解,并掌握框式构图,会对你摄影水平的提高更有帮助。【欢迎点击上方关注:金立...
- 这个构图很简洁,但为什么不耐看?
-
摄影爱好者最常犯的错就是过于复杂、主体不明确,但当遇到简单的场景往往又会出现单调、不耐看的状况。为什么会这样?说白了还是观察力不够。下面是本周的摄影入围习作,我们一起来看看这些照片中主体、陪体以及背景...
- 初学者需要记牢的八种常用构图法
-
作者:冯海军摄影中,构图很关键,决定照片是否成功,所以在构图上要加以重视和推敲,虽然说构图无定法,但是也有很多的规律可循,以下列举几种常用构图,会对初学者有很大的帮助。多彩刘卫洲摄苏州姑苏俱乐部(...
- 构图这件事不难!掌握14种构图模式就稳了
-
如果说视觉元素是视觉信息的载体,那么构图就是视觉元素的载体。没有适当形式的构图对视觉元素有机、有序地承载,平面设计将无法传达预定的设计意图和视觉信息。因此,对于平面设计而言,构图是平面设计不可或缺的重...
- 框架构图如何使用?
-
1分钟教你用手机拍大片。今天我们利用框架构图,在不同的运镜方法下拍摄。·首先将手机贴近地面,拍摄人物走过的画面。·然后利用3D效果的背景衬托,将手机贴近地面,以低角度仰拍人物。·最后我们用高清画质来呈...
- 面构图的5种超实用的构图形式 前景构图,框架构图,填充构图
-
面构图的5种超实用的构图形式。为什么有的人拍摄的照片好看又舒适?仔细观察会发现他们善用构图。大家好,今天带大家了解摄影中5种超实用的面构图形式。·一、前景构图。前景是构图中的神奇要素,可以提升照片的表...
- 一看就懂!跟着马格南的大师学构图
-
马格南图片社是迄今为止全球最重要的摄影图片社,其网站包涵了太多经典的名字和照片。细细品味这些经典图片,能够学到很多有用的构图手法。跟着大师走,总不会错吧?前后景的运用这似乎是非常常见的一种手法,仔细看...
- 这才是框架构图,有想法!能给你启发么?
-
框架构图大家并不陌生,但并不是有一个框就行了。框架构图用得不好,就很死板生硬,给人感觉很假。如果你理解透了,拍出的作品不会单调。今天就给大家分享一下框架构图,你看看有哪些妙用?1.广角与长焦的应用长焦...
- 7B小模型写好学术论文,新框架告别AI引用幻觉
-
ScholarCopilot团队投稿量子位|公众号QbitAI学术写作通常需要花费大量精力查询文献引用,而以ChatGPT、GPT-4等为代表的通用大语言模型(LLM)虽然能够生成流畅文本,但...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 若依框架 (41)
- MVC框架 (46)
- spring框架 (46)
- 框架图 (58)
- bootstrap框架 (43)
- flask框架 (53)
- quartz框架 (51)
- abp框架 (47)
- jpa框架 (47)
- scrapy框架 (52)
- beego框架 (42)
- java框架spring (43)
- grpc框架 (55)
- 前端框架bootstrap (42)
- orm框架有哪些 (43)
- ppt框架 (48)
- 内联框架 (52)
- winform框架 (46)
- gui框架 (44)
- cad怎么画框架 (58)
- ps怎么画框架 (47)
- ssm框架实现登录注册 (49)
- oracle v (42)
- oracle字符串长度 (48)
- oracle提交事务 (47)