golang源码阅读-nsq-生产者代码

1.基本使用

var producer *nsq.Producer

// 主函数
func main() {
	strIP1 := "127.0.0.1:4150"
	InitProducer(strIP1)

	running := true

	//读取控制台输入
	reader := bufio.NewReader(os.Stdin)

	for running {
		data, _, _ := reader.ReadLine()
		command := string(data)
		if command == "stop" {
			running = false
		}

		err := Publish("test", command)
		fmt.Println(err)
	}
}

// 初始化生产者
func InitProducer(str string) {
	var err error
	fmt.Println("address: ", str)
     //生产者实例
	producer, err = nsq.NewProducer(str, nsq.NewConfig())
	if err != nil {
		panic(err)
	}
}

//发布消息
func Publish(topic string, message string) error {
	var err error
	if producer != nil {
		if message == "" { //不能发布空串,否则会导致error
			return nil
		}
		err = producer.Publish(topic, []byte(message)) // 发布消息
		return err
	}
	return fmt.Errorf("producer is nil", err)
}

2. 初始化config-nsq.NewConfig()

func NewConfig() *Config {
	//nsq 配置
	c := &Config{
        //structTagsConfig 和 tlsConfig 用来处理Config中的字段
		configHandlers: []configHandler{&structTagsConfig{}, &tlsConfig{}},
		initialized:    true,
	}
    //分别调用structTagsConfig和tlsConfig 对config进行字段的验证,字段的默认值设置
	if err := c.setDefaults(); err != nil {
		panic(err.Error())
	}
	return c
}

3.生产者初始化-nsq.NewProducer

func NewProducer(addr string, config *Config) (*Producer, error) { 
    //判断config是否初始化过
	config.assertInitialized()
    //对config内的字段进行验证
    //分别调用 structTagsConfig 和 tlsConfig 的Validate方法进行验证
	err := config.Validate()
	if err != nil {
		return nil, err
	}

    //初始化结构体
	p := &Producer{
		id: atomic.AddInt64(&instCount, 1),

		addr:   addr,
		config: *config,

		logger: log.New(os.Stderr, "", log.Flags()),
		logLvl: LogLevelInfo,

		transactionChan: make(chan *ProducerTransaction),
		exitChan:        make(chan int),
		responseChan:    make(chan []byte),
		errorChan:       make(chan []byte),
	}
	return p, nil
}

4. 消息发布- producer.Publish(topic, []byte(message))

func (w *Producer) Publish(topic string, body []byte) error {
    //Publish 返回Command结构体
	return w.sendCommand(Publish(topic, body))
}

5. 发送命令-sendCommand

func (w *Producer) sendCommand(cmd *Command) error {
	doneChan := make(chan *ProducerTransaction)
    //发送命令
	err := w.sendCommandAsync(cmd, doneChan, nil)
	if err != nil {
		close(doneChan)
		return err
	}
   //阻塞管道 执行发送结束
	t := <-doneChan
	return t.Error
}

6. 同步发送命令-sendCommandAsync

func (w *Producer) sendCommandAsync(cmd *Command, doneChan chan *ProducerTransaction,
	args []interface{}) error {
	//生产者数量加1
	atomic.AddInt32(&w.concurrentProducers, 1)
	defer atomic.AddInt32(&w.concurrentProducers, -1)

    //判断状态 如果没有连接 则进行连接
	if atomic.LoadInt32(&w.state) != StateConnected {
		err := w.connect()
		if err != nil {
			return err
		}
	}

	t := &ProducerTransaction{
		cmd:      cmd,
		doneChan: doneChan,
		Args:     args,
	}

	select {
    //通过管道将命令发送给nsq
	case w.transactionChan <- t:
	case <-w.exitChan:
		return ErrStopped
	}

	return nil
}

7.连接-connect

func (w *Producer) connect() error {
//加锁
	w.guard.Lock()
	defer w.guard.Unlock()

    //判断标志
	if atomic.LoadInt32(&w.stopFlag) == 1 {
		return ErrStopped
	}

    //再次判断状态
	switch state := atomic.LoadInt32(&w.state); state {
	case StateInit:
	case StateConnected:
		return nil
	default:
		return ErrNotConnected
	}

	w.log(LogLevelInfo, "(%s) connecting to nsqd", w.addr)

	logger, logLvl := w.getLogger()
    
    //生成连接 直接返回了 Conn结构体
    //producerConnDelegate 各种钩子的具体实现
	w.conn = NewConn(w.addr, &w.config, &producerConnDelegate{w})
	w.conn.SetLogger(logger, logLvl, fmt.Sprintf("%3d (%%s)", w.id))
       
   //进行连接
	_, err := w.conn.Connect()
	if err != nil {
		w.conn.Close()
		w.log(LogLevelError, "(%s) error connecting to nsqd - %s", w.addr, err)
		return err
	}
	atomic.StoreInt32(&w.state, StateConnected)
	w.closeChan = make(chan int)
	w.wg.Add(1)
    //处理各个管道接收到的数据
	go w.router()

	return nil
}

8.进行连接-Connect

func (c *Conn) Connect() (*IdentifyResponse, error) {
	dialer := &net.Dialer{
		LocalAddr: c.config.LocalAddr,
		Timeout:   c.config.DialTimeout,
	}
    //tcp连接
	conn, err := dialer.Dial("tcp", c.addr)
	if err != nil {
		return nil, err
	}
	c.conn = conn.(*net.TCPConn)
	c.r = conn
	c.w = conn
    //V2 写入
	_, err = c.Write(MagicV2)
	if err != nil {
		c.Close()
		return nil, fmt.Errorf("[%s] failed to write magic - %s", c.addr, err)
	}
    //进行鉴定
	resp, err := c.identify()
	if err != nil {
		return nil, err
	}

	if resp != nil && resp.AuthRequired {
		if c.config.AuthSecret == "" {
			c.log(LogLevelError, "Auth Required")
			return nil, errors.New("Auth Required")
		}
		err := c.auth(c.config.AuthSecret)
		if err != nil {
			c.log(LogLevelError, "Auth Failed %s", err)
			return nil, err
		}
	}

	c.wg.Add(2)
	atomic.StoreInt32(&c.readLoopRunning, 1)
    //负责 读相关工作 如一些心跳检测的响应 接收到不同消息 会触发钩子函数
	go c.readLoop()
    //负责写 将cmdChan管道内的命令写入nsq
	go c.writeLoop()
	return resp, nil
}

9. 命令发送-router

func (w *Producer) router() {
	for {
		select {
		case t := <-w.transactionChan:
			w.transactions = append(w.transactions, t)
             //读取管道信息写入nsq
			err := w.conn.WriteCommand(t.cmd)
			if err != nil {
				w.log(LogLevelError, "(%s) sending command - %s", w.conn.String(), err)
				w.close()
			}
		case data := <-w.responseChan:
			w.popTransaction(FrameTypeResponse, data)
		case data := <-w.errorChan:
			w.popTransaction(FrameTypeError, data)
		case <-w.closeChan:
			goto exit
		case <-w.exitChan:
			goto exit
		}
	}

exit:
	w.transactionCleanup()
	w.wg.Done()
	w.log(LogLevelInfo, "exiting router")
}

10. 真正写入命令-WriteCommand

func (c *Conn) WriteCommand(cmd *Command) error {
	c.mtx.Lock()
    
    //调用Command的WriteTo方法 进行写
	_, err := cmd.WriteTo(c)
	if err != nil {
		goto exit
	}
//有缓冲
	err = c.Flush()

exit:
	c.mtx.Unlock()
	if err != nil {
		c.log(LogLevelError, "IO error - %s", err)
		c.delegate.OnIOError(c, err)
	}
	return err
}

11.写-WriteTo

func (c *Command) WriteTo(w io.Writer) (int64, error) {
	var total int64
	var buf [4]byte
    //链接写 
   //写入命令的类型 eg:PUB
	n, err := w.Write(c.Name)
	total += int64(n)
	if err != nil {
		return total, err
	}
     //遍历topic
	for _, param := range c.Params {
    //先写入空格
		n, err := w.Write(byteSpace)
		total += int64(n)
		if err != nil {
			return total, err
		}
        //写入topic
		n, err = w.Write(param)
		total += int64(n)
		if err != nil {
			return total, err
		}
	}
    //写入 \n
	n, err = w.Write(byteNewLine)
	total += int64(n)
	if err != nil {
		return total, err
	}
    //写入具体内容
	if c.Body != nil {
		bufs := buf[:]
		binary.BigEndian.PutUint32(bufs, uint32(len(c.Body)))
      //吸入长度
		n, err := w.Write(bufs)
		total += int64(n)
		if err != nil {
			return total, err
		}
        //写入内容
		n, err = w.Write(c.Body)
		total += int64(n)
		if err != nil {
			return total, err
		}
	}
   //返回总长度
	return total, nil
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值