如您所见,总部作为根节点,一些分支作为子节点。有一条 Data 类型的消息,我想根据 Data 对象的内容发布消息,例如:
if (data.value == xxxx) publish(data, Br1, Br2)
else if (data.value == yyyy) publish(data, Br3, Br4)
else if (data.value == zzzz) publis(data, Br5, Br6)
这是某种方式的pub / sub模式的定制版本。但我希望根据消息内容向一些特殊订阅者发布类型为Data的消息。
Rebus有解决方案吗?
答案 0 :(得分:0)
Rebus有几种解决方案:)
对于您的场景,我可以看到两种解决方案:1)使用自定义主题,或2)实现基于内容的真实路由器。
如果有意义,您可以使用Rebus的主题API来使用主题对此发布/订阅方案进行建模,以处理路由。如果您可以说您的数据消息属于某个类别,您的订阅者可以订阅这些类别,这是有道理的。
与“真实的”基于主题的排队系统相比,例如RabbitMQ,Rebus中的主题API非常粗糙。它不允许使用通配符(*)或任何类似的高级主题 - 主题只是您可以订阅的简单字符串,然后用作发布/订阅频道将事件路由到多个订阅者。
您可以在订阅者端使用它:
await bus.Advanced.Topics.Subscribe("department_a");
然后在出版商的最后:
var data = new Data(...);
await bus.Advanced.Topics.Publish("department_a", data);
如果没有删除它,您可以插入一个“真正的”基于内容的路由器,它只是您await bus.Send(eachDataMessage)
的端点,而后者又将消息转发给相关订户。
根据您的要求,可以使用Rebus在两个级别完成。如果查看消息的标题就足够了,您应该将其实现为“传输消息转发器”,因为它会跳过反序列化并提供一个很好的API来简单地转发消息:
Configure.With(...)
.Transport(t => t.UseMsmq("router"))
.Routing(r => {
r.AddTransportMessageForwarder(async transportMessage => {
var headers = transportMessage.Headers;
var subscribers = Decide(headers);
return ForwardAction.ForwardTo(subscribers);
});
})
.Start();
如果您需要查看实际消息,您应该只实现一个普通的消息处理程序,然后使用总线转发消息:
public class Router : IHandleMessages<Data>
{
readonly IBus _bus;
public Router(IBus bus)
{
_bus = bus;
}
public async Task Handle(Data message)
{
var subscribers = Decide(message);
foreach(var subscriber in subscribers)
{
await _bus.Advanced.TransportMessage.ForwardTo(subscriber);
}
}
}
自定义实现的路由器是最灵活的解决方案,因为您可以实现您喜欢的任何逻辑,但正如您所看到的那样稍微复杂一点。
(*)Rebus一般不允许使用通配符,尽管 直接将主题传递给RabbitMQ,如果你碰巧使用它作为传输,这意味着你实际上可以充分利用RabbitMQ(有关详细信息,请参阅this issue)
答案 1 :(得分:0)
static void Main()
{
using (var activator = new BuiltinHandlerActivator())
{
activator.Handle<Packet>(async (bus, packet) =>
{
string subscriber = "subscriberA";
await bus.Advanced.TransportMessage.Forward(subscriber);
});
Configure.With(activator)
.Logging(l => l.ColoredConsole(minLevel: LogLevel.Warn))
.Transport(t => t.UseMsmq("router"))
.Start();
for (int i = 0; i < 10; i++)
{
activator.Bus.SendLocal(
new Packet()
{
ID = i,
Content = "content" + i.ToString(),
Sent = false,
}).Wait();
}
}
Console.ReadLine();
}
答案 2 :(得分:0)
using (var trScope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
scope.EnlistRebus();
Packet packet = ReadFromDB()
activator.Bus.SendLocal(packet).Wait()
scope.Complete()
}
activator.Handle<Packet>(async (bus, packet) =>
{
string subscriber = "subscriberA";
await bus.Advanced.TransportMessage.Forward(subscriber);
});
答案 3 :(得分:0)
using (var activator = new BuiltinHandlerActivator())
{
activator.Handle<Packet>(async message =>
{
string connectionString =
"Data Source=.;Initial Catalog=Rebus;User ID=sa;Password=123456";
using (SqlConnection connection = new SqlConnection(connectionString))
{
string queryString = @"INSERT INTO CLIENTPACKET(ID, CONTENT, SENT) VALUES(@id, @content, @sent)";
connection.Open();
using (SqlCommand command = new SqlCommand(queryString, connection))
{
command.Parameters.Add(new SqlParameter("@id", message.ID));
command.Parameters.Add(new SqlParameter("@content", message.Content));
command.Parameters.Add(new SqlParameter("@sent", message.Sent));
await command.ExecuteNonQueryAsync();
}
}
});
Configure.With(activator)
.Logging(l => l.ColoredConsole(minLevel: LogLevel.Warn))
.Transport(t => t.UseMsmq(@"subscriberA"))
.Routing(r => r.TypeBased().MapAssemblyOf<Packet>("router"))
.Options(o =>
{
TransactionOptions tranOp = new TransactionOptions();
tranOp.IsolationLevel = IsolationLevel.ReadCommitted;
o.HandleMessagesInsideTransactionScope(tranOp);
o.SetNumberOfWorkers(2);
o.SetMaxParallelism(2);
})
.Start();
activator.Bus.Subscribe<Packet>().Wait();
Console.WriteLine("Press ENTER to quit");
Console.ReadLine();
}