自己造轮子是一件苦差事。 现在,您可以专注于业务开发,仅需集成 ⭐️Furion⭐️ 即可。
Skip to main content

22. 事件总线

v2.20+ 版本说明

Furion v2.20+ 版本后采用 Jaina 事件总线替换原有的 EventBus😶查看新文档

22.1 什么是事件总线

事件总线是对发布-订阅模式的一种实现。它是一种集中式事件处理机制,允许不同的组件之间进行彼此通信而又不需要相互依赖,达到一种解耦的目的。

我们来看看事件总线的处理流程:

22.2 MessageCenter 消息中心

Furion 框架中,实现了一种轻量级的事件总线实现机制:MessageCenter(消息中心),MessageCenter 采用字符串消息机制进行广播, 可以在绝大多数中小型项目中发挥作用,缺点是消息处理是在主线程中完成并且消息不支持分布式存储。

另外,MessageCenter 支持单播、多播发布及多订阅。如图所示:

22.2.1 注册 轻量级事件总线服务

如果想使用 MessageCenter 轻量级事件总线,只需要在 Startup.cs 中注册服务即可,如:

public void ConfigureServices(IServiceCollection services)
{
services.AddSimpleEventBus();
}

22.2.2 定义订阅处理程序

MessageCenter 是根据 MesseageId 消息 Id 来触发对应的处理程序的,所以我们需要定义 订阅处理程序类,该类需实现 ISubscribeHandler 接口,如:

public class UserChangeSubscribeHandler : ISubscribeHandler
{
// 定义一条消息
[SubscribeMessage("create:user")]
public void CreateUser(string eventId, object payload)
{
Console.WriteLine("我是"+eventId);
}

// 多条消息共用同一个处理程序
[SubscribeMessage("delete:user")]
[SubscribeMessage("remove:user")]
public void RemoveUser(string eventId, object payload)
{
Console.WriteLine("我是"+eventId);
}

// 支持异步
[SubscribeMessage("delete:user")]
public async Task SupportAsync(string eventId, object payload)
{
await MethodAsync();
}
}

22.2.3 发布消息

定义好订阅处理程序后,我们就可以在程序任何地方进行广播消息,事件总线会自动根据 消息 Id 触发对应的处理程序方法:

MessageCenter.Send("create:user", new User {}); // => 打印:我是create:user

MessageCenter.Send("delete:user", new User {}); // => 打印:我是delete:user
MessageCenter.Send("remove:user", new User {}); // => 打印:我是remove:user

22.2.4 直接订阅消息

在上面的例子中,我们需要创建 ISubscribeHandler 的派生类进行相关配置才能实现订阅处理。

在某些特殊情况下,可能只需要订阅一次即可。所以,在 Furion 框架中,为了更简便的订阅消息,也提供了 MessageCenter.Subscribe<T> 静态方法,如:

MessageCenter.Subscribe<User>("create:user", (i,p) => {
// do something。。。
});

22.3 同步方式执行

默认情况下,事件总线总是采用新线程方式执行,但是我们可以配置为同步方式,如:

MessageCenter.Send("create:user", isSync: true);

22.4 关于依赖注入

Furion 框架中,事件总线是不支持构造函数注入的,而且构造函数也只会执行一次。所以需要用到服务,应该通过静态类解析,App.GetService<xx>()Db.GetRepository<XX>()

public class UserChangeSubscribeHandler : ISubscribeHandler
{
public UserChangeSubscribeHandler()
{
// 不支持这里解析服务!!!!!!!!!!!
}

// 定义一条消息
[SubscribeMessage("create:user")]
public void CreateUser(string eventId, object payload)
{
// 创建一个作用域,对象使用完成自动释放
Scoped.Create((_, scope) =>
{
var services = scope.ServiceProvider;

var repository = Db.GetRepository<Person>(services); // services 传递进去
var someService = App.GetService<ISomeService>(services); // services 传递进去
var otherService = services.GetService<IOtherService>(); // 直接用 services 解析
});
}
}
关于 App.GetService<TService>() 解析服务

在高频定时任务中调用App.GetService(TService),可能会出现内存无法回收的情况,建议始终使用scope.ServiceProvider.GetService(TService)来获取TService

数据库操作注意

如果作用域中对数据库有任何变更操作,需手动调用 SaveChanges 或带 Now 结尾的方法。也可以使用 Scoped.CreateUow(handler) 代替。

关于依赖注入

ISubscribeHandler 接口主要是用来查找定义事件对象的,也就是它的实现类并未提供依赖注入功能,所以在实现类并不支持构造函数注入依赖项。

22.5 反馈与建议

与我们交流

给 Furion 提 Issue