ArgEventBus 1.0.1
.NET Standard 2.0
This package targets .NET Standard 2.0. The package is compatible with this framework or higher.
.NET Framework 4.6.2
This package targets .NET Framework 4.6.2. The package is compatible with this framework or higher.
dotnet add package ArgEventBus --version 1.0.1
NuGet\Install-Package ArgEventBus -Version 1.0.1
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ArgEventBus" Version="1.0.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ArgEventBus" Version="1.0.1" />
<PackageReference Include="ArgEventBus" />
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ArgEventBus --version 1.0.1
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: ArgEventBus, 1.0.1"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ArgEventBus@1.0.1
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ArgEventBus&version=1.0.1
#tool nuget:?package=ArgEventBus&version=1.0.1
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
ArgEventBus (.NET Framework 使用指南)
事件发布订阅(本地/Redis)。支持 .NET Framework: net462 / net472 / net48,以及 .NET Standard 2.0。
功能特性
- 本地进程内事件发布与订阅(反射扫描观察者,按
Order
顺序执行) - 可选 Redis 分布式总线(跨进程/跨服务广播,支持确认机制)
- 可配置重试与超时
- 轻量、零侵入,默认日志为空实现(可接入 NLog/Serilog/log4net)
安装
- NuGet(推荐):在 Visual Studio 的 NuGet 管理器中搜索
ArgEventBus
- 或者包管理器控制台:
Install-Package ArgEventBus -Version 1.0.0
快速上手(.NET Framework)
1) 定义事件类型
public class UserRegisteredEvent
{
public string UserId { get; set; }
public DateTime RegisteredAt { get; set; }
}
2) 实现观察者(IEventObserver<T>)
using System.Threading.Tasks;
using ArgEventBus.PubSub;
public class SendWelcomeEmailObserver : IEventObserver<UserRegisteredEvent>
{
public int Order => 10;
public Task HandleEventAsync(UserRegisteredEvent e)
{
// 发送欢迎邮件
return Task.CompletedTask;
}
}
public class AddBonusPointObserver : IEventObserver<UserRegisteredEvent>
{
public int Order => 20;
public Task HandleEventAsync(UserRegisteredEvent e)
{
// 增加积分
return Task.CompletedTask;
}
}
3) 本地事件总线(进程内)
using System;
using System.Threading.Tasks;
using ArgEventBus.PubSub;
class Program
{
static void Main(string[] args)
{
// 扫描当前 AppDomain 的程序集,自动发现并注册 IEventObserver<T>
EventManager.Initialize(AppDomain.CurrentDomain.GetAssemblies());
// 发布事件(.NET Framework 可用同步等待方式)
EventManager.Instance
.PublishEventAsync(new UserRegisteredEvent
{
UserId = "u-001",
RegisteredAt = DateTime.UtcNow
})
.GetAwaiter().GetResult();
}
}
4) 分布式事件总线(Redis,可选)
using System;
using System.Threading.Tasks;
using ArgEventBus.PubSub;
class Program
{
static void Main(string[] args)
{
// 初始化 Redis(示例:本地默认端口)
RedisEventManager.Initialize("127.0.0.1:6379");
// 发布并等待确认(可选)
RedisEventManager.Instance
.PublishEventAsync(new UserRegisteredEvent
{
UserId = "u-002",
RegisteredAt = DateTime.UtcNow
}, waitForConfirmation: true, timeoutMs: 5000)
.GetAwaiter().GetResult();
}
}
依赖与日志
- 依赖:
Newtonsoft.Json
、StackExchange.Redis
(仅 Redis 功能) - 日志:默认使用
Microsoft.Extensions.Logging.Abstractions
的空实现;如需输出日志,请在项目中接入 NLog/Serilog/log4net,并在初始化时传入 logger(示例见源码)。
使用提示
- 事件分发按
IEventObserver<T>.Order
从小到大执行 - 观察者类需要存在于当前进程可扫描到的程序集中(
AppDomain.CurrentDomain.GetAssemblies()
) - Redis 通道:
rob:events:all
与rob:events:ack
- 如需依赖注入,可参考扩展方法(需要
Microsoft.Extensions.DependencyInjection
):
using Microsoft.Extensions.DependencyInjection;
using ArgEventBus.Extensions;
var services = new ServiceCollection();
services.AddEventServices();
services.AddRedisEventServices("127.0.0.1:6379");
var provider = services.BuildServiceProvider();
故障排查
- 发布多目标框架项目时需显式指定/绑定
TargetFramework
(本项目发布配置已内置) - Redis 连接失败时检查连接串、网络连通性及防火墙
.NET Core / ASP.NET Core 调用示例
以下示例基于 net6.0+
(同样适用于 .NET 5/7/8,代码几乎一致)。
1) 控制台(Generic Host)
// Program.cs
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ArgEventBus.Extensions;
using ArgEventBus.PubSub;
public class UserRegisteredEvent
{
public string UserId { get; set; }
public DateTime RegisteredAt { get; set; }
}
public class SendWelcomeEmailObserver : IEventObserver<UserRegisteredEvent>
{
public int Order => 10;
public Task HandleEventAsync(UserRegisteredEvent e)
{
Console.WriteLine($"[WelcomeEmail] {e.UserId} @ {e.RegisteredAt:O}");
return Task.CompletedTask;
}
}
public class AddBonusPointObserver : IEventObserver<UserRegisteredEvent>
{
public int Order => 20;
public Task HandleEventAsync(UserRegisteredEvent e)
{
Console.WriteLine($"[BonusPoint] {e.UserId} +100");
return Task.CompletedTask;
}
}
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddLogging(builder => builder.AddConsole());
// 本地事件(进程内)
services.AddEventServices();
// 可选:分布式事件(Redis)
services.AddRedisEventServices("127.0.0.1:6379");
})
.Build();
// 发布本地事件
var eventManager = host.Services.GetRequiredService<EventManager>();
await eventManager.PublishEventAsync(new UserRegisteredEvent
{
UserId = "u-core-001",
RegisteredAt = DateTime.UtcNow
});
// 发布分布式事件(等待确认,可选)
var redisEventManager = host.Services.GetRequiredService<RedisEventManager>();
await redisEventManager.PublishEventAsync(new UserRegisteredEvent
{
UserId = "u-core-002",
RegisteredAt = DateTime.UtcNow
}, waitForConfirmation: true, timeoutMs: 5000);
2) ASP.NET Core(最小 API)
// Program.cs
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ArgEventBus.Extensions;
using ArgEventBus.PubSub;
var builder = WebApplication.CreateBuilder(args);
// 注册事件服务
builder.Services.AddEventServices();
builder.Services.AddRedisEventServices("127.0.0.1:6379");
var app = builder.Build();
app.MapPost("/register", async (EventManager em, RedisEventManager rem, UserRegisteredEvent e) =>
{
await em.PublishEventAsync(e); // 本地分发
await rem.PublishEventAsync(e, waitForConfirmation: true); // 分布式分发(可选)
return Results.Ok();
});
app.Run();
public record UserRegisteredEvent(string UserId, DateTime RegisteredAt);
public class SendWelcomeEmailObserver : IEventObserver<UserRegisteredEvent>
{
public int Order => 10;
public Task HandleEventAsync(UserRegisteredEvent e)
{
Console.WriteLine($"[WelcomeEmail] {e.UserId} @ {e.RegisteredAt:O}");
return Task.CompletedTask;
}
}
public class AddBonusPointObserver : IEventObserver<UserRegisteredEvent>
{
public int Order => 20;
public Task HandleEventAsync(UserRegisteredEvent e)
{
Console.WriteLine($"[BonusPoint] {e.UserId} +100");
return Task.CompletedTask;
}
}
最佳实践与注意事项
🔧 资源管理
- Redis 连接释放:
RedisEventManager
实现了IDisposable
接口,建议在应用程序关闭时调用Dispose()
方法释放 Redis 连接资源 - 单例生命周期:在依赖注入容器中,事件管理器被注册为单例,容器会自动处理资源释放
// 手动释放示例(通常不需要,容器会自动处理)
using var redisManager = RedisEventManager.Instance;
// 使用完毕后自动释放
🛡️ 异常处理与参数验证
- 参数验证:所有公共方法都包含完整的参数验证,传入
null
值会抛出ArgumentNullException
- 连接字符串验证:Redis 连接字符串不能为空,否则会在初始化时抛出异常
- 重试机制:事件处理失败时会自动重试(默认最多 3 次),可在初始化时配置
// 正确的初始化方式
try
{
RedisEventManager.Initialize("127.0.0.1:6379", logger, maxRetryCount: 5, retryDelayMs: 1000);
}
catch (ArgumentException ex)
{
// 处理连接字符串无效的情况
Console.WriteLine($"Redis 初始化失败: {ex.Message}");
}
⚡ 性能优化
- 反射缓存:事件管理器内部缓存了观察者的反射信息,避免重复反射调用
- 并发安全:使用
ConcurrentDictionary
和ConcurrentBag
确保线程安全 - 超时控制:事件处理有超时机制(默认 10 秒),防止长时间阻塞
🔄 重新初始化
- 安全重新初始化:调用
Initialize
方法会安全地释放现有实例并创建新实例 - 线程安全:初始化过程使用锁机制,确保多线程环境下的安全性
// 重新配置 Redis 连接
RedisEventManager.Initialize("new-redis-server:6379", logger);
📊 监控与日志
- 详细日志:提供了丰富的日志信息,包括性能指标、错误详情和调试信息
- 事件确认:Redis 模式支持事件确认机制,可以监控事件是否被成功处理
// 启用详细日志
var logger = serviceProvider.GetService<ILogger<RedisEventManager>>();
RedisEventManager.Initialize(connectionString, logger);
// 使用确认机制监控事件处理
bool confirmed = await redisManager.PublishEventAsync(eventData,
waitForConfirmation: true,
timeoutMs: 10000);
if (!confirmed)
{
// 处理确认超时的情况
logger.LogWarning("事件处理确认超时");
}
🚨 常见问题与解决方案
1. 观察者未被发现
问题:定义了观察者但事件发布时没有执行 解决:
- 确保观察者类在可扫描的程序集中
- 检查观察者是否正确实现了
IEventObserver<T>
接口 - 确保观察者类是公共的且不是抽象类
2. Redis 连接问题
问题:Redis 事件发布失败 解决:
- 检查 Redis 服务是否运行
- 验证连接字符串格式
- 检查网络连通性和防火墙设置
- 查看日志中的详细错误信息
3. 内存泄漏
问题:长时间运行后内存占用过高 解决:
- 确保在应用程序关闭时正确释放资源
- 避免在观察者中创建大量未释放的对象
- 使用依赖注入容器管理生命周期
4. 性能问题
问题:事件处理速度慢 解决:
- 检查观察者的
HandleEventAsync
方法是否有耗时操作 - 考虑使用异步操作而不是同步阻塞
- 调整超时和重试参数
- 监控日志中的性能指标
📈 版本更新说明
v1.0.0 改进内容
- ✅ 修复了资源泄漏问题,添加了
IDisposable
实现 - ✅ 改进了线程安全性,完善了单例模式实现
- ✅ 增强了参数验证,防止运行时异常
- ✅ 优化了错误处理和异常信息
- ✅ 完善了 XML 文档注释
- ✅ 提升了代码质量和可维护性
技术支持
如果遇到问题或需要技术支持,请:
- 查看日志输出中的详细错误信息
- 确认按照最佳实践进行配置
- 检查依赖项版本兼容性
- 参考本文档中的常见问题解决方案
ArgEventBus - 轻量级、高性能的 .NET 事件总线解决方案
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 is compatible. net463 was computed. net47 was computed. net471 was computed. net472 is compatible. net48 is compatible. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
-
.NETFramework 4.6.2
- Microsoft.Extensions.DependencyInjection (>= 6.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.5)
- Newtonsoft.Json (>= 13.0.3)
- StackExchange.Redis (>= 2.6.122)
- System.ValueTuple (>= 4.6.1)
-
.NETFramework 4.7.2
- Microsoft.Extensions.DependencyInjection (>= 6.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.5)
- Newtonsoft.Json (>= 13.0.3)
- StackExchange.Redis (>= 2.6.122)
- System.ValueTuple (>= 4.6.1)
-
.NETFramework 4.8
- Microsoft.Extensions.DependencyInjection (>= 6.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.5)
- Newtonsoft.Json (>= 13.0.3)
- StackExchange.Redis (>= 2.6.122)
- System.ValueTuple (>= 4.6.1)
-
.NETStandard 2.0
- Microsoft.Extensions.DependencyInjection (>= 6.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.5)
- Newtonsoft.Json (>= 13.0.3)
- StackExchange.Redis (>= 2.6.122)
- System.ValueTuple (>= 4.6.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.