nsq-nsqd

一般nsqd是部署在靠近client的,甚至是本机,用于收集client的pub信息

nsqd会吧topic和channel信息持久化,并同步到nsqlookup


入口代码位于github.com/nsqio/nsq/apps/nsqd.go

func (p *program) Start() error {

                ...

                nsqd := nsqd.New(opts)

                加载旧的metadata(topic,channnel)

                err := nsqd.LoadMetadata()

                过滤ephemeral的topic和channel后进行持久化

                err = nsqd.PersistMetadata()

                启动nsqd

                nsqd.Main()

            ...

}

此处我们只看tcpserver(它用于client创建pub,sub等)

func (n *NSQD) Main() {

                ...

                tcpListener, err := net.Listen("tcp", n.getOpts().TCPAddress)

                tcpServer := &tcpServer{ctx: ctx}

                protocol.TCPServer(n.tcpListener, tcpServer, n.logf)

                ...        

                用于同步本nsqd的topic,channel到nsqlookupd

                n.waitGroup.Wrap(func() { n.lookupLoop() })

                ...

}



func (p *tcpServer) Handle(clientConn net.Conn) {

                ...

                 var prot protocol.Protocol

                ...

                 prot = &protocolV2{ctx: p.ctx}

                进入读取命令

                err = prot.IOLoop(clientConn)

                ...

}


func (p *protocolV2) IOLoop(conn net.Conn) error {

                ...

                line, err = client.Reader.ReadSlice('\n')

                params := bytes.Split(line, separatorBytes)

                进入命令解析 

               response, err = p.Exec(client, params)

                ...

}



func (p *protocolV2) Exec(client *clientV2, params [][]byte) ([]byte, error) {

                ...    

                case bytes.Equal(params[0], []byte("PUB")):

                                进入发布命令

                                 return p.PUB(client, params)

...

}


func (p *protocolV2) PUB(client *clientV2, params [][]byte) ([]byte, error) {

                ...

                获取topic

                topic := p.ctx.nsqd.GetTopic(topicName)

                创建消息

                msg := NewMessage(topic.GenerateID(), messageBody)

                持久化消息

                err = topic.PutMessage(msg)

                ...

}


func (n *NSQD) GetTopic(topicName string) *Topic {

                ...

                新建topic

                 t = NewTopic(topicName, &context{n}, deleteCallback)

                从nsqlookup查询所有此topic下的channel

                channelNames, err := n.ci.GetLookupdTopicChannels(t.name, lookupdHTTPAddrs)

                 for _, channelName := range channelNames {

                            if strings.HasSuffix(channelName, "#ephemeral") {    

                            // we don't want to pre-create ephemeral channels

                            // because there isn't a client connected

                           continue

                    }

                ...

}


func NewTopic(topicName string, ctx *context, deleteCallback func(*Topic)) *Topic {

                ...

                用于持久化    

                t.backend = diskqueue.New(

                ...)

                ...

                通知创建topic成功

                t.ctx.nsqd.Notify(t)

                ...

}

func (n *NSQD) Notify(v interface{}) {

                ...

                case n.notifyChan <- v:

                ...

                     if !persist {

                         return

                   }

                    n.Lock()

                    err := n.PersistMetadata()

                    持久化topic

                    ...        

}



func (t *Topic) PutMessage(m *Message) error {

                ...

                err := t.put(m)

                ...

}


func (t *Topic) put(m *Message) error {

                ...    

                    err := writeMessageToBackend(b, m, t.backend)

                    ...

}


 func writeMessageToBackend(buf *bytes.Buffer, msg *Message, bq BackendQueue) error {

                 buf.Reset()

                 _, err := msg.WriteTo(buf)

                    if err != nil {

                         return err

                     }

                return bq.Put(buf.Bytes())

 }


 func (d *diskQueue) Put(data []byte) error {

     d.RLock()

     defer d.RUnlock()


     if d.exitFlag == 1 {

         return errors.New("exiting")

     }


     d.writeChan <- data

     return <-d.writeResponseChan

 }


func (d *diskQueue) ioLoop() {

...

 case dataWrite := <-d.writeChan:

              真正的持久化入口

             d.writeResponseChan <- d.writeOne(dataWrite)

...

}


func (n *NSQD) lookupLoop() {

...

                通过此方法来更新nsqlookupd重连后nsqlookupd的topic,channel信息

                 lookupPeer := newLookupPeer(host, n.getOpts().MaxBodySize, n.logf,

                    connectCallback(n, hostname, syncTopicChan))

...

select {

        定期ping nsqlookupd

         case <-ticker:

            for _, lookupPeer := range lookupPeers {

                n.logf(LOG_DEBUG, "LOOKUPD(%s): sending heartbeat", lookupPeer)

                 cmd := nsq.Ping()

                 _, err := lookupPeer.Command(cmd)

                 if err != nil {

                     n.logf(LOG_ERROR, "LOOKUPD(%s): %s - %s", lookupPeer, cmd, err)

                 }


                ...

                同步topic到nsqlookup

                   case val := <-n.notifyChan:

                switch val.(type) {

                ...

                case *Topic:

                ...

                topic := val.(*Topic)

                 if topic.Exiting() == true {

                     cmd = nsq.UnRegister(topic.name, "")

                } else {

                     cmd = nsq.Register(topic.name, "")

                 }

             }


            for _, lookupPeer := range lookupPeers {


                _, err := lookupPeer.Command(cmd)


...

}

你可能感兴趣的:(nsq-nsqd)