MassTransit rabbitMq - Why are all my messages skipped - rabbitmq

I am working on a .net core 3.1 app, for some reason my messages are not getting consumed.
service configuration :
services.AddMassTransit(x =>
{
x.AddConsumer<ItemAddedConsumer>();
x.AddBus(provider => Bus.Factory.CreateUsingRabbitMq(cfg =>
{
cfg.UseHealthCheck(provider);
cfg.Host(new Uri($"rabbitmq://{rMqSettings.host}:{rMqSettings.port}"), h =>
{
h.Username(rMqSettings.username);
h.Password(rMqSettings.password);
});
cfg.ReceiveEndpoint("items-service.ItemAdded", ep =>
{
ep.ConfigureConsumeTopology=false;
ep.Bind("ItemAdded");
ep.PrefetchCount = 15;
ep.Consumer<ItemAddedConsumer>(provider);
});
consumer class :
public class ItemAddedConsumer : IConsumer<ItemAdded>
{
private readonly IMediator _mediator;
public ItemAddedConsumer(IMediator mediator)
{
_mediator = mediator;
}
public async Task Consume(ConsumeContext<ItemAdded> context)
{
await _mediator.Send(new ItemAdded(context.Message.Id));
}
}
and this is how i am sending the messages :
Uri uri = new Uri("exchange:ItemAdded?bind=true&queue=items-service.ItemAdded");
var endPoint = await _bus.GetSendEndpoint(uri);
await endPoint.Send(#event);
all messages get sent to a new queue called items-service.ItemAdded_skipped
queues

Make sure the sent message and the consumer are using the same message type, including namespace, as outlined in the docs.
Also, why the overly complicated send endpoint address and receive endpoint configuration? You can change the EntityName of the message (via attribute or the publish topology) and simply use Publish from your message producer.

Related

Rebus: Could not resolve Rebus.Bus.IBus with decorator depth 0 - registrations: Rebus.Injection.Injectionist+Handler"

Getting error while working with Azuer Service Bus, if I use MSMQ queue then no error, so please suggest what is wrong with code or any missing configuration?
Note: Below Rebus initialization code is running under web applcatication:
_adapter = new BuiltinHandlerActivator();
_adapter.Register(() => new HubHandler());
var configurer = Configure.With(_adapter)
.Options(o =>
{
o.SetNumberOfWorkers(RebusConfigSettings.Workers);
o.SimpleRetryStrategy(maxDeliveryAttempts: RebusConfigSettings.RetryAttempts);
})
.Logging(l => l.Log4Net())
.Routing(r => r.TypeBasedRoutingFromAppConfig())
.Events(x =>
{
x.BeforeMessageSent += (bus, headers, message, context) =>
{
if (RebusConfigSettings.TimeToBeReceived != "-1")
{
headers.Add(Headers.TimeToBeReceived, RebusConfigSettings.TimeToBeReceived);
headers.Add(Headers.Express, "rebus-express");
}
};
});
string azureServiceBusUrl = EnvironmentVariablesManager.GetEnvironmentVariable("AzureServiceBusUrl");
configurer.Transport(t => t.UseAzureServiceBus(azureServiceBusUrl, RebusConfigSettings.InputQueue)).Start();
Implementation of HubHandler class
public class HubHandler: IHandleMessages<AuthenticationResponse>
{
public HubHandler() { }
public async Task Handle(AuthenticationResponse message)
{
//...
}
}
Inner exception:
The remote server returned an error: (401) Unauthorized. Invalid TLS version. TrackingId:4236d339-2355-4074-b187-9d07904ddb30_G62, SystemTracker:jkc-development.servicebus.windows.net:queuename, Timestamp:2022-06-13T13:34:08
It was problem with TLS version, in Azure it was set to 1.2 (Service Bus--> Configuration --> Security) and where in web config, httpRuntime-->targetFramework was set to 4.5.
So after changing .net framework from 4.5 to 4.7.2 in web project then it is started working.

Why is JobConsumer not being hit/run?

I am trying out the new MassTransit IJobConsumer implementation, and although I've tried to follow the documentation, the JobConsumer I have written is never being run/hit.
I have:
created the JobConsumer which has a run method that runs the code I need it to
public class CalculationStartRunJobConsumer : IJobConsumer<ICalculationStartRun>
{
private readonly ICalculationRunQueue runQueue;
public CalculationStartRunJobConsumer(ICalculationRunQueue runQueue)
{
this.runQueue = runQueue;
}
public Task Run(JobContext<ICalculationStartRun> context)
{
return Task.Run(
() =>
{
var longRunningJob = new LongRunningJob<ICalculationStartRun>
{
Job = context.Job,
CancellationToken = context.CancellationToken,
JobId = context.JobId,
};
runQueue.StartSpecial(longRunningJob);
},
context.CancellationToken);
}
}
I have registered that consumer trying both ConnectReceiveEndpoint and AddConsumer
Configured the ServiceInstance as shown in the documentation
services.AddMassTransit(busRegistrationConfigurator =>
{
// TODO: Get rid of this ugly if statement.
if (consumerTypes != null)
{
foreach (var consumerType in consumerTypes)
{
busRegistrationConfigurator.AddConsumer(consumerType);
}
}
if(requestClientType != null)
{
busRegistrationConfigurator.AddRequestClient(requestClientType);
}
busRegistrationConfigurator.UsingRabbitMq((context, cfg) =>
{
cfg.UseNewtonsoftJsonSerializer();
cfg.UseNewtonsoftJsonDeserializer();
cfg.ConfigureNewtonsoftJsonSerializer(settings =>
{
// The serializer by default omits fields that are set to their default value, but this causes unintended effects
settings.NullValueHandling = NullValueHandling.Include;
settings.DefaultValueHandling = DefaultValueHandling.Include;
return settings;
});
cfg.Host(
messagingHostInfo.HostAddress,
hostConfigurator =>
{
hostConfigurator.Username(messagingHostInfo.UserName);
hostConfigurator.Password(messagingHostInfo.Password);
});
cfg.ServiceInstance(instance =>
{
instance.ConfigureJobServiceEndpoints(serviceCfg =>
{
serviceCfg.FinalizeCompleted = true;
});
instance.ConfigureEndpoints(context);
});
});
});
Seen that the queue for the job does appear in the queue for RabbitMQ
When I call .Send to send a message to that queue, it does not activate the Run method on the JobConsumer.
public async Task Send<T>(string queueName, T message) where T : class
{
var endpointUri = GetEndpointUri(messagingHostInfo.HostAddress, queueName);
var sendEndpoint = await bus.GetSendEndpoint(endpointUri);
await sendEndpoint.Send(message);
}
Can anyone help?
Software
MassTransit 8.0.2
MassTransit.RabbitMq 8.0.2
MassTransit.NewtonsoftJson 8.0.2
.NET6
Using in-memory for JobConsumer
The setup of any type of repository for long running jobs is missing. We needed to either:
explicitly specify that it was using InMemory (missing from the docs)
Setup saga repositories using e.g. EF Core.
As recommended by MassTransit, we went with the option of setting up saga repositories by implementing databases and interacting with them using EF Core.

MassTransit with RabbitMq Request/Response wrong reply address because of network segments

I have a web app that uses a request/response message in Masstransit.
This works on out test environment, no problem.
However on the customer deployment we face a problem. At the customer site we do have two network segments A and B. The component doing the database call is in segment A, the web app and the RabbitMq server in segment B.
Due to security restrictions the component in segment A has to go through a loadbalancer with a given address. The component itself can connect to RabbitMQ with Masstransit. So far so good.
The web component on segment B however uses the direct address for the RabbitMq server. When the web component now is starting the request/response call, I can see that the message arrives at the component in segment A.
However I see that the consumer tries to call the RabbitMQ server on the "wrong" address. It uses the address the web component uses to issue the request. However the component in segment A should reply on the "loadbalancer" address.
Is there a way to configure or tell the RespondAsync call to use the connection address configured for that component?
Of course the easiest would be to have the web component also connect through the loadbalancer, but due to the network segments/security setup the loadbalancer is only reachable from segment A.
Any input/help is appreciated.
I had a similar problem with rabbitmq federation. Here's what I did.
ResponseAddressSendObserver
class ResponseAddressSendObserver : ISendObserver
{
private readonly string _hostUriString;
public ResponseAddressSendObserver(string hostUriString)
{
_hostUriString = hostUriString;
}
public Task PreSend<T>(SendContext<T> context)
where T : class
{
if (context.ResponseAddress != null)
{
// Send relative response address alongside the message
context.Headers.Set("RelativeResponseAddress",
context.ResponseAddress.AbsoluteUri.Substring(_hostUriString.Length));
}
return Task.CompletedTask;
}
...
}
ResponseAddressConsumeFilter
class ResponseAddressConsumeFilter : IFilter<ConsumeContext>
{
private readonly string _hostUriString;
public ResponseAddressConsumeFilter(string hostUriString)
{
_hostUriString = hostUriString;
}
public Task Send(ConsumeContext context, IPipe<ConsumeContext> next)
{
var responseAddressOverride = GetResponseAddress(_hostUriString, context);
return next.Send(new ResponseAddressConsumeContext(responseAddressOverride, context));
}
public void Probe(ProbeContext context){}
private static Uri GetResponseAddress(string host, ConsumeContext context)
{
if (context.ResponseAddress == null)
return context.ResponseAddress;
object relativeResponseAddress;
if (!context.Headers.TryGetHeader("RelativeResponseAddress", out relativeResponseAddress) || !(relativeResponseAddress is string))
throw new InvalidOperationException("Message has ResponseAddress but doen't have RelativeResponseAddress header");
return new Uri(host + relativeResponseAddress);
}
}
ResponseAddressConsumeContext
class ResponseAddressConsumeContext : BaseConsumeContext
{
private readonly ConsumeContext _context;
public ResponseAddressConsumeContext(Uri responseAddressOverride, ConsumeContext context)
: base(context.ReceiveContext)
{
_context = context;
ResponseAddress = responseAddressOverride;
}
public override Uri ResponseAddress { get; }
public override bool TryGetMessage<T>(out ConsumeContext<T> consumeContext)
{
ConsumeContext<T> context;
if (_context.TryGetMessage(out context))
{
// the most hackish part in the whole arrangement
consumeContext = new MessageConsumeContext<T>(this, context.Message);
return true;
}
else
{
consumeContext = null;
return false;
}
}
// all other members just delegate to _context
}
And when configuring the bus
var result = MassTransit.Bus.Factory.CreateUsingRabbitMq(cfg =>
{
var host = cfg.Host(new Uri(hostAddress), h =>
{
h.Username(...);
h.Password(...);
});
cfg.UseFilter(new ResponseAddressConsumeFilter(hostAddress));
...
});
result.ConnectSendObserver(new ResponseAddressSendObserver(hostAddress));
So now relative response addresses are sent with the messages and used on the receiving side.
Using observers to modify anything is not recommended by the documentation, but should be fine in this case.
Maybe three is a better solution, but I haven't found one. HTH

Unit testing for a publish method in mass transit using in memory bus

I am new to MassTransit and messaging. I am trying to write a unit test for a IBus.Publish and am not able to succeed with the result.
I am observing the fault from RabbitMQ and my observer looks like this:
public class FaultObserver : IReceiveObserver
{
public FaultObserver(IRequestUpdater statusUpdater,Lazy<IBus> bus)
{
this.statusUpdater = statusUpdater;
this.bus = bus;
}
public async Task ConsumeFault<T>(ConsumeContext<T> context, TimeSpan duration, string consumerType, Exception exception) where T : class
{
}
}
and my tests looks like the below code
var bus = fixture.Freeze<Mock<IBus>>();
bus.Setup(bu => bu.Publish<ReportFailedEvent>(It.IsAny<ReportFailedEvent>(),It.IsAny<CancellationToken>())).Verifiable();
var sut = fixture.Create<ReportRequestedFaultObserver>();
// Act
await sut.ConsumeFault(context.Object,new TimeSpan(0,0,1),string.Empty,exception);
// Assert
//bus.Verify(b => b.Publish<ReportFailedEvent>(It.IsAny<ReportFailedEvent>(), It.IsAny<CancellationToken>()), Times.Exactly(1));
bus.Verify(b =>b.Publish<ReportFailedEvent>(new ReportFailedEvent(request,exception.Message),It.IsAny<CancellationToken>()),Times.Once());
my setup looks like
[SetUp]
public void SetUp()
{
fixture.Customize(new AutoMoqCustomization());
var inMemoryTransportCache = new InMemoryTransportCache(Environment.ProcessorCount);
bus = Bus.Factory.CreateUsingInMemory(configure =>
{
configure.SetTransportProvider(inMemoryTransportCache);
configure.ReceiveEndpoint("input_queue", configurator =>
{
configurator.Handler<(cc =>
{
});
});
});
bus.Start().Ready.Wait();
}
I am not able to mock my Publish method. Does anyone knows what I am doing wrong?
Your mock for the bus is wrong. It should be:
bus.Setup(bu => bu.Publish<ReportFailedEvent>(
It.IsAny<object>(), It.IsAny<CancellationToken>()));
Publish takes an object and a CancellationToken, as per the interface definition in MassTransit:
Task Publish(object message, CancellationToken cancellationToken = default(CancellationToken));
Also, if you want to check the contents of the message, you can use the Moq Callback extension:
ReportFailedEvent message = null;
bus
.Setup(bu => bu.Publish<ReportFailedEvent>(It.IsAny<object>(), It.IsAny<CancellationToken>()))
.Callback<object, CancellationToken>((a, b) => { message = a as ReportFailedEvent; });
//.. your system under test code....
Assert.That(message.Property, Is.EqualTo(expectation));
If you are setting the priority, the MassTransit interface looks like this:
public static Task Publish<T>(
this IPublishEndpoint endpoint,
T message,
Action<PublishContext<T>> callback,
CancellationToken cancellationToken = default(CancellationToken))
where T : class;
And an example would be:
bus.Publish<ReportFailedEvent>(message, context =>
{
context.SetPriority(messagePriority.Priority);
});
And the accompanying Moq is:
ReportFailedEvent message = null;
bus
.Setup(bu => bu.Publish<ReportFailedEvent>(It.IsAny<object>(), It.IaAny<Action<PublishContext<ReportFailedEvent>>>(), It.IsAny<CancellationToken>()))
.Callback<object, Action<PublishContext<ReportFailedEvent>>, CancellationToken>((a, b, c) => { message = a as ReportFailedEvent; });
Just as a side note, ideally you should be publishing the interfaces rather than classes, so IReportFailedEvent rather than ReportFailedEvent.

Adding values to header in MassTransit.RabbitMq

I am using MassTransit 3.0.0.0 and I have a hard time understanding how to intercept messages in a Request-Response scenario on their way out and add some information to the headers field that I can read on the receiver's end.
I was looking at the Middleware, as recommended in the MassTransit docs - see Observers warning - but the context you get on the Send is just a Pipe context that doesn't have access to the Headers field so I cannot alter it. I used the sample provided in Middleware page.
I then, looked at IPublishInterceptor
public class X<T> : IPublishInterceptor<T> where T : class, PipeContext
{
public Task PostPublish(PublishContext<T> context)
{
return new Task(() => { });
}
public Task PostSend(PublishContext<T> context, SendContext<T> sendContext)
{
return new Task(() => { });
}
public Task PrePublish(PublishContext<T> context)
{
context.Headers.Set("ID", Guid.NewGuid().ToString());
return new Task(() => { });
}
public Task PreSend(PublishContext<T> context, SendContext<T> sendContext)
{
context.Headers.Set("ID", Guid.NewGuid().ToString());
return new Task(() => { });
}
}
Which is very clear and concise. However, I don't know where it is used and how to link it to the rest of the infrastructure. As it stands, this is just an interface that is not really linked to anything.
If you need to add headers when a message is being sent, you can add middleware components to either the Send or the Publish pipeline as shown below. Note that Send filters will apply to all messages, whereas Publish filters will only apply to messages which are published.
// execute a synchronous delegate on send
cfg.ConfigureSend(x => x.Execute(context => {}));
// execute a synchronous delegate on publish
cfg.ConfigurePublish(x => x.Execute(context => {}));
The middleware can be configured on either the bus or individual receive endpoints, and those configurations are local to where it's configured.
You can also add headers in the consumer class:
public async Task Consume(ConsumeContext<MyMessage> context)
{
....
await context.Publish<MyEvent>(new { Data = data }, c => AddHeaders(c));
}
public static void AddHeaders(PublishContext context)
{
context.Headers.Set("CausationId", context.MessageId);
}
http://masstransit-project.com/MassTransit/advanced/middleware/custom.html
Shows adding an extension method to make it clear what you're setup. That's a big help if it's an interceptor that will be used a lot, so it's clear that purpose. You can skip that step if you want.
Basically, just...
cfg.AddPipeSpecification(new X<MyMessage>());
When configuring the transport.