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? :)
Related
I'm trying to upload something to my firestore database, but if the user doesn't have an internet connection it just tries to upload it foreever withour giving me an error.
Is there a way to cancel it when I don't have a connection?
I see two options:
use react-native-netinfo to detect if there's a connection before uploading, something like
NetInfo.fetch().then(({isConnected}) => {
if (isConnected) {
doSomethingWithFirebase();
}
});
Add a timeout to make it fail, like so:
// make it a promise, if it isn't already
const firebaseResult = new Promise(resolve => {
doSomethingWithFirebase()
.then(() => resolve(true))
.catch(() => resolve(false))
})
// resolve after 30s
const timeout = new Promise(resolve => setTimeout(() => resolve(false), 30 * 1000));
const didUpload = await Promise.race([firebasePromise, timeOut]);
I'd personally go with #2, because you can show the user an error (something like "failed to upload. does your connection work?") but it depends what it's for, like if it's analytics data that you don't want them to know about #1 is good for that.
Edit: as OP pointed out, with #2 the action would take place when the connection came back online again, without notice, which may not be desired behavior. You could indicate that there's an open connection somehow, like with an "uploading" icon, and clear it when it finally resolves (with failure or success).
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 have a scenario, where in when a http port error occurs, the effects gets unsubscribed, and doesn't work anymore. For Example,
#Effect() newMessages$ : Observable<any> = this.actions$
.ofType(SEND_NEW_MESSAGE_ACTION)
.switchMap(action => this.threadsService.saveNewMessage(action.payload))
.catch(() => Observable.of(new ErrorOccurredAction("Error Ocurred while saving
message")) );
lets say error occurs, and it gets displayed in in the UI in the messages section. However after that, the send new message (post) button doesn't do another post, as the effect is no longer subscribed. Any idea how to handle this gracefully?
There are basically two ways (probably more) what you could do:
1. Handle the error inside the switchMap:
#Effect() newMessages$ = this.actions$
.ofType(SEND_NEW_MESSAGE_ACTION)
.switchMap(action => this.threadsService.saveNewMessage(action.payload)
.catch(() => Observable.of(new ErrorOccurredAction("Error Ocurred while saving message")))
);
Meaning, that if there is an error in saveNewMessage, it will not affect the effect, just the single run of saveNewMessage.
2. Re-subscribe the original observable (see the catch-documentation)
#Effect() newMessages$ = this.actions$
.ofType(SEND_NEW_MESSAGE_ACTION)
.switchMap(action => this.threadsService.saveNewMessage(action.payload))
.catch((err, orig) => orig.startWith(new ErrorOccurredAction("Error Ocurred while saving message")));
What the catch here does, it will return the original stream(basically a clean version of newMessages$) for the old subscription to switch the subscription to and initially emits the ErrorOccurredAction.
Both solutions are technically valid solutions, personally I prefer the first one, because it seems to be more intuitive to read for someone who it not super-familiar with the rxjs-api. (But that's just my personal taste)
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)