中心にソースを解析する解析NSQメッセージキュー(B)

前回のポストで分析NSQメッセージキュー(a)の紹介と分散型の実装原理は、私が分散的にnslookupコマンドであり実現するために、1が直接接続され、NSQ二つの方法を紹介しましたおそらく、使用、および実装の原則について述べ、物事を理解するのが難しいものは何もありません、私はこの記事nsq分散型電源や産業の一つを実現するには、それを見るために皆のための論理的な表示でした。

nsqd通信とnsqlookupdを達成

前回のスタートではnsqd、私は、次のコマンドを使用し、私は、パラメータを指定しました--lookupd-tcp-address

./nsqd -tcp-address ":8000"  -http-address ":8001" --lookupd-tcp-address=127.0.0.1:8200 --lookupd-tcp-address=127.0.0.1:7200 -data-path=./a

--lookupd-tcp-address指定するのに使用されるリスンアドレスを。nsqlookupdtcp

nsqdそして、nsqlookupd通信交換はとても簡単に図であります

nsqd接続を開始した後nsqlookupd、接続が成功すると、あなたは魔法の識別情報を送信したいnsq.MagicV1、この魔法はなぜ、もちろん、彼はただ、クライアントの情報通信バージョンを使用するとサーバーの両方を指定するために使用されない何を識別、いないバージョンが異なるアプローチを持っていますそれを容易にするために、後者は新しいバージョンのメッセージングを行うためです。
nsqlookupdコードブロック

func (p *tcpServer) Handle(clientConn net.Conn) {   
    // ...
    buf := make([]byte, 4)
    _, err := io.ReadFull(clientConn, buf)
    // ...
    protocolMagic := string(buf)
    // ...
    var prot protocol.Protocol
    switch protocolMagic {
    case "  V1":
        prot = &LookupProtocolV1{ctx: p.ctx}
    default:
        // ...
        return
    }
    err = prot.IOLoop(clientConn)
    //...
}

この時間はnsqd、すでにしているnsqlookupd良好な接続を確立したが、今回は、これらの接続のみ記述は成功です。
nsqlookupdまた、利用可能に追加されたリンクしなかったnsqdリストを。
接続が完了する確立した後、nsqdそれがお送りしますIDENTIFYのNSQ含まれている基本的な情報コマンド、
nsqdコードを

        ci := make(map[string]interface{})
        ci["version"] = version.Binary
        ci["tcp_port"] = n.RealTCPAddr().Port
        ci["http_port"] = n.RealHTTPAddr().Port
        ci["hostname"] = hostname
        ci["broadcast_address"] = n.getOpts().BroadcastAddress

        cmd, err := nsq.Identify(ci)
        if err != nil {
            lp.Close()
            return
        }
        resp, err := lp.Command(cmd)

含有nsqd提供tcphttpに送信ポート、ホスト名、バージョン等がnsqlookupdnsqlookupd受信したIDENTIFYコマンドを、その後、情報が分析に加えnsqd利用可能のリストにある
nsqlookupdコードブロック

func (p *LookupProtocolV1) IDENTIFY(client *ClientV1, reader *bufio.Reader, params []string) ([]byte, error) {
    var err error
    if client.peerInfo != nil {
        return nil, protocol.NewFatalClientErr(err, "E_INVALID", "cannot IDENTIFY again")
    }
    var bodyLen int32
    err = binary.Read(reader, binary.BigEndian, &bodyLen)
    // ...
    body := make([]byte, bodyLen)
    _, err = io.ReadFull(reader, body)
    // ...  
    peerInfo := PeerInfo{id: client.RemoteAddr().String()}
    err = json.Unmarshal(body, &peerInfo)
    // ...
    client.peerInfo = &peerInfo
    // 把nsqd的连接加入到可用列表里    
    if p.ctx.nsqlookupd.DB.AddProducer(Registration{"client", "", ""}, &Producer{peerInfo: client.peerInfo}) {
        p.ctx.nsqlookupd.logf(LOG_INFO, "DB: client(%s) REGISTER category:%s key:%s subkey:%s", client, "client", "", "")
    }
    // ...
    return response, nil
}

その後、15秒ごとに、送信PINGコマンドにハートビートをnsqlookupd、その実行可能な状態を維持するために、nsqlookupdそれが受信するたびに私を送っPINGたコマンドが書き留めますnsqd長期間にわたって更新されない場合は、このようなフィルタ条件として、最終更新時間を、それは問題のノードを考慮し、この情報には、使用可能なノードのリストに追加されません。
これまでのところ、nsqdに登録された自分の情報入れnsqlookupd可能なのリスト、我々はより開始することができますnsqdし、より多くのnsqlookupdために、nsqd
より多くを指定するnsqlookupdだけで書かれたように私の前のポストとして、

--lookupd-tcp-address=127.0.0.1:8200 --lookupd-tcp-address=127.0.0.1:7200

nsqdそして、すべてnsqlookupdの接続は、サービス登録情報を確立し、利用可能なアップデートのリストことを確実にするために、心拍数を維持しています。

取り扱いのnsqlookupdハング

私たちの上にいることを言ったnsqd問題が発生した場合、リストには、この接続情報の中に捨てために使用されます。以下のようにそれを行う方法をハングアップ現在のアプローチは、ということです、それは、ハートビートであるかどうか、またはその他のコマンドすべてがしますメッセージ、ときに送信発見し、問題が発生したとき、あなたはコマンドを送信するたびに、それは再接続していきますが。nsqlookupdnsqdnsqlookupd


nsqdnsqlookupnsqdnsqlookupd

func (lp *lookupPeer) Command(cmd *nsq.Command) ([]byte, error) {
    initialState := lp.state
    if lp.state != stateConnected {
        err := lp.Connect()
        if err != nil {
            return nil, err
        }
        lp.state = stateConnected
        _, err = lp.Write(nsq.MagicV1)
        if err != nil {
            lp.Close()
            return nil, err
        }
        if initialState == stateDisconnected {
            lp.connectCallback(lp)
        }
        if lp.state != stateConnected {
            return nil, fmt.Errorf("lookupPeer connectCallback() failed")
        }
    }
    // ...
}

接続に成功すると、それが再び呼び出されるconnectCallbackメソッド、IDENTIFYその上でコマンドを起動して。

クライアントとnsqlookupd、nsqd通信実装

前の記事紹介で、とクライアントを接続する方法をnsqlookupd通信します

    adds := []string{"127.0.0.1:7201", "127.0.0.1:8201"}
    config := nsq.NewConfig()
    config.MaxInFlight = 1000
    config.MaxBackoffDuration = 5 * time.Second
    config.DialTimeout = 10 * time.Second

    topicName := "testTopic1"
    c, _ := nsq.NewConsumer(topicName, "ch1", config)
    testHandler := &MyTestHandler{consumer: c}

    c.AddHandler(testHandler)
    if err := c.ConnectToNSQLookupds(adds); err != nil {
        panic(err)
    }

それに注意すべきaddsポートのアドレスでは、ポートIはまた、マップ上のポストを使用してあなたの詳細な分析与える呼び出し方法を、彼の訪問を達成することであるHTTPポートを提供する得るためにサブスクリプションをすべてのノード情報、データはURLで返されます次のような情報があります。nsqlookupdhttp


c.ConnectToNSQLookupds(adds)nsqlookupdhttp://127.0.0.1:7201/lookup?topic=testTopic1consumertopicproducers

{
  "channels": [
    "nsq_to_file",
    "ch1"
  ],
  "producers": [
    {
      "remote_address": "127.0.0.1:58606",
      "hostname": "li-peng-mc-macbook.local",
      "broadcast_address": "li-peng-mc-macbook.local",
      "tcp_port": 8000,
      "http_port": 8001,
      "version": "1.1.1-alpha"
    },
    {
      "remote_address": "127.0.0.1:58627",
      "hostname": "li-peng-mc-macbook.local",
      "broadcast_address": "li-peng-mc-macbook.local",
      "tcp_port": 7000,
      "http_port": 7001,
      "version": "1.1.1-alpha"
    }
  ]
}


方法queryLookupdの動作は、図上で実行されます。

  • 申し出受け取るために購読リストをtopicnsqd
  • 接続
func (r *Consumer) queryLookupd() {
    retries := 0
retry:
    endpoint := r.nextLookupdEndpoint()

    // ...  
    err := apiRequestNegotiateV1("GET", endpoint, nil, &data)
    if err != nil {
        // ...
    }
    var nsqdAddrs []string
    for _, producer := range data.Producers {
        broadcastAddress := producer.BroadcastAddress
        port := producer.TCPPort
        joined := net.JoinHostPort(broadcastAddress, strconv.Itoa(port))
        nsqdAddrs = append(nsqdAddrs, joined)
    }
    // 进行连接
    for _, addr := range nsqdAddrs {
        err = r.ConnectToNSQD(addr)
        if err != nil && err != ErrAlreadyConnected {
            r.log(LogLevelError, "(%s) error connecting to nsqd - %s", addr, err)
            continue
        }
    }
}

利用可能nsqdのリストを更新する方法

新nsqdは、追加それに対処する方法ですか?
コールではConnectToNSQLookupdそれがコルーチンを開始しますgo r.lookupdLoop()メソッドを呼び出すlookupdLoopタイミングループのアクセスのをqueryLookupd更新nsqd可能なのリストを

// poll all known lookup servers every LookupdPollInterval
func (r *Consumer) lookupdLoop() {
    // ...
    var ticker *time.Ticker
    select {
    case <-time.After(jitter):
    case <-r.exitChan:
        goto exit
    }
    // 设置Interval 来循环访问 queryLookupd
    ticker = time.NewTicker(r.config.LookupdPollInterval)
    for {
        select {
        case <-ticker.C:
            r.queryLookupd()
        case <-r.lookupdRecheckChan:
            r.queryLookupd()
        case <-r.exitChan:
            goto exit
        }
    }

exit:
    // ...
}

単一障害点nsqdプロセス


ある場合nsqd、障害が発生行う方法?現在のアプローチがあります

  • nsqdlookupdこれは、使用可能なインターフェイスのリストから受信したクライアントが常に利用可能で、利用可能なリストから、障害が発生したノードを削除します。
  • クライアント障害が発生したノードが使用可能なノードから削除し、その後の使用に行くかどうかを決定することになるnsqlookup、作られて接続し、そうであればcase r.lookupdRecheckChan <- 1可能なのリストを更新するためにqueryLookupd、故障ならば、その後、タイミングはコルーチンは、接続が行われ、再試行を開始しない場合は、回復は、接続が成功すると、利用可能なリストに再度追加されます。
    コードのクライアントの実装
func (r *Consumer) onConnClose(c *Conn) {
    // ...
    // remove this connections RDY count from the consumer's total
    delete(r.connections, c.String())
    left := len(r.connections)
    // ...
    r.mtx.RLock()
    numLookupd := len(r.lookupdHTTPAddrs)
    reconnect := indexOf(c.String(), r.nsqdTCPAddrs) >= 0
    // 如果使用的是nslookup则去刷新可用列表
    if numLookupd > 0 {
        // trigger a poll of the lookupd
        select {
        case r.lookupdRecheckChan <- 1:
        default:
        }
    } else if reconnect {
        // ... 
        }(c.String())
    }
}

おすすめ

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