public static void SendREsbDx(Job job)
{
using (var adapter = new BuiltinContainerAdapter())
{
adapter.Handle<ReplyMsg>(msg =>
{
string mss = msg.message;
});
Configure.With(adapter)
.Logging(l => l.ColoredConsole(LogLevel.Warn))
.MessageOwnership(o => o.FromRebusConfigurationSection())
.Transport(t => t.UseSqlServer("server=.;initial catalog=rebus_test;integrated security=true","consumerx","error")
.EnsureTableIsCreated())
.CreateBus()
.Start();
adapter.Bus.Send<Job>(job);
}
}
I am using the above code to send a message to a consumer. The consumer will use the bus.Reply, but the above code obviously does not work.
I simply want to be able to receive a reply from the consumer. How would this be accomplished?
Sounds like your consumer does not have a handler for Job messages.
In your case, it sounds like you'll need two bus instances - a consumer instance that has an implementation of IHandleMessages<Job> which will bus.Reply(new ReplyMsg {...}), and a producer instance that has an implementation of IHandleMessages<ReplyMsg> which will bus.Send(new Job{...}) and do whatever needs to be done in the reply handler.
If you're interested in looking at some sample code that demonstrates request/reply, take a look at the integration sample in the Rebus samples repository which has some simple request/reply going on between the Client (which would correspond to the producer in your case) and the IntegrationService (which corresponds to the consumer).
The following code snippet demonstrates how it can be done:
var producer = new BuiltinContainerAdapter();
var consumer = new BuiltinContainerAdapter();
consumer.Handle<Job>(job => {
...
consumer.Bus.Reply(new ReplyMsg {...});
});
producer.Handle<ReplyMsg>(reply => {
....
});
Configure.With(producer)
.Transport(t => t.UseSqlServer(connectionString, "producer.input", "error")
.EnsureTableIsCreated())
.MessageOwnership(o => o.FromRebusConfigurationSection())
.CreateBus()
.Start();
Configure.With(consumer)
.Transport(t => t.UseSqlServer(connectionString, "consumer.input", "error")
.EnsureTableIsCreated())
.MessageOwnership(o => o.FromRebusConfigurationSection())
.CreateBus()
.Start();
// for the duration of the lifetime of your application
producer.Bus.Send(new Job {...});
// when your application shuts down:
consumer.Dispose();
producer.Dispose();
and in your app.config there must be an endpoint mapping that maps Job to consumer.input:
<rebus>
<endpoints>
<add messages="SomeNamespace.Job, SomeAssembly" endpoint="consumer.input"/>
</endpoints>
</rebus>
I hope you can see now why your code does not work. Please let me know if I should elaborate further :)
I've added a request/reply sample to the Rebus samples repository to serve as proof that the code shown above can actually run (provided that you remove the .... etc of course - you need a basic understanding of C# to be able to use this code)
Related
I'm making use of MassTransit to receive some messages from a client's application, and redistribute the message within our environment with some routing headers added.
Whilst we are processing a high amount of messages, not all consuming applications are going to be interested in the whole set of the messages. As such the various consumers are configured with the SNS/SQS FilterPolicy attribute to ensure that only the messages we care about are consumed.
The issues I'm experiencing comes about when using the WebApplicationFactory and the IServiceCollection AddMassTransitTestHarness extension method.
The first issue might be down to my misunderstanding. As I'm using SQS/SNS specific functionality, the InMemoryTestHarness is unsuitable. I'm calling the below snippet when configuring my MassTransitTestHarness:
services.AddMassTransitTestHarness( x =>
{
RegisterConsumers( x );
x.UsingAmazonSqs( ( context, factoryConfigurator ) =>
{
factoryConfigurator.Host( new Uri( "amazonsqs://localhost:4566" ),
h =>
{
h.Config( new AmazonSimpleNotificationServiceConfig { ServiceURL = serviceUrl } );
h.Config( new AmazonSQSConfig { ServiceURL = serviceUrl } );
h.AccessKey( "test" );
h.SecretKey( "test" );
} );
RegisterReceiveEndpoints( factoryConfigurator, context );
} );
} );
protected override void RegisterConsumers( IRegistrationConfigurator configurator )
{
configurator.AddConsumer<MovementConsumer>();
}
protected override void RegisterReceiveEndpoints( IAmazonSqsBusFactoryConfigurator factoryConfigurator, IRegistrationContext context )
{
factoryConfigurator.ReceiveEndpoint( $"{ServiceConstants.ServiceName}-test-movement", endpointConfigurator =>
{
endpointConfigurator.ConfigureConsumer<MovementConsumer>( context );
endpointConfigurator.QueueSubscriptionAttributes["FilterPolicy"] = $"{{\"RoutingKey\": [\"null\"]}}";
} );
}
My first question with this approach is that is it necessary to re-register consumers? Ideally I'd like to call AddMassTransitTestHarness and just have it replace the already existing Bus with the TestHarness, but I was finding my consumers weren't being called. Having to re-register the endpoints in both the tests project and the actual project is a burden I'd like to avoid any other developers on this project having.
The second question I have is with regards to asserting against what's been published. I'm experiencing inconsistent results with the below code:
await busTestHarness.Start();
await busTestHarness.Bus.Publish( message, CancellationToken.None );
await busTestHarness.InactivityTask;
//await Task.Delay( TimeSpan.FromSeconds( 2 ) );
busTestHarness.Published.Select<T>().Any( publishedMessage => publishedMessage.Context.Headers.Any( headerValue => headerValue.Key == "RoutingKey" && (string) headerValue.Value == expectedHeader ) ).ShouldBeTrue();
Sometimes the above assertion fails. I am expecting my consumer to publish a new message with routing headers (I am not asserting against the message I publish in the test).
I've found that whilst still flakey, the 2 second delay seems to reduce the rate of failure. Is my usage of InactivityTask correct? Is there anything else I should be waiting for.
Thanks for any help, I've scoured the docs and watched the video on testing with WebApplicationFactory. I've mirrored what's done in there as best as I can. The major difference being that I am not expecting any responses from my messages.
I have a .NET Core service publishing events to Rebus with RMQ Transport with the following configuration:
services.AddRebus(configure => configure
.Logging(x => x.Serilog())
.Transport(x => x.UseRabbitMq(rabbitMqConnection, "ServiceA"))
.Routing(x => x.TypeBased()));
When I run it, it appears to publish the event to the RebusTopics exchange. So then service B has config like this:
services.AutoRegisterHandlersFromAssemblyOf<MyHandler1>();
services.AddRebus(configure => configure
.Logging(x => x.Serilog() )
.Transport(x => x.UseRabbitMq(rabbitMqConnection, "ServiceB"))
.Routing(x => x.TypeBased()));
and a handler:
public class MyHandler1: IHandleMessages<ServiceAEvent>
{
public CreateMinisiteWhenPageIsCreated(){}
public Task Handle(PageCreated message)
{
//do stuff..
return Task.CompletedTask;
}
There appears to be a binding to the RebusDirect exchange to a new ServiceB queue, but when I publish an event from ServiceA, the handler never fires in ServiceB... there is no binding on the RebusTopics exchange for that message type also.
Im going crazy wondering why, its quite similar in syntax to NServiceBus so very confused as to why its not working.
just add app.ApplicationServices.UseRebus(); in the Consumer's Starup.cs no need to subscribe also. ProducerApp can bus.Send() instead of bus.Publish();
It sounds to me like your subscriber needs to
await bus.Subscribe<ServiceAEvent>();
If the bus instance with the input queue named ServiceB makes the call above, a binding will be created from a topic, whose name is derived from the ServiceAEvent type, to the bus' input queue.
After that is done, it will receive the event whenever another bus instance calls
await bus.Publish(new ServiceAEvent(...));
Is it possible to move error message from error queue to its original queue, programmatically or via UI?
Update
Questions below on the code below:
1 Does the code below apply to Publiser or Subscriber or both?
The code below:
Configure.With(activator)
.Transport(t => (...)) //< use queue "error" here
.Routing(r =>
{
r.AddTransportMessageForwarder(async transportMessage =>
{
var sourceQueue = transportMessage.Headers.TryGetValue(Headers.SourceQueue, out var result)
? result
: throw new ArgumentException($"Could not find '{Headers.SourceQueue}' header");
return ForwardAction.ForwardTo(sourceQueue);
});
})
.Start();
2 Transport method below works for my code. However, the code above suggests using error queue name, will it work?
Where are the Publiser and Subscriber queue name like below specified if the code above is used?
Please provide code for pub sub pattern.
Publisher:
.Transport(t => t.UseAzureServiceBus(Consts.ServiceBusConnectionString, Consts.Publisher))
Subscriber:
.Transport(t=>t.UseAzureServiceBus(Consts.ServiceBusConnectionString, Consts.Subscriber1))
https://github.com/rebus-org/Rebus/wiki/Transport-message-forwarding
Since Rebus uses ordinary queues as its dead-letter queues, it's quite easy to start a bus instance with error as the input queue – then you can e.g. use Rebus' built-in transport message forwarding capability to do that you want to the messages – e.g. forward them to their source queues:
Configure.With(activator)
.Transport(t => (...)) //< use queue "error" here
.Routing(r =>
{
r.AddTransportMessageForwarder(async transportMessage =>
{
var sourceQueue = transportMessage.Headers.TryGetValue(Headers.SourceQueue, out var result)
? result
: throw new ArgumentException($"Could not find '{Headers.SourceQueue}' header");
return ForwardAction.ForwardTo(sourceQueue);
});
})
.Start();
or whatever you want in there.
There also exists a UI, Fleet Manager, that can do this – it replaces the need for dead-letter queues entirely, as it stores failed messages in its database and makes it possible to return the failed messages to their source queues (or another queue, if that's what you want), but it's only available if you're a Rebus Pro subscriber.
Update (with answers to the questions in your update):
1) AddTransportMessageForwarder is only relevant for an endpoint that receives messages.
2) It's the "queue name" specified as an argument to the .Useblablabla method. For example, with Azure Service Bus it would read
.Transport(t => t.UseAzureServiceBus(Consts.ServiceBusConnectionString, "error"))
I use Rebus 3.0.1 with Simpleinjector. I have registered a decorator, for logging, of all the handlers, this way:
container.RegisterDecorator(typeof(IHandleMessages<>), typeof(HandlerLogDecorator<>));
Everything works fine, except the Sagas: because, in LoadSagaDataStep, the following code
var handlerInvokersForSagas = context.Load<HandlerInvokers>()
.Where(l => l.HasSaga)
.ToList();
is not able to find the Saga under the covers.
If I unregister the decorators, saga starts working again.
Any suggestion to conciliate sagas and handlers decorators?
Unfortunately, as you have discovered, Rebus does not expect handlers (including sagas) to be wrapped in decorators.
It uses decorators a lot for various things internally, and it encourages the use of decorators as an extension point for developers, but that only goes for all of Rebus' services like IPipeline, ISubscriptionStorage, etc.
If you want to log stuff in relation to message handling, a better extension point would be to either
a) use Rebus.Events and simply install an event handler like this:
Configure.With(...)
.(...)
.Events(e => {
e.AfterMessageHandled += (bus, headers, message, context, args) => {
// log stuff in here :)
};
})
.Start();
or
b) create an incoming pipeline step that logs what you want to log like this:
Configure.With(...)
.(...)
.Options(o => {
o.Decorate<IPipeline>(c => {
var step = new YourLoggingStep();
var pipeline = c.Get<IPipeline>();
return new PipelineStepInjector(pipeline)
.OnReceive(step, PipelineRelativePosition.After, typeof(DeserializeIncomingMessageStep));
});
})
.Start();
probably wrapping all of the ugly stuff in an extension method that makes usage look more like this:
Configure.With(...)
.(...)
.Options(o => {
o.LogHandledMessages();
})
.Start();
I have followed the pub/sub demo with msmq and am loosing messages when the publisher is started before the subscribers. The msmq has already been created.
My Publisher code in one console app
_activator = new BuiltinHandlerActivator();
Configure.With(_activator)
.Transport(t => t.UseMsmq("PaymentsToTake"))
.Subscriptions(s => s.StoreInMemory())
.Start();
/* In the timer code */
MyDateMessage m = new MyDateMessage()
{
NowTime = DateTime.Now,
Counter = _index
};
_activator.Bus.Publish(m).Wait();
_index++;
My Subscriber Code in another console app
_activator = new BuiltinHandlerActivator();
_activator.Register(() => new PrintDateTime());
Configure.With(_activator)
.Transport(t => t.UseMsmq("PaymentsToTake-Receiver1"))
.Routing(r => r.TypeBased().Map<MyDateMessage>("PaymentsToTake"))
.Start();
_activator.Bus.Subscribe<MyDateMessage>().Wait();
Results
When I run the subscriber, I get the message Sending MyDateMessage -> and then when I run the consumer, the first message that comes up is "53 The time is" hence messages 0-52 were lost!
I suspect this is because you are using the in-mem subscription storage, meaning that the publisher does NOT remember who subscribed when it was previously running.
For most (if not all) real-world scenarios, you should choose some kind of persistent storage for subscriptions. It could be a database like SQL Server, or it could even be a local JSON file.
You just need to change the line
.Subscriptions(s => s.StoreInMemory())
into something like e.g.
.Subscriptions(s => s.UseJsonFile(#"subscriptions.json"))
Could you try and see if that fixes your problem? :)