Monitor multiple RabbitMq subscriptions using single application - ruby

I have an application that I wrote in .NET that can monitor multiple RabbitMq queues using a single consumer.
For example:
using (IConnection connection = factory.CreateConnection())
{
using (IModel channel = connection.CreateModel())
{
var _consumer = new QueueingBasicConsumer(channel);
string[] list = new string[] { "Queue1", "Queue2", "Queue3", "Queue4" };
_consumer = new QueueingBasicConsumer(channel);
foreach (string currQueueName in list)
{
channel.QueueDeclare(currQueueName, false, false, false, null);
channel.BasicConsume(currQueueName, true, _consumer);
}
while (true)
{
var ea = (BasicDeliverEventArgs)_consumer.Queue.Dequeue();
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(" [x] Received {0}", message);
ProcessMessage(message);
}
}
}
Basically, I just want to be able to distribute work across multiple queues, but only have a single application consuming them all (or multiple applications can be deployed and perform the same function).
I'm trying to spread work out across queues so that consumers are taking jobs equally across the queues.
Is this possible using Bunny, or the native Ruby driver?

I guess I just needed to spend a bit more time on this.
I'm adding a brief answer just in case this helps anyone else (or if someone wants to provide a cleaner solution :P)
client = Bunny.new
client.start
channel = client.create_channel
queues = %w(testing1 testing2 testing3)
queues.each do |name|
channel.queue(name).subscribe(timeout: 2) do |info, props, body|
puts "[#{name}] Got #{body}"
end
end
loop do
sleep 1
end
This effectively does what I was describing in the OP.

Related

Masstransit - turnout with a consumer

I have a long running PDF generation as part of a SAGA, so I read about the Turnout and I am trying to use it. However I have a complex consumer - several calls to DB, dependency on 3rd party service for generating the PDF and etc. I have troubles finding a good example how exactly I should use the Turnout endpoint.
My current usage is:
c.TurnoutEndpoint<PdfGenerationCommand>(host, "pdf-generation-TURNOUT", e =>
{
e.SuperviseInterval = TimeSpan.FromSeconds(30);
e.SetJobFactory(async context =>
{
var consumer = container.GetInstance<PdfGenerationCommandConsumer>();
var sw = new Stopwatch();
sw.Start();
Console.WriteLine($"{DateTime.Now} - [PdfGenerationCommand] Job started!");
await consumer.Consume(context.Command).ConfigureAwait(false);
sw.Stop();
Console.WriteLine($"{DateTime.Now} - [PdfGenerationCommand] Job finished in {sw.ElapsedMilliseconds}!");
});
});
However, the messages end up in the _skipped queue. I am doing something wrong and I cannot see it

Replay a particular type of event from eventstore

I am currently using the Event Store to handle my events. I currently need to replay a particular type of event as I have made changes in the way they are subscribed and written to DB.
Is this possible? If so, how can it be done? Thanks.
You cannot tell EventStore to replay a specific event onto a persistent subscription because the point of the persistent subscription is to keep state for the subscribers.
To achieve this kind of fix you would really need a catch up application to do the work.
And really if you think about, if you replayed ALL the events to a new database then you would have the correct data in there?
So I have a console application that reuses the same logic as the persistent connection but the only difference is:
I change the target database connection string - So this would be a new Database or Collection (not the broken one)
It connects to EventStore and replays all the events from the start
It rebuilds the entire database to the correct state
Switch the business over to the new database
This is the point of EventStore - You just replay all the events to build any database at any time and it will be correct
Your persistent connections deal with new, incoming events and apply updates.
If you enable $by_event_type projection than you can access that projection stream under
/streams/$et-{event-type}
https://eventstore.org/docs/projections/system-projections/index.html
Then you can read it using .net api if you wish.
Here is some code to get you started
private static T GetInstanceOfEvent<T>(ResolvedEvent resolvedEvent) where T : BaseEvent
{
var metadataString = Encoding.UTF8.GetString(resolvedEvent.Event.Metadata);
var eventClrTypeName = JObject.Parse(metadataString).Property(EventClrTypeHeader).Value;
var #event = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(resolvedEvent.Event.Data), Type.GetType((string) eventClrTypeName));
if (!(#event is BaseEvent))
{
throw new MessageDeserializationException((string) eventClrTypeName, metadataString);
}
return #event as T;
}
private static IEventStoreConnection GetEventStoreConnection()
{
var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["EventStore"].ConnectionString;
var connection = EventStoreConnection.Create(connectionString);
connection.ConnectAsync().Wait();
return connection;
}
private static string GetStreamName<T>() where T : BaseEvent
{
return "$et-" + typeof(T).Name;
}
And to read events you can use this code snippet
StreamEventsSlice currentSlice;
long nextSliceStart = StreamPosition.Start;
const int sliceCount = 200;
do
{
currentSlice = await esConnection.ReadStreamEventsForwardAsync(streamName, nextSliceStart, sliceCount, true);
foreach (var #event in currentSlice.Events)
{
var myEvent = GetInstanceOfEvent<OrderMerchantFeesCalculatedEvent>(#event);
TransformEvent(myEvent);
}
nextSliceStart = currentSlice.NextEventNumber;
} while (currentSlice.IsEndOfStream == false);

Async sends in .NET ActiveMQ

I'm looking to increase the performance of a high-throughput producer that I'm writing against ActiveMQ, and according to this useAsyncSend will:
Forces the use of Async Sends which adds a massive performance boost;
but means that the send() method will return immediately whether the
message has been sent or not which could lead to message loss.
However I can't see it making any difference to my simple test case.
Using this very basic application:
const string QueueName = "....";
const string Uri = "....";
static readonly Stopwatch TotalRuntime = new Stopwatch();
static void Main(string[] args)
{
TotalRuntime.Start();
SendMessage();
Console.ReadLine();
}
static void SendMessage()
{
var session = CreateSession();
var destination = session.GetQueue(QueueName);
var producer = session.CreateProducer(destination);
Console.WriteLine("Ready to send 700 messages");
Console.ReadLine();
var body = new byte[600*1024];
Parallel.For(0, 700, i => SendMessage(producer, i, body, session));
}
static void SendMessage(IMessageProducer producer, int i, byte[] body, ISession session)
{
var message = session.CreateBytesMessage(body);
var sw = new Stopwatch();
sw.Start();
producer.Send(message);
sw.Stop();
Console.WriteLine("Running for {0}ms: Sent message {1} blocked for {2}ms",
TotalRuntime.ElapsedMilliseconds,
i,
sw.ElapsedMilliseconds);
}
static ISession CreateSession()
{
var connectionFactory = new ConnectionFactory(Uri)
{
AsyncSend = true,
CopyMessageOnSend = false
};
var connection = connectionFactory.CreateConnection();
connection.Start();
var session = connection.CreateSession(AcknowledgementMode.AutoAcknowledge);
return session;
}
I get the following output:
Ready to send 700 messages
Running for 2430ms: Sent message 696 blocked for 12ms
Running for 4275ms: Sent message 348 blocked for 1858ms
Running for 5106ms: Sent message 609 blocked for 2689ms
Running for 5924ms: Sent message 1 blocked for 2535ms
Running for 6749ms: Sent message 88 blocked for 1860ms
Running for 7537ms: Sent message 610 blocked for 2429ms
Running for 8340ms: Sent message 175 blocked for 2451ms
Running for 9163ms: Sent message 89 blocked for 2413ms
.....
Which shows that each message takes about 800ms to send and the call to session.Send() blocks for about two and a half seconds. Even though the documentation says that
"send() method will return immediately"
Also these number are basically the same if I either change the parallel for to a normal for loop or change the AsyncSend = true to AlwaysSyncSend = true so I don't believe that the async switch is working at all...
Can anyone see what I'm missing here to make the send asynchronous?
After further testing:
According to ANTS performance profiler that vast majority of the runtime is being spent waiting for synchronization. It appears that the issue is that the various transport classes block internally through monitors. In particular I seem to get hung up on the MutexTransport's OneWay method which only allows one thread to access it at a time.
It looks as though the call to Send will block until the previous message has completed, this explains why my output shows that the first message blocked for 12ms, while the next took 1858ms. I can have multiple transports by implementing a connection-per-message pattern which improves matters and makes the message sends work in parallel, but greatly increases the time to send a single message, and uses up so many resources that it doesn't seem like the right solution.
I've retested all of this with 1.5.6 and haven't seen any difference.
As always the best thing to do is update to the latest version (1.5.6 at the time of this writing). A send can block if the broker has producer flow control enabled and you've reached a queue size limit although with async send this shouldn't happen unless you are sending with a producerWindowSize set. One good way to get help is to create a test case and submit it via a Jira issue to the NMS.ActiveMQ site so that we can look into it using your test code. There have been many fixes since 1.5.1 so I'd recommend giving that new version a try as it could already be a non-issue.

Azure queues Windows Phone

I have problem retrieve Azure messages from Queue on Windows Phone 7.
To add message I am using such code:
var queueClient = CloudStorageContext.
Current.
Resolver.
CreateCloudQueueClient() as CloudQueueClient;
var queue = queueClient.GetQueueReference("queuein");
queue.Create(
r => queue.AddMessage(
new CloudQueueMessage { AsBytes = Encoding.UTF8.GetBytes(msg) },
c =>
{
// Some logic here.
}));
To get message back it looks like I have to follow similar principle and use queue instance.
But GetMessage() method has VOID return type:
Could you guys help me to understand what the hell is going on in Mobile version of Azure queues, because in Windows console application those methods has return type.
Async patterns are a bit different on the phone. You need to handle the message in the callback function.
ShowAmWorkingInUILol = true;
// snip
queue.GetMessage(TimeSpan.FromMinutes(1), OnMessageReturned);
}
private void OnMessageReturned(CloudOperationResponse<CloudQueueMessage> response)
{
ShowAmWorkingInUILol = false;
// here's your response.
}

Windows Workflow Foundation 4.0 and Tracking

I'm working with the Beta 2 version of Visual Studio 2010 to get some advanced learning using WF4. I've been working with the SqlTracking Sample in the WF_WCF_Samples SDK, and have gotten a pretty good understanding of how to emit and store tracking data in a SQL Database, but haven't seen anything on how to query the data when needed. Does anyone know if there are any .Net classes that are to be used for querying the tracking data, and if so are there any known samples, tutorials, or articles that describe how to query the tracking data?
According to Matt Winkler, from the Microsoft WF4 Team, there isn't any built in API for querying the tracking data, the developer must write his/her own.
These can help:
WorkflowInstanceQuery Class
Workflow Tracking and Tracing
Tracking Participants in .NET 4 Beta 1
Old question, I know, but there is actually a more or less official API in AppFabric: Windows Server AppFabric Class Library
You'll have to find the actual DLL's in %SystemRoot%\AppFabric (after installing AppFabric, of course). Pretty weird place to put it.
The key classes to look are at are SqlInstanceQueryProvider, InstanceQueryExecuteArgs. The query API is asynchronous and can be used something like this (C#):
public InstanceInfo GetWorkflowInstanceInformation(Guid workflowInstanceId, string connectionString)
{
var instanceQueryProvider = new SqlInstanceQueryProvider();
// Connection string to the instance store needs to be set like this:
var parameters = new NameValueCollection()
{
{"connectionString", connectionString}
};
instanceQueryProvider.Initialize("Provider", parameters);
var queryArgs = new InstanceQueryExecuteArgs()
{
InstanceId = new List<Guid>() { workflowInstanceId }
};
// Total ruin the asynchronous advantages and use a Mutex to lock on.
var waitEvent = new ManualResetEvent(false);
IEnumerable<InstanceInfo> retrievedInstanceInfos = null;
var query = instanceQueryProvider.CreateInstanceQuery();
query.BeginExecuteQuery(
queryArgs,
TimeSpan.FromSeconds(10),
ar =>
{
lock (synchronizer)
{
retrievedInstanceInfos = query.EndExecuteQuery(ar).ToList();
}
waitEvent.Set();
},
null);
var waitResult = waitEvent.WaitOne(5000);
if (waitResult)
{
List<InstanceInfo> instances = null;
lock (synchronizer)
{
if (retrievedInstanceInfos != null)
{
instances = retrievedInstanceInfos.ToList();
}
}
if (instances != null)
{
if (instances.Count() == 1)
{
return instances.Single();
}
if (!instances.Any())
{
Log.Warning("Request for non-existing WorkflowInstanceInfo: {0}.", workflowInstanceId);
return null;
}
Log.Error("More than one(!) WorkflowInstanceInfo for id: {0}.", workflowInstanceId);
}
}
Log.Error("Time out retrieving information for id: {0}.", workflowInstanceId);
return null;
}
And just to clarify - this does NOT give you access to the tracking data, which are stored in the Monitoring Database. This API is only for the Persistence Database.

Resources