|
Ultimamente mi trovo a lavorare su di un progetto che utilizza WCF e MSMQ. Una delle classiche operazioni quando si lavora con queste tecnologie è sicuramente recuperare il numero di messaggi presenti in una coda. Tra documentazione e ricerche su internet, alla fine si “scopre” che ci sono diversi metodi per risolvere lo stesso problema: - Cursori di MSMQ
- Utilizzo di GetAllMessages
- Utilizzo di GetEnumerator2
- PowerShell
- Performance Counter
Traducendo il tutto in righe di codice, per il punto 1) abbiamo: public int CountByCursor()
{
int count = 0;
Cursor cursor = _messageQueue.CreateCursor();
Message m = CursorPeekWithoutTimeout(cursor, PeekAction.Current);
if (m != null)
{
count = 1;
while ((m = CursorPeekWithoutTimeout(cursor, PeekAction.Next)) != null)
{
count++;
}
if (m != null) m.Dispose();
}
cursor.Dispose();
return count;
}
private Message CursorPeekWithoutTimeout(Cursor cursor, PeekAction action)
{
Message ret = null;
try
{
ret = _messageQueue.Peek(new TimeSpan(1), cursor, action);
}
catch (MessageQueueException mqe)
{
if (mqe.MessageQueueErrorCode != MessageQueueErrorCode.IOTimeout)
{
throw;
}
}
return ret;
}
Sinceramente questo è il metodo che più non mi piace, sarà per la parola “Cursore”, sarà per la gestione dell’eccezione, sarà perchè la vedo poco pulita.
Passiamo al punto 2):
public int CountByGetAllMessages()
{
return _messageQueue.GetAllMessages().Length;
}
Semplice e pulita, ma come vedremo a breve, le prestazioni scendono con l’aumentare dei messaggi in coda.
Punto 3), “GetMessageEnumerator2”:
var x = _messageQueue.GetMessageEnumerator2();
int counter = 0;
while (x.MoveNext())
{
counter++;
}
return counter;
Sfruttiamo il MessageEnumerator ritornato dal metodo GetEnumerator2 ed iteriamo per spostare il cursore fino alla fine della coda.
Punto 4), “PowerShell” (in questo caso mi sono limitato a copiare del codice trovato in rete):
var path = string.Format(@"\\{0}\root\CIMv2", machine);
ManagementScope scope;
if (string.IsNullOrEmpty(username))
{
scope = new ManagementScope(path);
}
else
{
var options = new ConnectionOptions { Username = username, Password = password };
scope = new ManagementScope(path, options);
}
scope.Connect();
if (queuePath.StartsWith(".\\")) queuePath = queuePath.Replace(".\\", string.Format("{0}\\", machine));
string queryString = String.Format("SELECT * FROM Win32_PerfFormattedData_msmq_MSMQQueue");
var query = new ObjectQuery(queryString);
var searcher = new ManagementObjectSearcher(scope, query);
IEnumerable<int> messageCountEnumerable =
from ManagementObject queue in searcher.Get()
select (int)(UInt64)queue.GetPropertyValue("MessagesInQueue");
var x = messageCountEnumerable.First();
return x;
ed infine il punto 5) “Performance Counter”:
System.Diagnostics.PerformanceCounter backupQueueCounter = new System.Diagnostics.PerformanceCounter(
"MSMQ Queue", "Messages in Queue", "queue_path");
return backupQueueCounter.NextValue();
Bene, ora quale usare ? Proviamo con un piccolo test: ad una coda privata aggiungiamo 10.000 messaggi per volta (all’interno di un ciclo) e proviamo ad utilizzare i metodi su descritti per recuperare il numero di messaggi presenti in coda (si suppone che tra un conteggio e l’altro nella coda non vengano aggiunti altri messaggi). Iteriamo il procedimento per tre volte.
Di seguito i risultati ottenuti (espressi in millisecondi, per le misure del tempo di esecuzione è stata utilizzata la classe System.Diagnostics.Stopwatcher):
Prima Iterazione
#Msgs |
MSMQ Cursor |
GetAllMessages |
GetEnumerator2 |
PowerShell |
Performance C. |
10.000 |
449,816 |
580,703 |
36,37 |
78,543 |
351,86 |
20.000 |
928,18 |
1.297,72 |
78,16 |
9,888 |
0,462 |
30.000 |
1.340,44 |
1.854,52 |
137,82 |
10,563 |
1,121 |
40.000 |
1.769,73 |
2.656,28 |
194,652 |
12,061 |
3,187 |
50.000 |
2.188,74 |
3.427,84 |
190,672 |
10,739 |
0,452 |
60.000 |
2.631,29 |
3.909,71 |
231,995 |
10,219 |
0,364 |
70.000 |
3.006,52 |
4.771,09 |
264,112 |
5.802,42 |
0,462 |
80.000 |
3.469,49 |
6.116,47 |
309,691 |
10,075 |
0,545 |
90.000 |
4.000,00 |
6.134,66 |
372,885 |
9,876 |
0,435 |
100.000 |
4.544,33 |
6.932,08 |
424,196 |
9,024 |
0,41 |
Seconda Iterazione
#Msgs |
MSMQ Cursor |
GetAllMessages |
GetEnumerator2 |
PowerShell |
Performance C. |
10.000 |
475,357 |
609,866 |
38,022 |
285,978 |
351,955 |
20.000 |
912,591 |
1.315,49 |
76,912 |
10,453 |
0,502 |
30.000 |
1.417,26 |
2.066,92 |
122,745 |
11,156 |
0,366 |
40.000 |
1.901,66 |
2.718,64 |
153,373 |
9,947 |
0,349 |
50.000 |
2.393,97 |
3.386,43 |
203,553 |
10,962 |
0,358 |
60.000 |
2.659,28 |
4.546,84 |
280,257 |
9,832 |
0,413 |
70.000 |
3.246,77 |
4.938,01 |
278,404 |
11,664 |
0,517 |
80.000 |
3.718,89 |
5.881,67 |
330,857 |
13,688 |
0,506 |
90.000 |
4.230,99 |
6.677,00 |
362,374 |
10,155 |
0,508 |
100.000 |
4.832,89 |
7.585,19 |
464,216 |
16,495 |
0,681 |
Terza Iterazione
#Msgs |
MSMQ Cursor |
GetAllMessages |
GetEnumerator2 |
PowerShell |
Performance C. |
10.000 |
533,294 |
621,392 |
41,496 |
79,286 |
376,431 |
20.000 |
911,513 |
1.338,41 |
79,827 |
9,354 |
0,38 |
30.000 |
1.339,34 |
2.123,46 |
120,693 |
12,969 |
0,414 |
40.000 |
1.799,28 |
2.658,01 |
158,606 |
9,423 |
0,374 |
50.000 |
2.196,96 |
3.221,87 |
199,273 |
10,413 |
0,426 |
60.000 |
2.562,49 |
4.140,15 |
257,22 |
8,81 |
0,411 |
70.000 |
3.358,83 |
4.715,56 |
327,474 |
14,808 |
0,53 |
80.000 |
4.417,58 |
6.690,44 |
316,044 |
17,334 |
0,825 |
90.000 |
4.185,52 |
6.229,67 |
340,457 |
11,773 |
0,396 |
100.000 |
4.407,13 |
6.658,46 |
390,498 |
12,944 |
0,589 |
Dai risultati ottenuti si ottiene che l’utilizzo del “Performance Counter”, in caso di letture successive, sembrerebbe essere quello più efficiente.
In attesa di altri giudizi ed approfondimenti :-).
|