[英]Parallel consuming messages from one queue
我有一個N
消息隊列。 現在我想創建一個控制台應用程序(使用者),它創建給定數量的任務/線程,每個線程將從隊列中獲取1條消息,處理它並接收新消息......直到隊列為空。
到目前為止,這就是我所擁有的:
private static void Main(string[] args)
{
var runner = new Runner();
for (var j = 0; j < 5; j++)
{
var task = Task.Factory.StartNew(() => { runner.ReceiveMessageFromServer(); });
}
}
和
public class QRegistrator : IDisposable
{
private const string activeMqUri = "activemq:tcp://localhost:61616";
private readonly IConnection _connection;
private readonly ISession _session;
public QRegistrator(/*string activeMqUri*/)
{
var connectionFactory = new ConnectionFactory(activeMqUri);
_connection = connectionFactory.CreateConnection();
_connection.Start();
_session = _connection.CreateSession();
}
public void Dispose()
{
_connection?.Close();
}
public IMessageConsumer CreateConsumer(string queueName, string topicName)
{
//there have to be new session for each concurrent consumer
var session = _connection.CreateSession();
var queue = new ActiveMQQueue(queueName);
var consumer = string.IsNullOrWhiteSpace(topicName) ? session.CreateConsumer(queue) : session.CreateConsumer(queue, $"topic = '{topicName}'");
return consumer;
}
}
並在班級Runner
public void ReceiveMessageFromServer()
{
var consumer = _registrator.CreateConsumer("myQueue", null);
while (true)
{
ITextMessage message = consumer.Receive() as ITextMessage;
if (message == null)
{
break;
}
Console.WriteLine("Received message with ID: " + message.NMSMessageId);
DoSomething();
}
}
但是,當我運行此代碼時,有時它不會創建連接,有時它不會創建會話等。我真的不明白。 當我嘗試沒有for循環(但仍然是一個task())時,它的行為相同。 當我嘗試沒有任務,並只調用runner.ReceiveMessageFromServer()
它工作正常。 誰能告訴我我做錯了什么?
你的問題是prefetchPolicy。
persistent queues (default value: 1000)
non-persistent queues (default value: 1000)
persistent topics (default value: 100)
non-persistent topics (default value: Short.MAX_VALUE - 1)
所有消息都被分派到第一個連接的使用者,當另一個消息連接時他沒有收到消息,因此如果您有隊列的並發使用者,則需要將prefetchPolicy設置為低於默認值的值,以更改此行為。 例如,將此jms.prefetchPolicy.queuePrefetch=1
添加到activemq.xml中的uri配置或將其設置在客戶端URL上,如下所示
我不知道C#是否可能,但試試這個
private const string activeMqUri = "activemq:tcp://localhost:61616?jms.prefetchPolicy.queuePrefetch=1";
如果它不起作用,您可以通過將其添加到activemq.xml文件在代理端設置它:
<destinationPolicy>
<policyMap>
<policyEntries>
<!-- http://activemq.apache.org/per-destination-policies.html -->
<policyEntry queue=">" queuePrefetch="1" >
</policyEntry>
</policyEntries>
</policyMap>
</destinationPolicy>
建議使用較大的預取值以實現高消息量的高性能。 但是,對於較低的消息量,每個消息需要很長時間才能處理,預取應設置為1.這可確保消費者一次只處理一條消息。 但是,將預取限制指定為零將導致使用者一次一個地輪詢消息,而不是將消息推送到使用者。
看看http://activemq.apache.org/what-is-the-prefetch-limit-for.html
和
附注:您不必等待Main
方法中的任務,您可以使用Task.WaitAll
方法執行此操作, Task.WaitAll
方法將阻止主線程,直到所有消息都已傳遞。 另外, 避免使用StartNew
方法 ,最好一般使用Task.Run
。
現在,回到消息。 根據目前的文件 :
消費者的並發注意事項
在當前實現中,每個
IConnection
實例都由一個后台線程支持,該后台線程從套接字讀取並將結果事件分派給應用程序。從版本
3.5.0
應用程序回調處理程序可以調用阻塞操作(例如IModel.QueueDeclare
或IModel.BasicCancel
)。 同時調用IBasicConsumer
回調。
所以,根據這個問題和答案 ,你需要為你的消費者使用其他類: EventingBasicConsumer
,像這樣:
var channel = _connection.CreateModel();
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (ch, ea) =>
{
var body = ea.Body;
// ... process the message
channel.BasicAck(ea.DeliveryTag, false);
};
String consumerTag = channel.BasicConsume(queueName, false, consumer);
在這種情況下,您將以基於事件的方式獲得消息,而不會阻止頻道。
其他例子可以在這里或這里找到。 在Andras Nemes的博客中可以找到.Net中消息傳遞的絕佳參考。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.