在之前的文章中,我已介绍了 Brighter 与 RabbitMQ 的集成。本文聚焦于迁移到 Brighter V10 的关键步骤,重点说明 RabbitMQ 配置变更和破坏性更新。
要求
- .NET 8 或更高版本
- 包含以下 NuGet 包的 .NET 项目:
- Paramore.Brighter.MessagingGateway.RMQ.Sync:使用 RabbitMQ.Client V6(同步 API)启用 RabbitMQ 集成。
- Paramore.Brighter.MessagingGateway.RMQ.Async:使用 RabbitMQ.Client V7(异步 API)启用 RabbitMQ 集成。
- Paramore.Brighter.ServiceActivator.Extensions.DependencyInjection:通过 Microsoft DI 注册 Brighter。
- Paramore.Brighter.ServiceActivator.Extensions.Hosting:将 Brighter 作为后台服务托管。
- Serilog.AspNetCore:结构化日志记录(可选但推荐)。
Brighter 核心概念回顾
请求(命令/事件)
通过 IRequest 定义消息:
public class Greeting() : Event(Guid.NewGuid())
{
public string Name { get; set; } = string.Empty;
}
- 命令:单接收者操作(如 SendEmail)。
- 事件:广播通知(如 OrderShipped)。
消息映射器(可选)
在 Brighter 消息与应用对象间转换:
public class GreetingMapper : IAmAMessageMapper<Greeting>
{
public Message MapToMessage(Greeting request, Publication publication)
{
var header = new MessageHeader
{
MessageId = request.Id,
TimeStamp = DateTimeOffset.UtcNow,
Topic = publication.Topic!,
MessageType = MessageType.MT_EVENT
};
var body = new MessageBody(JsonSerializer.Serialize(request));
return new Message(header, body);
}
public Greeting MapToRequest(Message message)
{
return JsonSerializer.Deserialize<Greeting>(message.Body.Bytes)!;
}
public IRequestContext? Context { get; set; }
}
V10 变更:异步映射器需实现 IAmAMessageMapperAsync。
请求处理器
处理入站消息:
public class GreetingHandler(ILogger<GreetingHandler> logger) : RequestHandler<Greeting>
{
public override Greeting Handle(Greeting command)
{
logger.LogInformation("Hello {Name}", command.Name);
return base.Handle(command);
}
}
配置 Brighter 与 RabbitMQ
1. 选择同步 vs 异步
- 同步 (RMQ.Sync):使用 RabbitMQ.Client V6(阻塞式 API),适合渐进式迁移。
- 异步 (RMQ.Async):使用 RabbitMQ.Client V7(全异步 API),推荐用于新项目。
提示:先迁移到 Brighter V10,再切换到 RMQ.Async 以隔离变更。
2. 连接配置
定义 RabbitMQ 连接信息:
var connection = new RmqMessagingGatewayConnection
{
AmpqUri = new AmqpUriSpecification(new Uri("amqp://guest:guest@localhost:5672")),
Exchange = new Exchange("paramore.brighter.exchange"),
};
3. RabbitMQ 订阅
订阅队列/主题:
.AddServiceActivator(opt =>
{
opt.Subscriptions = [
new RmqSubscription<Greeting>(
new SubscriptionName("kafka.greeting.subscription"),
new ChannelName("greeting.queue"),
new RoutingKey("greeting.topic"),
makeChannels: OnMissingChannel.Create,
messagePumpType: MessagePumpType.Reactor
),
];
opt.DefaultChannelFactory = new ChannelFactory(new RmqMessageConsumerFactory(connection));
})
4. RabbitMQ 生产者配置
发布事件到主题:
.UseExternalBus(opt =>
{
opt.ProducerRegistry = new RmqProducerRegistryFactory(connection, [
new RmqPublication<Greeting>
{
MakeChannels = OnMissingChannel.Create,
Topic = new RoutingKey("greeting.topic"),
},
]).Create();
});
Brighter V10 破坏性变更
包名变更
为适配 RabbitMQ.Client 版本并明确同步/异步用法:
- 旧:Paramore.Brighter.MessagingGateway.RMQ
- 新:
- Paramore.Brighter.MessagingGateway.RMQ.Sync(V6 同步 API
- Paramore.Brighter.MessagingGateway.RMQ.Async(V7 异步 API)
消息映射器重构
- 默认 JSON 序列化:V10 移除强制自定义映射器。
- 接口拆分:
- IAmAMessageMapper(同步/Reactor)
- IAmAMessageMapperAsync(异步/Proactor)
// V10
IRequestContext? Context { get; set; }
Message MapToMessage(Greeting request, Publication publication);
// V9
Message MapToMessage(Greeting request);
订阅配置变更
- 显式消息泵类型:runAsync 改为 messagePumpType(枚举:Reactor, Proactor)。
- 属性重命名:ChannelFactory 改为 DefaultChannelFactory。
发布配置变更
- 配置重构:移除 IAmAProducerRegistry,改用 UseExternalBus 配置生产者。
// V10
.UseExternalBus(opt => { ... })
// V9
.UseExternalBus(new RmqProducerRegistryFactory(...))
- 请求类型指定:非泛型 RmqPublication 需设置 RequestType。
new RmqPublication<Greeting>
{
MakeChannels = OnMissingChannel.Create,
Topic = new RoutingKey("greeting.topic"),
}
// or
new RmqPublication
{
MakeChannels = OnMissingChannel.Create,
Topic = new RoutingKey("greeting.topic"),
RequestType = typeof(Greeting)
}
迁移建议
1. 优先使用 RMQ.Sync:先完成 Brighter V10 迁移,再升级到 RMQ.Async。
2. 审计映射器:异步工作流需替换为 IAmAMessageMapperAsync。
3. 验证订阅配置:确保 messagePumpType 匹配同步/异步需求。
4. 更新发布配置:为非泛型 RmqPublication 添加 RequestType。
总结
Brighter V10 通过清晰的同步/异步分离简化了 RabbitMQ 集成,关键步骤包括:
- 更新 NuGet 包
- 重构消息映射器至异步模式
https://github.com/lillo42/brighter-sample/tree/v10-connect-to-rabbitmq-async
https://github.com/lillo42/brighter-sample/tree/v10-connect-to-rabbitmq-sync
using System.Text.Json;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Paramore.Brighter;
using Paramore.Brighter.Extensions.DependencyInjection;
using Paramore.Brighter.MessagingGateway.RMQ.Async;
using Paramore.Brighter.ServiceActivator.Extensions.DependencyInjection;
using Paramore.Brighter.ServiceActivator.Extensions.Hosting;
using Serilog;
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();
var host = new HostBuilder()
.UseSerilog()
.ConfigureServices(
(ctx, services) =>
{
var connection = new RmqMessagingGatewayConnection
{
AmpqUri = new AmqpUriSpecification(new Uri("amqp://guest:guest@localhost:5672")),
Exchange = new Exchange("paramore.brighter.exchange"),
};
services
.AddHostedService<ServiceActivatorHostedService>()
.AddServiceActivator(opt =>
{
opt.Subscriptions =
[
new RmqSubscription<Greeting>(
new SubscriptionName("kafka.greeting.subscription"),
new ChannelName("greeting.topic"),
new RoutingKey("greeting.topic"),
makeChannels: OnMissingChannel.Create,
messagePumpType: MessagePumpType.Reactor
),
];
opt.DefaultChannelFactory = new ChannelFactory(new RmqMessageConsumerFactory(connection));
})
.AutoFromAssemblies()
.UseExternalBus(opt =>
{
opt.ProducerRegistry = new RmqProducerRegistryFactory(
connection, [
new RmqPublication<Greeting>
{
MakeChannels = OnMissingChannel.Create,
Topic = new RoutingKey("greeting.topic"),
},
]
).Create();
}
);
}
)
.Build();
await host.StartAsync();
while (true)
{
await Task.Delay(TimeSpan.FromSeconds(10));
Console.Write("Say your name (or q to quit): ");
var name = Console.ReadLine();
if (string.IsNullOrEmpty(name))
{
continue;
}
if (name == "q")
{
break;
}
var process = host.Services.GetRequiredService<IAmACommandProcessor>();
process.Post(new Greeting { Name = name });
}
await host.StopAsync();
public class Greeting() : Command(Guid.NewGuid())
{
public string Name { get; set; } = string.Empty;
}
public class GreetingHandler(ILogger<GreetingHandler> logger) : RequestHandler<Greeting>
{
public override Greeting Handle(Greeting command)
{
logger.LogInformation("Hello {Name}", command.Name);
return base.Handle(command);
}
}
843

被折叠的 条评论
为什么被折叠?



