Lord.Service
7.10.6
dotnet add package Lord.Service --version 7.10.6
NuGet\Install-Package Lord.Service -Version 7.10.6
<PackageReference Include="Lord.Service" Version="7.10.6" />
<PackageVersion Include="Lord.Service" Version="7.10.6" />
<PackageReference Include="Lord.Service" />
paket add Lord.Service --version 7.10.6
#r "nuget: Lord.Service, 7.10.6"
#:package Lord.Service@7.10.6
#addin nuget:?package=Lord.Service&version=7.10.6
#tool nuget:?package=Lord.Service&version=7.10.6
LordService 完整使用文档
企业级推送与消息队列集成库
支持钉钉/企业微信/飞书消息推送、RabbitMQ/RocketMQ 发布订阅与死信队列、Redis 分布式缓存与锁、AES-GCM 认证加密、多日志框架。
版本:v7.10.5 | 目标框架:net6.0 / net8.0 / net9.0 / net10.0
目录
安装
dotnet add package Lord.Service
完整 appsettings.json 配置参考
以下是所有模块的完整配置示例,实际使用时按需选取:
{
"RabbitMQ": {
"HostName": "localhost",
"VirtualHost": "/",
"UserName": "guest",
"Password": "guest",
"Prefix": "MyApp",
"ServiceName": "订单服务"
},
"RocketMQ": {
"NameServerAddress": "localhost:9876",
"Group": "LordService",
"Prefix": "MyApp",
"ServiceName": "订单服务",
"RequestTimeoutSeconds": 30,
"ConsumeBatchSize": 1,
"RetryCount": 5
},
"Redis": {
"ConnectionString": "localhost:6379",
"Prefix": "MyApp",
"Database": 0,
"ConnectTimeout": 5,
"SyncTimeout": 5,
"AllowAdmin": false,
"Ssl": false,
"Password": "",
"ClientName": "LordService"
},
"Encryption": {
"PublicKeyOrKey": "你的AES密钥Base64字符串",
"PrivateKeyOrIV": "备用IV的Base64字符串",
"EncryptType": "Aes"
},
"DingTalk": {
"Alias": "系统通知群",
"Url": "https://oapi.dingtalk.com",
"Token": "your_dingtalk_access_token",
"Secret": "your_dingtalk_secret"
},
"WeChatPush": {
"Alias": "运维群",
"Url": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send",
"Token": "your_wechat_key"
},
"LarkPush": {
"Alias": "开发群",
"Url": "https://open.feishu.cn/open-apis/bot/v2/hook",
"Token": "your_lark_token",
"Secret": "your_lark_secret"
},
"DingApp": {
"AppKey": "your_ding_app_key",
"AppSecret": "your_ding_app_secret",
"AgentId": "your_agent_id"
},
"WeChatOfficial": {
"AppId": "your_app_id",
"AppSecret": "your_app_secret",
"Token": "your_token",
"EncodingAesKey": null
},
"WeChatMiniProgram": {
"AppId": "your_mini_program_appid",
"AppSecret": "your_mini_program_secret",
"BaseUrl": "https://api.weixin.qq.com"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
多群组配置
钉钉/企业微信/飞书支持多群组,使用数组配置:
{
"DingTalk": [
{
"Alias": "生产告警群",
"Url": "https://oapi.dingtalk.com",
"Token": "prod_alert_token",
"Secret": "prod_alert_secret"
},
{
"Alias": "运维群",
"Url": "https://oapi.dingtalk.com",
"Token": "ops_token",
"Secret": "ops_secret"
}
]
}
RabbitMQ 多连接池
不同业务可使用不同的 RabbitMQ 连接池:
{
"RabbitMQ": {
"HostName": "localhost",
"VirtualHost": "/",
"UserName": "guest",
"Password": "guest",
"Prefix": "MyApp"
},
"OrderMQ": {
"HostName": "mq-order.internal",
"VirtualHost": "/order",
"UserName": "order_user",
"Password": "order_pass",
"Prefix": "OrderApp"
},
"OrderQueue": {
"QueueName": "Order_Process_Queue",
"ExchangeName": "Order_Process_Topic",
"RouteKey": "Service.order.process",
"MQPool": "OrderMQ"
}
}
快速上手
最简配置(内存缓存 + NLog + AES)
// Program.cs
builder.Services.AddLordService(lord => lord.UseDefaults());
UseDefaults() 等价于:
builder.Services.AddLordService(lord => lord
.UseHttp() // RestSharp,超时 1 分钟
.UseLogging(log => log.UseNLog()) // NLog 日志
.UseCache(cache => cache.UseMemoryCache()) // 内存缓存
.UseEncryption(enc => enc.UseAES(a => a.FromConfiguration())) // AES-GCM
);
生产推荐配置
builder.Services.AddLordService(lord => lord
.UseHttp(h => h.UseNetHttp().WithTimeout(TimeSpan.FromSeconds(30)))
.UseLogging(log => log.UseNLog())
.UseCache(cache => cache.UseRedis(r => r.FromConfiguration("Redis")))
.UseEncryption(enc => enc.UseAES(a => a.FromConfiguration("Encryption")))
.UseMQ(mq => mq
.UseDeadLetter()
.UseRabbitMQ(r => r.FromConfiguration("RabbitMQ")))
.UsePush(push => push
.AddDingTalk(d => d.FromConfiguration("DingTalk"))
.AddWeChat(w => w.FromConfiguration("WeChatPush"))
.AddLark(l => l.FromConfiguration("LarkPush")))
);
模块详解
1. 缓存(Redis / Memory)
注册方式
// 方式1:从配置加载 Redis
.UseCache(cache => cache.UseRedis(r => r.FromConfiguration("Redis")))
// 方式2:直接传连接字符串
.UseCache(cache => cache.UseRedis("localhost:6379"))
// 方式3:使用内存缓存
.UseCache(cache => cache.UseMemoryCache())
// 方式4:自定义缓存实现
.UseCache(cache => cache.UseCustom<MyRedisCache>(c => c.FromConfiguration("Redis").AsSingleton()))
// 方式5:直接传实例
.UseCache(cache => cache.UseCustom(new MyRedisCache()))
Redis 配置项说明
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ConnectionString |
string | localhost:6379 |
Redis 连接字符串 |
Prefix |
string | "" |
Key 前缀,用于多系统共用 Redis 隔离 |
Database |
int | 0 |
Redis 数据库编号 |
ConnectTimeout |
int | 5 |
连接超时(秒) |
SyncTimeout |
int | 5 |
同步操作超时(秒) |
AllowAdmin |
bool | false |
是否允许管理操作(如 FlushDatabase) |
Ssl |
bool | false |
是否启用 SSL |
Password |
string? | null |
Redis 密码 |
ClientName |
string? | LordService |
客户端名称 |
业务使用
public class UserService
{
private readonly ICache _cache;
public UserService(ICache cache) => _cache = cache;
// 基本读写
public async Task<User?> GetUserAsync(int userId)
{
return await _cache.GetItemAsync<User>($"user:{userId}");
}
public async Task SetUserAsync(int userId, User user)
{
await _cache.SetItemAsync($"user:{userId}", user, TimeSpan.FromMinutes(30));
}
// 缓存穿透保护:AddOrGetCacheItem 保证同 key 只回源一次
public async Task<User> GetOrLoadUserAsync(int userId)
{
return await _cache.AddOrGetCacheItemAsync(
$"user:{userId}",
async () => await LoadFromDbAsync(userId), // 只在缓存缺失时执行
TimeSpan.FromMinutes(30),
isSlidingExpiration: true); // 滑动过期:每次读取续期
}
// 批量操作(自动分批,每批 500 条)
public async Task SetUsersBatchAsync(Dictionary<string, User> users)
{
await _cache.SetBatchAsync(users, TimeSpan.FromHours(1));
}
public async Task<Dictionary<string, User?>> GetUsersBatchAsync(IEnumerable<string> keys)
{
return await _cache.GetBatchAsync<User>(keys);
}
// 分布式锁
public async Task DoWithLockAsync(string resourceKey, Func<Task> action)
{
await using var handle = await _cache.DistributedLock.AcquireAsync(
$"lock:{resourceKey}",
TimeSpan.FromMinutes(1));
if (handle.IsAcquired)
{
await action();
}
}
// 自动续期锁(看门狗模式,适合长时间任务)
public async Task DoWithAutoRenewalLockAsync(string resourceKey, Func<Task> action)
{
await using var handle = await _cache.DistributedLock.AcquireWithRenewalAsync(
$"lock:{resourceKey}",
TimeSpan.FromSeconds(30)); // 锁 30 秒,自动续期
if (handle.IsAcquired)
{
await action(); // 即使任务超过 30 秒,锁也会自动续期
}
}
}
安全提示:同步
AddOrGetCacheItem的回源等待有 30 秒超时保护,避免cachePopulate卡住时线程池饥饿。生产环境推荐使用异步AddOrGetCacheItemAsync。
2. 消息队列(RabbitMQ)
注册方式
.UseMQ(mq => mq
.UseDeadLetter() // 全局启用死信队列(所有队列自动生成 per-queue DLX)
.UseRabbitMQ(r => r
.FromConfiguration("RabbitMQ") // 从配置加载
// 或:.WithConnection("host", "/vhost", "user", "pass") // 直接配置
.WithPrefix("MyApp") // 队列名前缀,隔离多系统
.WithServiceName("订单服务")) // 连接名称,便于运维识别
)
RabbitMQ 配置项说明
| 配置项 | 说明 |
|---|---|
HostName |
RabbitMQ 主机地址 |
VirtualHost |
虚拟主机 |
UserName |
用户名 |
Password |
密码 |
Prefix |
队列/交换机/路由键前缀 |
ServiceName |
连接名称(便于 RabbitMQ 管理端识别) |
IMQHub 简化 API(推荐)
// 定义消息(队列名自动从类型名推导)
public record OrderCreated(string OrderId, decimal Amount);
// 发布
await _hub.PublishAsync(new OrderCreated("ORD-001", 99.9m));
// 订阅
await _hub.SubscribeAsync<OrderCreated>(async (msg, sp) =>
{
var logger = sp.GetRequiredService<ILogger<Program>>();
logger.LogInformation("收到订单: {OrderId}, 金额: {Amount}", msg.OrderId, msg.Amount);
return true; // true=Ack, false=Reject(触发重试或死信)
});
// 死信订阅
await _hub.SubscribeDeadLetterAsync<OrderCreated>(async (msg, sp) =>
{
var logger = sp.GetRequiredService<ILogger<Program>>();
logger.LogWarning("订单消息进入死信: {OrderId}", msg.OrderId);
return true;
});
IMQHub 完整 API
// 发布
await hub.PublishAsync(message); // 类型推导
await hub.PublishAsync(message, cfg => cfg.Qos = 20); // 自定义配置
await hub.PublishAsync("custom_name", message); // 自定义队列名
await hub.PublishAsync("custom_name", message, cfg => { }); // 自定义名 + 配置
await hub.PublishAsync(rawJsonString); // 原始字符串
// 订阅(5 种重载)
await hub.SubscribeAsync<T>(msg => Task.FromResult(true)); // 最简
await hub.SubscribeAsync<T>(msg => true); // 同步
await hub.SubscribeAsync<T>(async (msg, sp) => true); // 注入 ServiceProvider
await hub.SubscribeAsync<T>(handler, cfg => { }); // 自定义配置
await hub.SubscribeAsync<T>("name", handler); // 自定义队列名
// 死信订阅
await hub.SubscribeDeadLetterAsync<T>(async (msg, sp) => true);
await hub.SubscribeDeadLetterAsync<T>("name", handler, cfg => { });
MQ 拦截器
.UseMQ(mq => mq
.AddFilter<MyQueueArgsFilter>() // IMQQueueArgsFilter: 修改队列参数
.AddFilter<MyDeadLetterFilter>() // IMQDeadLetterFilter: 修改死信配置
.AddFilter<MyCryptoFilter>() // IMQCryptoFilter: 自定义加解密
.AddFilter<MyJsonFilter>() // IMQJsonFilter: 自定义序列化
.AddFilter<MyExceptionFilter>() // IMQExceptionFilter: 异常处理
.UseRabbitMQ(r => r.FromConfiguration("RabbitMQ"))
)
消费端看门狗(RabbitMQ)
RabbitMQ 消费端内置看门狗机制:
- 心跳检测:每 10 秒检查 consumer 存活状态
- 自动恢复:consumer 关闭或 Channel 断开时自动重建
- 指数退避:恢复失败时 1s→2s→4s→8s→16s→30s 退避重试
- 低频持续:连续失败 20 次后进入低频模式(每 60 秒一次),永不永久停止
- 超时保护:handler 执行超过 3 分钟自动 Reject 并取消后台任务
2.5 消息队列(RocketMQ)
说明:v7.11.0 新增 RocketMQ 基础支持,采用 NewLife.RocketMQ 客户端,适用于信创场景和国产化 MQ 需求。
注册方式
.UseMQ(mq => mq
.UseRocketMQ(r => r
.FromConfiguration("RocketMQ") // 从配置加载
// 或:.WithNameServer("127.0.0.1:9876") // 直接配置 NameServer
.WithGroup("LordService") // 默认生产/消费组
.WithPrefix("MyApp") // Topic/Tag/ConsumerGroup 前缀
.WithServiceName("订单服务")) // 连接名称,便于运维识别
)
RocketMQ 配置项说明
| 配置项 | 说明 |
|---|---|
NameServerAddress |
NameServer 地址,如 127.0.0.1:9876 |
Group |
默认生产/消费组名 |
Prefix |
Topic/Tag/ConsumerGroup/ProducerGroup 前缀 |
ServiceName |
服务名称(便于识别) |
RequestTimeoutSeconds |
请求超时时间(秒) |
ConsumeBatchSize |
消费批量大小 |
RetryCount |
最大重试次数 |
RocketMQ 与 RabbitMQ 字段映射
| 通用字段 | RocketMQ 映射 | 说明 |
|---|---|---|
ExchangeName |
Topic |
消息主题 |
RouteKey |
Tag |
消息标签过滤 |
QueueName |
ConsumerGroup |
消费者组 |
EnableDeadLetter |
EnableFailedMessage |
失败消息能力 |
IMQHub 简化 API(推荐)
// 定义消息(Topic/Tag/ConsumerGroup 自动从类型名推导)
public record OrderCreated(string OrderId, decimal Amount);
// 发布(自动生成 Topic: OrderCreated_Topic, Tag: Service.ordercreated)
await _hub.PublishAsync(new OrderCreated("ORD-001", 99.9m));
// 订阅(自动生成 ConsumerGroup: OrderCreated_ConsumerGroup)
await _hub.SubscribeAsync<OrderCreated>(async (msg, sp) =>
{
var logger = sp.GetRequiredService<ILogger<Program>>();
logger.LogInformation("收到订单: {OrderId}, 金额: {Amount}", msg.OrderId, msg.Amount);
return true; // true=消费成功, false=触发 RocketMQ 重试
});
// 失败消息订阅(通用接口,避免 RabbitMQ DLX 术语)
await _hub.SubscribeFailedAsync<OrderCreated>(async (msg, sp) =>
{
logger.LogWarning("订单处理失败: {OrderId}", msg.OrderId);
return true; // 确认已处理
});
失败消息处理
RocketMQ 使用 %DLQ%{ConsumerGroup} 约定 Topic 存储失败消息:
// 消费失败时返回 false,RocketMQ 自动重试
await _hub.SubscribeAsync<OrderCreated>(async msg =>
{
try
{
// 业务处理
return true; // 成功
}
catch
{
return false; // 失败,进入 RocketMQ 重试机制
}
});
// 订阅失败消息
await _hub.SubscribeFailedAsync<OrderCreated>(async msg =>
{
// 处理进入 %DLQ%{ConsumerGroup} 的失败消息
return true;
});
高级功能
RocketMQ 支持以下高级功能(通过 IRocketMQAdvancedPush 和 IRocketMQAdvancedReceive 接口):
1. 事务消息
// 获取高级发布接口
var push = await _factory.GetPushServiceAsync<OrderCreated>();
if (push is IRocketMQAdvancedPush advancedPush)
{
// 发送事务消息(半消息)
var sendResult = await advancedPush.PublishTransactionAsync(new OrderCreated("ORD-001", 99.9m));
try
{
// 执行本地事务
await _dbContext.SaveOrderAsync(new Order { OrderId = "ORD-001" });
// 提交事务
await advancedPush.EndTransactionAsync(sendResult, commit: true);
}
catch
{
// 回滚事务
await advancedPush.EndTransactionAsync(sendResult, commit: false);
}
}
2. 顺序消息
// 发送顺序消息(相同 orderKey 的消息进入同一队列)
await advancedPush.PublishOrderAsync(new OrderCreated("ORD-001", 99.9m), orderKey: "ORD-001");
3. 延迟消息
// 发送延迟消息(延迟级别 1-18)
await advancedPush.PublishDelayAsync(new OrderCreated("ORD-001", 99.9m), delayLevel: 3);
// 延迟级别对应:1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
4. Request-Reply 模式
// 发送请求并等待响应
var response = await advancedPush.RequestAsync<OrderQuery, OrderResult>(
new OrderQuery("ORD-001"),
timeout: 5000);
5. Tag/SQL92 过滤
// 获取高级消费接口
var receive = await _factory.GetReceiveServiceAsync<OrderCreated>();
if (receive is IRocketMQAdvancedReceive advancedReceive)
{
// Tag 过滤
await advancedReceive.SubscribeWithFilterAsync<OrderCreated>(
async msg => { /* 处理消息 */ return true; },
RocketMQExpressionType.Tag,
"TagA || TagB");
// SQL92 过滤
await advancedReceive.SubscribeWithFilterAsync<OrderCreated>(
async msg => { /* 处理消息 */ return true; },
RocketMQExpressionType.SQL92,
"amount > 100 AND status = 'ACTIVE'");
}
6. 多 Topic 订阅
// 一个 Consumer 同时消费多个 Topic
await advancedReceive.SubscribeMultipleTopicsAsync<OrderCreated>(
async msg => { /* 处理消息 */ return true; },
topics: "Topic1;Topic2;Topic3");
7. Pop 消费模式(RocketMQ 5.0+)
// 轻量消费模式,无需客户端 Rebalance
await advancedReceive.PopConsumeAsync<OrderCreated>(
async msg => { /* 处理消息 */ return true; },
maxNums: 32,
invisibleTime: 30000);
云厂商支持
RocketMQ 支持主流云厂商的托管服务:
{
"RocketMQ": {
"NameServerAddress": "MQ_INST_xxx.aliyuncs.com:80",
"CloudProvider": "Aliyun",
"AccessKey": "your_access_key",
"SecretKey": "your_secret_key",
"InstanceId": "MQ_INST_xxx"
}
}
| 云厂商 | CloudProvider 值 |
必需配置 |
|---|---|---|
| 阿里云 | Aliyun |
AccessKey, SecretKey, InstanceId |
| 华为云 | Huawei |
AccessKey, SecretKey, InstanceId, EnableSsl |
| 腾讯云 | Tencent |
AccessKey, SecretKey, Namespace |
| Apache ACL | Apache |
AccessKey, SecretKey |
消费端看门狗(RocketMQ)
RocketMQ 消费端内置看门狗机制(RocketMQSubscriber 和 RocketMQReceive 均支持):
- 心跳检测:每 10 秒检查消费活动状态
- 停滞检测:超过 5 分钟无消费活动,判定 consumer 可能已死,触发恢复
- 自动恢复:Dispose 旧 Consumer + 创建新 Consumer + Start
- 指数退避:恢复失败时 1s->2s->4s->8s->16s->30s 退避重试
- 低频持续:连续失败 20 次后进入低频模式(每 60 秒一次),永不永久停止
看门狗默认启用,无需额外配置。详见 MQ 看门狗使用指南。
注意事项
- 第一版限制:事务消息、顺序消息、延迟消息、ACL 等高级特性通过扩展接口提供,需要手动转换接口类型
- Topic 预创建:生产环境建议由运维预先创建 Topic,不依赖自动创建
- 多 Provider 并存:同一容器不能同时启用 RabbitMQ 和 RocketMQ
- 失败消息:推荐使用
IMQFailedMessageHub通用接口,避免依赖特定 MQ 术语
2.6 IMQProvider 统一入口(推荐)
v7.10.4 新增:
IMQProvider是 MQ 的统一提供者接口,一站式获取所有 MQ 服务。 支持细粒度能力检查,避免"静默降级"问题(如 RabbitMQ 的事务消息实际是普通消息)。
注册方式
.UseMQ(mq => mq
// 方式一:直接指定引擎(同时注册 IMQProvider)
.UseMQProvider(MQProviderType.RocketMQ)
// 或:.UseMQProvider(MQProviderType.RabbitMQ)
// 方式二:先配置引擎,再注册 Provider(可自定义配置)
.UseRocketMQ(r => r.FromConfiguration("RocketMQ"))
.UseMQProvider(MQProviderType.RocketMQ)
)
一站式获取所有服务
public class OrderService
{
private readonly IMQProvider _provider;
public OrderService(IMQProvider provider) => _provider = provider;
// 场景 1:IMQHub(日常发布订阅,类型/Topic 驱动)
public async Task PublishWithHub(OrderEvent order)
{
var hub = _provider.GetHub();
await hub.PublishAsync(order); // 类型自动推导队列名
}
// 场景 2:IMQPublisher(基础发布)
public async Task PublishWithPublisher(OrderEvent order)
{
var publisher = _provider.GetPublisher();
await publisher.PublishAsync("OrderTopic", order);
}
// 场景 3:IMQSubscriber(基础订阅)
public async Task SubscribeOrders()
{
var subscriber = _provider.GetSubscriber();
await subscriber.SubscribeAsync<OrderEvent>("OrderTopic", "OrderGroup", async order => {
return true;
});
}
// 场景 4:IMQFactory(按配置节获取)
public async Task PublishWithFactory(OrderEvent order)
{
var factory = _provider.GetFactory();
var push = await factory.GetPushServiceAsync("OrderMQ");
await push.PublishAsync(order);
}
}
细粒度能力检查
IMQProvider 提供 5 个能力标志,业务代码可以提前检查,避免使用不支持的功能:
| 能力标志 | RabbitMQ | RocketMQ | 说明 |
|---|---|---|---|
SupportsTransactionMessages |
❌ | ✅ | 事务消息(半消息 + 二次确认) |
SupportsDelayedMessages |
❌ | ✅ | 延迟消息(RocketMQ 18 级) |
SupportsRequestReply |
❌ | ✅ | Request-Reply 模式 |
SupportsSql92Filtering |
❌ | ✅ | SQL92 过滤表达式 |
SupportsOrderedMessages |
❌ | ✅ | 严格顺序消息 |
public class PaymentService
{
private readonly IMQProvider _provider;
public PaymentService(IMQProvider provider) => _provider = provider;
// ✅ 事务消息 + 能力检查
public async Task SendTransactionMessage(PaymentEvent payment)
{
if (!_provider.SupportsTransactionMessages)
{
throw new NotSupportedException(
$"当前 MQ 引擎 ({_provider.ProviderType}) 不支持事务消息,请切换到 RocketMQ");
}
var publisher = _provider.GetAdvancedPublisher()
?? throw new InvalidOperationException("无法获取高级发布者");
var result = await publisher.PublishTransactionAsync("PaymentTopic", payment);
try
{
await _db.SavePaymentAsync(payment); // 本地事务
await publisher.EndTransactionAsync(result, commit: true);
}
catch
{
await publisher.EndTransactionAsync(result, commit: false);
throw;
}
}
// ✅ 延迟消息 + 能力检查(RabbitMQ 降级为调度框架)
public async Task SendDelayMessage(PaymentEvent payment, int delayMinutes)
{
if (!_provider.SupportsDelayedMessages)
{
// RabbitMQ:使用调度框架替代
await _scheduler.ScheduleAsync(
() => _provider.GetPublisher().PublishAsync("PaymentTopic", payment),
TimeSpan.FromMinutes(delayMinutes));
return;
}
// RocketMQ:原生延迟消息(1-18 级)
var publisher = _provider.GetAdvancedPublisher()!;
await publisher.PublishDelayAsync("PaymentTopic", payment, delayLevel: 3);
}
}
性能说明
- 能力检查是常量属性,零运行时开销
GetHub()/GetFactory()内部懒加载缓存,只解析一次 DIGetPublisher()/GetSubscriber()每次返回新实例(Transient 语义),内部连接池(Producer/Consumer)复用,实例创建成本极低- Provider 本身是 Singleton,全应用共享一个实例
未来扩展(MQTT 等)
新增 MQ 引擎只需:
- 在
MQProviderType枚举中新增成员(如MQTT = 2) - 实现
IMQProvider(如MQTTProvider) - 实现对应的 Hub/Publisher/Subscriber 适配器
- 注册时一行切换,业务代码零改动
3. 消息推送(钉钉/企业微信/飞书)
注册方式
.UsePush(push => push
// 钉钉机器人
.AddDingTalk(d => d
.FromConfiguration("DingTalk") // 从配置加载
// 或:.AddGroup("告警群", "token", "secret") // 直接添加群组
)
// 企业微信机器人
.AddWeChat(w => w
.FromConfiguration("WeChatPush")
// 或:.AddGroup("运维群", "key")
)
// 飞书机器人
.AddLark(l => l
.FromConfiguration("LarkPush")
// 或:.AddGroup("开发群", "token", "secret")
)
// 钉钉应用推送(工作通知)
.AddDingApp(a => a
.FromConfiguration("DingApp")
// 或:.WithCredentials("appKey", "appSecret", "agentId")
)
)
业务使用
public class NotificationService
{
private readonly IDingTalkApiFactory _dingFactory;
private readonly IWeChatApiFactory _weChatFactory;
private readonly ILarkApiFactory _larkFactory;
public NotificationService(
IDingTalkApiFactory dingFactory,
IWeChatApiFactory weChatFactory,
ILarkApiFactory larkFactory)
{
_dingFactory = dingFactory;
_weChatFactory = weChatFactory;
_larkFactory = larkFactory;
}
// 推送到默认群
public async Task<bool> NotifyDingTalkAsync(string content)
{
var push = _dingFactory.GetPushService();
return await push.PushAsync(content);
}
// 推送到指定群(按 Alias)
public async Task<bool> NotifyDingTalkGroupAsync(string alias, string content)
{
var push = _dingFactory.GetPushService(alias);
return await push.PushAsync(content);
}
// 使用消息格式化器
public async Task<bool> NotifyWithFormatAsync()
{
var push = _dingFactory.GetPushService();
return await push.PushAsync(format => format.Text("服务器 CPU 超过 90%"));
}
// 获取所有推送服务
public async Task BroadcastAsync(string content)
{
foreach (var push in _dingFactory.GetAllPushService())
{
await push.PushAsync(content);
}
}
}
安全提示:推送失败日志已自动脱敏,不会将完整消息内容写入日志。
4. 加密(AES-GCM / RSA)
注册方式
// AES-GCM(推荐,默认)
.UseEncryption(enc => enc.UseAES(a => a
.FromConfiguration("Encryption") // 从配置加载
// 或:.WithKeys("base64_key", "base64_iv") // 直接配置
))
// RSA
.UseEncryption(enc => enc.UseRSA(r => r
.FromConfiguration("Encryption")
// 或:.WithKeys("public_key_xml", "private_key_xml")
))
// DES(已标记 Obsolete,仅用于历史数据解密,不推荐生产新用)
// .UseEncryption(enc => enc.UseDES(d => d.FromConfiguration("Encryption")))
AES 密钥要求
| 项目 | 要求 |
|---|---|
| 密钥格式 | Base64 编码字符串 |
| 解码后长度 | 必须为 16 / 24 / 32 字节 |
| 加密算法 | AES-GCM(认证加密) |
| 密文格式 | [LRD 0x02][nonce(12)][tag(16)][ciphertext] |
| 压缩 | 加密前先 Deflate 压缩 |
重要:v7.11.0 起,AES 新加密统一使用 AES-GCM。密钥长度不合法时会抛
InvalidOperationException,不再静默截断/补零。
生成 AES 密钥
using System.Security.Cryptography;
var key = RandomNumberGenerator.GetBytes(32); // 256 位
var iv = RandomNumberGenerator.GetBytes(16); // 备用 IV(仅 legacy 解密使用)
var keyBase64 = Convert.ToBase64String(key);
var ivBase64 = Convert.ToBase64String(iv);
// 写入 appsettings.json
// "Encryption": { "PublicKeyOrKey": "...", "PrivateKeyOrIV": "...", "EncryptType": "Aes" }
业务使用
public class SecureService
{
private readonly IEncryptProvider _encryptor;
public SecureService(IEncryptProvider encryptor) => _encryptor = encryptor;
public byte[] Encrypt(string plainText)
{
var bytes = Encoding.UTF8.GetBytes(plainText);
return _encryptor.Encryption(bytes);
}
public string Decrypt(byte[] cipher)
{
var bytes = _encryptor.Decryption(cipher);
return Encoding.UTF8.GetString(bytes);
}
}
加密配置示例
{
"Encryption": {
"PublicKeyOrKey": "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=",
"PrivateKeyOrIV": "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=",
"EncryptType": "Aes"
}
}
5. HTTP 请求
注册方式
// RestSharp(默认)
.UseHttp(h => h.UseRestSharp().WithTimeout(TimeSpan.FromSeconds(30)))
// HttpClient
.UseHttp(h => h.UseNetHttp().WithTimeout(TimeSpan.FromSeconds(30)))
业务使用
public class ApiService
{
private readonly IHttpFactory _httpFactory;
public ApiService(IHttpFactory httpFactory) => _httpFactory = httpFactory;
public async Task<MyResult?> GetDataAsync()
{
var factory = _httpFactory.CreateFactory("https://api.example.com");
var response = await factory.CreateRequest("/api/v1/data")
.AddHeader("Authorization", "Bearer token")
.AddQueryParameter("page", "1")
.GetAsync<MyResult>();
return response.Content;
}
public async Task PostDataAsync(object payload)
{
var factory = _httpFactory.CreateFactory("https://api.example.com");
await factory.CreateRequest("/api/v1/submit")
.AddJsonBody(payload)
.PostAsync();
}
}
HTTP 重试策略
| 方法 | 默认重试 | 说明 |
|---|---|---|
| GET / HEAD / OPTIONS / PUT / DELETE | 3 次 | 幂等方法自动重试 |
| POST / PATCH | 不重试 | 防止重复提交 |
| 4xx(除 429) | 不重试 | 客户端错误不可恢复 |
| 429 / 5xx | 重试 | 服务端临时错误 |
| 超时/连接失败 | 重试 | 网络问题可恢复 |
如需对 POST 启用重试:
var request = factory.CreateRequest("/api/submit")
.AddJsonBody(payload)
.SetRetryCount(3)
.AllowRetryNonIdempotent() // 显式允许 POST 重试
.PostAsync<MyResult>();
安全提示:HttpClient 缓存 key 已从
url.Host改为scheme://host:port,避免同 host 不同端口复用错误客户端。响应消息在读取内容后立即释放,避免连接池耗尽。
6. 日志(NLog / Log4Net)
注册方式
// NLog(默认)
.UseLogging(log => log.UseNLog())
// Log4Net
.UseLogging(log => log.UseLog4Net())
// 自定义日志配置
.UseLogging(log => log.UseNLog(builder =>
{
builder.SetMinimumLevel(LogLevel.Information);
builder.AddFilter<Microsoft.Hosting.Lifetime>("Microsoft", LogLevel.Warning);
}))
日志配置文件
nlog.config/log4net.config优先从应用根目录加载,不存在时使用内置默认配置。
7. 微信公众号
注册方式
.UseWeChatOfficial(wx =>
{
wx.FromConfiguration("WeChatOfficial");
// 或:wx.WithSettings("appId", "appSecret", "token", "encodingAesKey?");
// 可选:wx.UseMessageHandler<MyCustomHandler>();
})
微信公众号配置
{
"WeChatOfficial": {
"AppId": "your_app_id",
"AppSecret": "your_app_secret",
"Token": "your_token",
"EncodingAesKey": null
}
}
Controller 接入(安全 POST 入口)
[ApiController]
[Route("api/wechat")]
public class WeChatController : ControllerBase
{
private readonly IWeChatSecureMessageHandler _handler;
public WeChatController(IWeChatSecureMessageHandler handler)
=> _handler = handler;
// GET:微信 URL 验证
[HttpGet]
public string Get(
[FromQuery] string signature,
[FromQuery] string timestamp,
[FromQuery] string nonce,
[FromQuery] string echostr)
{
var service = _handler.MsgOfficialService;
return service.ValidateSignature(signature, timestamp, nonce, echostr, out var response)
? response : "fail";
}
// POST:微信消息回调(自动验签)
[HttpPost]
public async Task<string> Post(
[FromQuery] string signature,
[FromQuery] string timestamp,
[FromQuery] string nonce)
{
using var reader = new StreamReader(Request.Body);
var xml = await reader.ReadToEndAsync();
// 先验签,再处理 XML
return await _handler.HandleMessageAsync(xml, signature, timestamp, nonce);
}
}
安全提示:
IWeChatSecureMessageHandler在处理 XML 前会先校验微信签名。签名校验使用固定时间比较(CryptographicOperations.FixedTimeEquals),防止时序攻击。
事件订阅
public class WeChatEventService
{
private readonly IWeChatEventHandler _events;
public WeChatEventService(IWeChatEventHandler events)
{
_events = events;
// 用户关注
_events.OnUserSubscribed += async args =>
{
Console.WriteLine($"用户关注: {args.FromUser}");
await Task.CompletedTask;
};
// 文本消息
_events.OnTextMessageReceived += async args =>
{
Console.WriteLine($"收到文本: {args.Content}");
await Task.CompletedTask;
};
}
}
8. 微信小程序
注册方式
.UseWeChatMiniProgram(wx =>
{
wx.FromConfiguration("WeChatMiniProgram");
// 或:wx.WithSettings("appId", "appSecret");
// 多小程序:wx.AddProgram("alias", "appId", "appSecret");
})
配置
{
"WeChatMiniProgram": {
"AppId": "your_appid",
"AppSecret": "your_secret",
"BaseUrl": "https://api.weixin.qq.com"
}
}
9. JSON 序列化
// Newtonsoft.Json(默认)
.UseJson(j => j.UseNewtonsoftJson(settings =>
{
settings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
settings.NullValueHandling = NullValueHandling.Ignore;
}))
// System.Text.Json
.UseJson(j => j.UseTextJson(opts =>
{
opts.Serialize = o => o.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
}))
安全最佳实践
1. 加密
- 生产环境必须使用 AES-GCM,不要使用 DES
- AES 密钥长度必须为 16/24/32 字节(推荐 32 字节 = AES-256)
- 密钥不要硬编码在代码中,使用
appsettings.json或环境变量 - 定期轮换密钥
2. 日志脱敏
- 推送失败日志已自动脱敏,不会记录完整消息内容
- 微信 XML 日志已自动脱敏
Content、Recognition、FromUserName等敏感字段 - 如需自定义脱敏,使用
LogSanitizer工具类
3. 微信公众号
- POST 回调必须使用
IWeChatSecureMessageHandler进行签名校验 - 签名校验使用固定时间比较,防止时序攻击
EncodingAesKey配置后可支持加密模式
4. HTTP 请求
- POST/PATCH 默认不重试,防止重复提交
- 响应消息在读取内容后立即释放,避免连接池耗尽
- HttpClient 缓存按
scheme://host:port隔离
5. RabbitMQ
- 连接创建有 30 秒总超时,不会无限阻塞
- 消费端 handler 超时 3 分钟后自动取消,避免资源泄漏
- 看门狗永不永久停止,失败后进入低频持续重试
6. Redis
- 同步
AddOrGetCacheItem有 30 秒超时保护 - 分布式锁自动续期有重入保护,防止续期任务堆积
- 批量操作自动分批(每批 500 条),避免大命令阻塞 Redis
v7.11.0 变更与迁移
安全增强
| 变更项 | 旧行为 | 新行为 |
|---|---|---|
| 默认加密 | DES | AES-GCM |
| AES 模式 | CBC 无认证 | GCM 认证加密 |
| AES 密钥 | 静默截断/补零 | 严格校验 16/24/32 字节 |
| 推送日志 | 完整记录 content | 自动脱敏 |
| 微信日志 | 完整记录 XML | 自动脱敏敏感字段 |
| 微信 POST | 无签名校验入口 | 新增 IWeChatSecureMessageHandler |
| 签名比较 | 字符串相等 | 固定时间比较 |
稳定性增强
| 变更项 | 旧行为 | 新行为 |
|---|---|---|
| MQ 连接创建 | CancellationToken.None |
30 秒内部超时 |
| MQ 看门狗 | 20 次失败后永久停止 | 低频持续重试(60 秒一次) |
| MQ handler 超时 | 后台任务继续运行 | 取消后台任务 |
| HTTP 响应 | 未释放 | using var 确保释放 |
| HTTP POST | 默认重试 3 次 | 默认不重试 |
| HTTP 缓存 key | url.Host |
scheme://host:port |
性能增强
| 变更项 | 旧行为 | 新行为 |
|---|---|---|
| Redis RemoveBatch | 一次性删除 | 分批 500 条 |
| Redis SetBatch | 一次性写入 | 分批 500 条 |
| Redis 锁续期 | 可能重入堆积 | 重入保护 |
| Redis singleflight | 无超时 | 30 秒超时 |
架构改进
| 变更项 | 旧行为 | 新行为 |
|---|---|---|
| Builder 注册 | 8 处 BuildServiceProvider |
全部移除,改用 BindConfiguration 或懒加载 |
迁移指南
DES → AES:如果旧数据是用 DES 加密的,解密时仍可使用
UseDES。新数据必须使用UseAES。推送配置:
FromConfiguration()的配置在首次调用GetPushService(sectionName)时懒加载,行为与之前一致。HTTP POST 重试:如果业务依赖 POST 自动重试,调用
.AllowRetryNonIdempotent()显式开启。微信 POST:将 Controller 中的
HandleMessageAsync(xml)替换为HandleMessageAsync(xml, signature, timestamp, nonce)。RabbitMQ → RocketMQ 迁移:
无需修改的代码(使用通用接口):
// ✅ 以下代码从 RabbitMQ 切换到 RocketMQ 无需修改 await hub.PublishAsync(new OrderCreated("ORD-001", 99.9m)); await hub.SubscribeAsync<OrderCreated>(async msg => { return true; }); await hub.SubscribeFailedAsync<OrderCreated>(async msg => { return true; });需要修改的代码(仅注册部分):
// ❌ 旧代码(RabbitMQ) .UseMQ(mq => mq.UseRabbitMQ(r => r.FromConfiguration("RabbitMQ"))) // ✅ 新代码(RocketMQ) .UseMQ(mq => mq.UseRocketMQ(r => r.FromConfiguration("RocketMQ")))字段映射关系: | RabbitMQ 概念 | RocketMQ 概念 | 自动映射 | |--------------|--------------|---------| | Exchange | Topic | ✅ 自动 | | Queue | ConsumerGroup | ✅ 自动 | | RouteKey | Tag | ✅ 自动 | | DLX/DLQ | %DLQ%{ConsumerGroup} | ✅ 自动 |
注意事项:
- 同一容器不能同时启用 RabbitMQ 和 RocketMQ
- RocketMQ 需要在 broker 上预先创建 Topic(或启用自动创建)
- 高级功能(事务/顺序/延迟消息)需要手动转换接口类型
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. 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 is compatible. 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 is compatible. 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 is compatible. 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. |
-
net10.0
- log4net (>= 3.3.0)
- Microsoft.Extensions.Configuration (>= 10.0.0)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.0)
- Microsoft.Extensions.Configuration.Json (>= 10.0.0)
- Microsoft.Extensions.DependencyInjection (>= 10.0.0)
- Microsoft.Extensions.Http (>= 10.0.0)
- Microsoft.Extensions.Logging.Log4Net.AspNetCore (>= 8.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.0)
- NewLife.RocketMQ (>= 3.1.2026.601)
- Newtonsoft.Json (>= 13.0.4)
- NLog (>= 6.1.2)
- NLog.Extensions.Logging (>= 6.1.2)
- Polly (>= 8.4.1)
- RabbitMQ.Client (>= 7.2.1)
- RestSharp (>= 113.0.0)
- StackExchange.Redis (>= 2.9.25)
- System.Runtime.Caching (>= 10.0.0)
-
net6.0
- log4net (>= 3.3.0)
- Microsoft.Extensions.Configuration (>= 6.0.0)
- Microsoft.Extensions.Configuration.Abstractions (>= 6.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 6.0.0)
- Microsoft.Extensions.Configuration.Json (>= 6.0.0)
- Microsoft.Extensions.DependencyInjection (>= 6.0.0)
- Microsoft.Extensions.Http (>= 6.0.0)
- Microsoft.Extensions.Logging.Log4Net.AspNetCore (>= 8.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 6.0.0)
- NewLife.RocketMQ (>= 3.1.2026.601)
- Newtonsoft.Json (>= 13.0.4)
- NLog (>= 6.1.2)
- NLog.Extensions.Logging (>= 6.1.2)
- Polly (>= 8.4.1)
- RabbitMQ.Client (>= 7.2.1)
- RestSharp (>= 112.0.0)
- StackExchange.Redis (>= 2.9.25)
- System.Runtime.Caching (>= 6.0.0)
-
net8.0
- log4net (>= 3.3.0)
- Microsoft.Extensions.Configuration (>= 8.0.0)
- Microsoft.Extensions.Configuration.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 8.0.0)
- Microsoft.Extensions.Configuration.Json (>= 8.0.1)
- Microsoft.Extensions.DependencyInjection (>= 8.0.0)
- Microsoft.Extensions.Http (>= 8.0.0)
- Microsoft.Extensions.Logging.Log4Net.AspNetCore (>= 8.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 8.0.0)
- NewLife.RocketMQ (>= 3.1.2026.601)
- Newtonsoft.Json (>= 13.0.4)
- NLog (>= 6.1.2)
- NLog.Extensions.Logging (>= 6.1.2)
- Polly (>= 8.4.1)
- RabbitMQ.Client (>= 7.2.1)
- RestSharp (>= 113.0.0)
- StackExchange.Redis (>= 2.9.25)
- System.Runtime.Caching (>= 8.0.0)
-
net9.0
- log4net (>= 3.3.0)
- Microsoft.Extensions.Configuration (>= 9.0.0)
- Microsoft.Extensions.Configuration.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 9.0.0)
- Microsoft.Extensions.Configuration.Json (>= 9.0.0)
- Microsoft.Extensions.DependencyInjection (>= 9.0.0)
- Microsoft.Extensions.Http (>= 9.0.0)
- Microsoft.Extensions.Logging.Log4Net.AspNetCore (>= 8.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 9.0.0)
- NewLife.RocketMQ (>= 3.1.2026.601)
- Newtonsoft.Json (>= 13.0.4)
- NLog (>= 6.1.2)
- NLog.Extensions.Logging (>= 6.1.2)
- Polly (>= 8.4.1)
- RabbitMQ.Client (>= 7.2.1)
- RestSharp (>= 113.0.0)
- StackExchange.Redis (>= 2.9.25)
- System.Runtime.Caching (>= 9.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 7.10.6 | 31 | 8/5/2026 |
| 7.10.5 | 38 | 8/4/2026 |
| 7.10.1 | 83 | 8/1/2026 |
| 7.10.0 | 78 | 7/30/2026 |
| 7.0.8 | 98 | 7/29/2026 |
| 7.0.7 | 89 | 7/29/2026 |
| 7.0.6 | 89 | 7/16/2026 |
| 7.0.5 | 109 | 4/24/2026 |
| 7.0.3 | 134 | 2/10/2026 |
| 7.0.2 | 127 | 2/6/2026 |
| 7.0.1 | 128 | 2/4/2026 |
| 7.0.0 | 138 | 1/26/2026 |
| 6.2.0 | 137 | 1/23/2026 |
| 6.0.9 | 132 | 1/12/2026 |
| 6.0.8 | 305 | 11/30/2025 |
| 6.0.7 | 221 | 9/28/2025 |
| 6.0.6 | 221 | 9/26/2025 |
7.10.6:
- 【新功能】统一消息模型 PushMessage + 渠道适配器(钉钉/飞书/企业微信)
- 业务代码用 PushMessage 构建消息(渠道无关),推送时自动转换为各渠道 webhook JSON
- 切换渠道(钉钉→飞书→企业微信)业务代码零改动,只换 AddDingTalk/AddLark/AddWeChat 注册
- IApiPush 新增 Push(PushMessage)/PushAsync(PushMessage) 统一入口(默认接口实现)
- 支持 Markdown/Text/Link/ActionCard/FeedCard 五种消息类型
- 【新功能】消息构建器统一接口 IMessageBuilder{T}(一致性)
- PushMessage/DingTalkMessage/LarkMessage/WeChatMessage 均支持 Add 字典、Add 实体、At 用户
- Add(IDictionary) 批量键值对、Add(TEntity) 属性自动转键值对
- 修复泛型重载决议问题(传入 Dictionary 时运行时分流,避免被当实体反射)
- 【根本性改进】移除 RocketMQ 探针消息机制(LORD_SERVICE_DLQ_PROBE)
- 根因:探针用于创建 %DLQ% 主题,但 RocketMQ Consumer 会自动订阅 %RETRY%/%DLQ% 队列,探针被业务消费 → 解密失败 → 无限重试
- 验证:Consumer 订阅不存在的 Topic 时客户端会重试等待,失败消息转发时 Producer 发布(autoCreateTopic)自动创建 Topic,探针完全不需要
- 保留存量探针跳过检查(兼容清理旧版本积压)
- 【看门狗】修复消费停滞检测误判:生产端正常无消息时被误判"停滞"导致每 5 分钟反复重建 consumer
- 主检查改为 consumer 连接活性(Active/Disposed),停滞检测改为 handler 卡死检测(处理中标记 _processingTick,30 分钟阈值)
- 【看门狗】修复 Dispose 竞态:等待恢复任务结束再释放锁(避免 ObjectDisposedException)
- 【语义修复】Consumer.Tags 恢复 null(RocketMQ 协议缺省=不过滤),移除 Array.Empty 赋值(空数组可能导致过滤语义变化)
- 【新功能】消息级加密开关(IMQSettings.EnableEncryption + EncryptionPrefixes 前缀白名单)
- 无需敏感信息的消息可关闭加密(提升性能、避免加解密配置不匹配)
- 支持按队列名前缀白名单只加密部分队列(如 "secret_;private_")
- 生产端与消费端使用相同队列名时自动保持一致的加密策略
- RocketMQConfig/RabbitMQConfig 提供全局默认值(Topic 驱动接口场景)
- 【重构】提取 RocketMQMessageHelper(探针识别/重试退避)与 MQEncryptionHelper(加密策略),消除重复代码
- 【新功能】UseMQ 全局加密配置(引擎无关:RabbitMQ/RocketMQ/未来 Kafka)
- mq.EnableEncryption(false) 全局关闭加密;mq.EncryptionPrefixes("secret_", "private_") 白名单前缀
- 通过 PostConfigureAll 应用到所有已注册引擎,Factory 创建 settings 时自动继承
- RabbitMQ 生产/消费链路已接入加密开关(RabbitMQPush.ApplyEncryption / RabbitMQReceive.GetMessage)
- 优先级:IMQSettings(消息级) > UseMQ 全局配置 > appsettings 配置
- 【新功能】RocketMQ Builder 新增 ACL/云厂商/SSL 链式配置
- WithAcl(accessKey, secretKey) Apache ACL 认证
- WithCloudProvider(Aliyun/Huawei/Tencent, ak, sk, extra) 云厂商托管
- WithSsl(true) SSL/TLS 开关
- 【测试】新增 MQBuilder 全局加密配置测试 + RocketMQ Builder ACL 测试 + 全部 MQ 相关测试通过(42 个)
7.10.5:
- IMQProvider 性能优化(懒加载缓存 Hub/Factory/IJsonFormat,避免重复 DI 解析)
- 修复 RocketMQSettings 默认值点号问题(RocketMQ Topic/Group 不允许点号,改为下划线)
- 文档新增 IMQProvider 使用指南(一站式入口 + 能力检查 + MQTT 扩展指引)
7.10.4:
- IMQProvider 新增细粒度能力检查(SupportsTransactionMessages/SupportsDelayedMessages/SupportsRequestReply/SupportsSql92Filtering/SupportsOrderedMessages)
- IMQProvider 新增便捷方法(GetPublisher/GetSubscriber/GetHub/GetFactory),一站式获取所有 MQ 服务
- 修复 RabbitMQ 事务消息"静默降级"问题(现在可通过 SupportsTransactionMessages 提前检查)
- 修复 RabbitMQ 延迟消息"静默降级"问题(现在可通过 SupportsDelayedMessages 提前检查)
- 新增 RocketMQCloudHelper 共享帮助类,减少 160+ 行重复代码
7.10.3:
- 修复 RocketMQPublisher 缺少 Producer 活性检查问题(添加 IsProducerAlive 双检锁,与 RocketMQPush 对齐)
- 修复 EnsureFailedTopicAsync 中 Producer 资源泄漏(改用 finally 确保释放)
- 抽取 ConfigureCloudProvider 到共享帮助类 RocketMQCloudHelper(减少 160 行重复代码)
- 优化 CleanupExpiredRetryEntries 避免 LINQ 分配(改用 foreach 直接遍历)
- 文档版本同步更新至 v7.10.3
7.10.2:
- 修复 RocketMQ 模块 116 个 nullable reference 警告(提升类库质量)
- 修复 _retryCounts 字典无限增长问题(添加基于时间的自动清理机制)
- 修复 _failedProducer 资源泄漏问题(Dispose 时正确释放)
- 修复 _failedProducer 并发初始化竞态条件(双检锁保护)
- 优化 typeNameCache 缓存淘汰策略(达到上限时清空重建,确保新类型能被缓存)
- 修复 Tags 属性 null 赋值问题(改用 Array.Empty 兼容 C# 10)
- 修复 Type.FullName 可能为 null 的问题(添加回退值)
- 修复 message.Tags/Keys 可能为 null 的问题(添加默认值)
7.10.0:
- AES 加密改用随机 IV(每次加密生成新 IV,向后兼容旧数据)
- 新增 Polly 8 弹性策略(LordResilience),MQ 连接重试改为指数退避+抖动
- HTTP 重试改为指数退避+随机抖动(RestSharp + HttpClient 双实现)
- 移除 RabbitMQFactory/RabbitMQService 终结器(消除 GC 线程死锁风险)
- _timedOutTags 添加基于时间的自动淘汰(心跳中清理超过 5 分钟的泄漏条目)
- 修复 LarkPush.PushAsync 空路径 bug(异步飞书推送发送到错误端点)
- 修复 DingTalkPush/LarkPush 并发竞态条件(GetAuthentication 修改共享对象)
- 修复 WeChatPush/LarkPush 死代码和 null 检查不一致
7.0.8:
- IMQHub 新增显式名称参数重载(PublishAsync/SubscribeAsync/SubscribeDeadLetterAsync)
- 新增 MQNameAttribute,支持为消息类型指定自定义名称前缀
- IMQHub 中文 XML 注释
- RabbitMQ Prefix 前缀隔离(多系统共用 broker 时区分队列名)
- RabbitMQ 消费端看门狗重写(心跳检测+无限重试+指数退避+Channel 重建)
- 生产端 RabbitMQPush 新增 Channel 自动恢复机制
- 死信队列(DLX)纳入看门狗保护
- RSA 改为标准保密模式(加密用公钥,解密用私钥)
- 默认加密算法从 DES 改为 AES
- 修复缓存 AddOrGetCacheItem 默认值误判问题
- 修复服务工厂缓存 token 污染问题
- 修复消费端异常消息无限 requeue 问题
- 移除 FreeRedis 支持(聚焦 StackExchange.Redis)