前回のポストで分析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
指定するのに使用されるリスンアドレスを。nsqlookupd
tcp
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
提供tcp
とhttp
に送信ポート、ホスト名、バージョン等がnsqlookupd
、nsqlookupd
受信した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
問題が発生した場合、リストには、この接続情報の中に捨てために使用されます。以下のようにそれを行う方法をハングアップ現在のアプローチは、ということです、それは、ハートビートであるかどうか、またはその他のコマンドすべてがしますメッセージ、ときに送信発見し、問題が発生したとき、あなたはコマンドを送信するたびに、それは再接続していきますが。nsqlookupd
nsqd
nsqlookupd
nsqd
nsqlookup
nsqd
nsqlookupd
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で返されます次のような情報があります。nsqlookupd
http
c.ConnectToNSQLookupds(adds)
nsqlookupd
http://127.0.0.1:7201/lookup?topic=testTopic1
consumer
topic
producers
{
"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
の動作は、図上で実行されます。
- 申し出受け取るために購読リストを
topic
nsqd
- 接続
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())
}
}