簡體   English   中英

並行消耗來自一個隊列的消息

[英]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

http://activemq.apache.org/destination-options.html

附注:您不必等待Main方法中的任務,您可以使用Task.WaitAll方法執行此操作, Task.WaitAll方法將阻止主線程,直到所有消息都已傳遞。 另外, 避免使用StartNew方法 ,最好一般使用Task.Run

現在,回到消息。 根據目前的文件

消費者的並發注意事項

在當前實現中,每個IConnection實例都由一個后台線程支持,該后台線程從套接字讀取並將結果事件分派給應用程序。

從版本3.5.0應用程序回調處理程序可以調用阻塞操作(例如IModel.QueueDeclareIModel.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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM