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(...));
Related
I have a simple service that listens to RabbitMQ, calls some gRPC services and does other stuff like DB updates etc. I started noticing Kubernetes pods failing with OOM exception so I took out all the logic from Consume method and controller constructor and started adding back parameters one by one. Everything is normal until I pass my gRPC client to constructor- then unmanaged memory starts going up with each new message and never goes down (the Consume method is still empty). Any ideas what I'm doing wrong?
My client is added in Program.cs like this:
services.AddGrpcClient<Notification.Api.Notification.NotificationClient>(o => { o.Address = new Uri(sso.GrpcOptions.ApiBaseAddress); })
.ConfigureChannel(c =>
{
c.LoggerFactory = LoggerFactory.Create(logging =>
{
logging.AddConsole();
logging.SetMinimumLevel(LogLevel.Warning);
});
c.HttpHandler = new SubdirectoryHandler(new HttpClientHandler()
{
}, sso.GrpcOptions.NotifSubdirectory);
});
Eventually I added gRPC clients with services.AddSingleton() instead of using services.AddGrpcClient() and it fixed the increasing memory consumption.
Long way is inserting meaningful microservices code as derived of ControllerBase class, adding various attributes like HttpGet and Authorize. Then place to Configure method something like app.UseMVC or app.UseRouting and all will be working fine.
But if we have service, registered for example as
services.AddSingleton<WeatherForecastService>();
is it possible call it directly by http://localhost:5000/ ?
if, of course, we have right configuration something similar
app.UseEndpoints(endpoints =>
{
endpoints.MapGet ("/", async context =>
{
await new WeatherForecastService.GetForecastAsync(DateTime.Now);
});
});
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"))
Need help on below issue, I have below method:
public IHttpActionResult Test()
{
Task.Run(() => DoTheStuff())
Return Ok()
}
Note: Here I don't want to use async/await keyword, as I don't care about the result of DoTheStuff() method. I just need to open one thread and execute the code.
DoTheStuff() method refers the objects which are injected through dependency injection (Autofac). and in Module.Config I have registered all the required dependencies with lifetimescope.
Below issue I am facing call to Task.Run(() => DoTheStuff()) starts new thread executing DoTheStuff() method.
At the same time Test() method completes it execution with return Ok(), but DoTheStuff method is still running asynchronously.
With execution of Test() method, the registered dependencies gets disposed, and DoTheStuff() method throws below exception:
Nested lifetime cannot be created from the LifetimeScope as it has
already been disposed
Can someone please let me know how to maintain dependency object instance within the thread?
I can see a couple options.
Extract DoTheStuff into its own service. Register the services that you're injecting and using in DoTheStuff as InstancePerDependency so that the service gets its own instance. See https://autofaccn.readthedocs.io/en/latest/lifetime/instance-scope.html.
Add a Wait:
var t = Task.Run(() => DoTheStuff());
t.Wait();
return Ok();
Let me know if neither of those works.
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)