NSQメッセージキュー(IV)メッセージをプロファイリングロード

メッセージキューの分析NSQ -カタログ
の実用的なアプリケーションでは、クラスタのサービスの一部同時に同じに加入することができるtopic、と同じでchannelケース。ときにnsqdメッセージが必要な時間を考慮するために、クライアントのニーズに送られ、に対処するためのクライアントをサブスクライブに送信されるように、私はそれがメッセージの負荷であると言うでしょう。

負荷が考慮されていない場合は、顧客端末を処理するためのメッセージを送信するために、ランダムなメッセージは、マシンの異なる性能の場合、状況は速度を処理する1つまたは複数のクライアントを発生することがありますが、新しいメッセージの数があります対処する必要性は、他のクライアントがアイドル状態です。理想的な状況は、メッセージを処理するために、現在の比較的自由なクライアントを見つけることです。

nsqアプローチは、クライアントがイニシアチブを取るですnsqd(ニュースレポートは、ある数扱うことができ、独自のRDYコマンド)。nsqdランダムメッセージ処理が実行されるクライアント・メッセージに利用可能である状況メッセージは、各クライアント接続のために処理することができる送信します

下図のように:

クライアントアップデートRDY

設定の最初の記事の例から、我々は消費者を設定してい

    config := nsq.NewConfig()
    config.MaxInFlight = 1000
    config.MaxBackoffDuration = 5 * time.Second
    config.DialTimeout = 10 * time.Second

MaxInFlightプロセスへのメッセージの最大数を設定し、更新するかどうかに応じて変数を算出しRDY
、接続処理の最大数を設定するnsqd updateRDYサーバに送信するクライアントの初期化

func (r *Consumer) maybeUpdateRDY(conn *Conn) {
    inBackoff := r.inBackoff()
    inBackoffTimeout := r.inBackoffTimeout()
    if inBackoff || inBackoffTimeout {
        r.log(LogLevelDebug, "(%s) skip sending RDY inBackoff:%v || inBackoffTimeout:%v",
            conn, inBackoff, inBackoffTimeout)
        return
    }

    remain := conn.RDY()
    lastRdyCount := conn.LastRDY()
    count := r.perConnMaxInFlight()

    // refill when at 1, or at 25%, or if connections have changed and we're imbalanced
    if remain <= 1 || remain < (lastRdyCount/4) || (count > 0 && count < remain) {
        r.log(LogLevelDebug, "(%s) sending RDY %d (%d remain from last RDY %d)",
            conn, count, remain, lastRdyCount)
        r.updateRDY(conn, count)
    } else {
        r.log(LogLevelDebug, "(%s) skip sending RDY %d (%d remain out of last RDY %d)",
            conn, count, remain, lastRdyCount)
    }
}

残りの利用可能な処理の数が場合remain未満又は1に等しく、以下最後のセットの利用可能な数以上であるlastRdyCount1/4、又は平均の利用可能な接続がmaxInFlight 0より大きく、小さいremain、更新RDYステータス

複数存在する場合nsqd、メッセージは、最大平均計算になります。

// perConnMaxInFlight calculates the per-connection max-in-flight count.
//
// This may change dynamically based on the number of connections to nsqd the Consumer
// is responsible for.
func (r *Consumer) perConnMaxInFlight() int64 {
    b := float64(r.getMaxInFlight())
    s := b / float64(len(r.conns()))
    return int64(math.Min(math.Max(1, s), b))
}

以下からのメッセージがある場合nsqdによって送ら呼び出しますmaybeUpdateRDY計算が送信する必要がある場合は、この方法RDYのコマンドは、

func (r *Consumer) onConnMessage(c *Conn, msg *Message) {
    atomic.AddInt64(&r.totalRdyCount, -1)
    atomic.AddUint64(&r.messagesReceived, 1)
    r.incomingMessages <- msg
    r.maybeUpdateRDY(c)
}

上面就是主要的处理逻辑,但还有一些逻辑,就是当消息处理发生错误时,nsq有自己的退避算法backoff也会更新RDY 简单来说就是当发现有处理错误时,来进行重试和指数退避,在退避期间RDY会为0,重试时会先放尝试RDY为1看有没有错误,如果没有错误则全部放开,这个算法这篇帖子我就不详细说了。

服务端nsqd选择客户端进行发送消息

同时订阅同一topic的客户端(comsumer)有很多个,每个客户端根据自己的配置或状态发送RDY命令到nsqd表明自己能处理多少消息量
nsqd服务端会检查每个客户端的的状态是否可以发送消息。也就是IsReadyForMessages方法,判断inFlightCount是否大于readyCount,如果大于或者等于就不再给客户端发送数据,等待Ready后才会再给客户端发送数据

func (c *clientV2) IsReadyForMessages() bool {
    if c.Channel.IsPaused() {
        return false
    }

    readyCount := atomic.LoadInt64(&c.ReadyCount)
    inFlightCount := atomic.LoadInt64(&c.InFlightCount)

    c.ctx.nsqd.logf(LOG_DEBUG, "[%s] state rdy: %4d inflt: %4d", c, readyCount, inFlightCount)

    if inFlightCount >= readyCount || readyCount <= 0 {
        return false
    }

    return true

每一次发送消息inFlightCount会+1并保存到发送中的队列中,当客户端发送FIN会-1在之前的帖子中有说过。

func (p *protocolV2) messagePump(client *clientV2, startedChan chan bool) {
    // ...
    for {
        // 检查订阅状态和消息是否可处理状态 
        if subChannel == nil || !client.IsReadyForMessages() {
            // the client is not ready to receive messages...
            memoryMsgChan = nil
            backendMsgChan = nil
            flusherChan = nil
            // ...
            flushed = true
        } else if flushed {
            memoryMsgChan = subChannel.memoryMsgChan
            backendMsgChan = subChannel.backend.ReadChan()
            flusherChan = nil
        } else {
            memoryMsgChan = subChannel.memoryMsgChan
            backendMsgChan = subChannel.backend.ReadChan()
            flusherChan = outputBufferTicker.C
        }

        select {
        case <-flusherChan:
            // ...
        // 消息处理         
        case b := <-backendMsgChan:
            client.SendingMessage()
            // ...
        case msg := <-memoryMsgChan:
            client.SendingMessage()     
            //...
        }
    }
// ...
}

おすすめ

転載: www.cnblogs.com/li-peng/p/11949258.html