package nsqd

import (
    "encoding/binary"
    "fmt"
    "io"
    "net"
    "time"

    "github.com/nsqio/go-nsq"
)

// lookupPeer is a low-level type for connecting/reading/writing to nsqlookupd
//
// A lookupPeer instance is designed to connect lazily to nsqlookupd and reconnect
// gracefully (i.e. it is all handled by the library).  Clients can simply use the
// Command interface to perform a round-trip.
type lookupPeer struct {
    l               Logger
    addr            string
    conn            net.Conn
    state           int32
    connectCallback func(*lookupPeer)
    maxBodySize     int64
    Info            peerInfo
}

// peerInfo contains metadata for a lookupPeer instance (and is JSON marshalable)
type peerInfo struct {
    TCPPort          int    `json:"tcp_port"`
    HTTPPort         int    `json:"http_port"`
    Version          string `json:"version"`
    BroadcastAddress string `json:"broadcast_address"`
}

// newLookupPeer creates a new lookupPeer instance connecting to the supplied address.
//
// The supplied connectCallback will be called *every* time the instance connects.
func newLookupPeer(addr string, maxBodySize int64, l Logger, connectCallback func(*lookupPeer)) *lookupPeer {
    return &lookupPeer{
        l:               l,
        addr:            addr,
        state:           stateDisconnected,
        maxBodySize:     maxBodySize,
        connectCallback: connectCallback,
    }
}

// Connect will Dial the specified address, with timeouts
func (lp *lookupPeer) Connect() error {
    lp.l.Output(2, fmt.Sprintf("LOOKUP connecting to %s", lp.addr))
    conn, err := net.DialTimeout("tcp", lp.addr, time.Second)
    if err != nil {
        return err
    }
    lp.conn = conn
    return nil
}

// String returns the specified address
func (lp *lookupPeer) String() string {
    return lp.addr
}

// Read implements the io.Reader interface, adding deadlines
func (lp *lookupPeer) Read(data []byte) (int, error) {
    lp.conn.SetReadDeadline(time.Now().Add(time.Second))
    return lp.conn.Read(data)
}

// Write implements the io.Writer interface, adding deadlines
func (lp *lookupPeer) Write(data []byte) (int, error) {
    lp.conn.SetWriteDeadline(time.Now().Add(time.Second))
    return lp.conn.Write(data)
}

// Close implements the io.Closer interface
func (lp *lookupPeer) Close() error {
    lp.state = stateDisconnected
    if lp.conn != nil {
        return lp.conn.Close()
    }
    return nil
}

// Command performs a round-trip for the specified Command.
//
// It will lazily connect to nsqlookupd and gracefully handle
// reconnecting in the event of a failure.
//
// It returns the response from nsqlookupd as []byte
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
        lp.Write(nsq.MagicV1)
        if initialState == stateDisconnected {
            lp.connectCallback(lp)
        }
    }
    if cmd == nil {
        return nil, nil
    }
    _, err := cmd.WriteTo(lp)
    if err != nil {
        lp.Close()
        return nil, err
    }
    resp, err := readResponseBounded(lp, lp.maxBodySize)
    if err != nil {
        lp.Close()
        return nil, err
    }
    return resp, nil
}

func readResponseBounded(r io.Reader, limit int64) ([]byte, error) {
    var msgSize int32

    // message size
    err := binary.Read(r, binary.BigEndian, &msgSize)
    if err != nil {
        return nil, err
    }

    if int64(msgSize) > limit {
        return nil, fmt.Errorf("response body size (%d) is greater than limit (%d)",
            msgSize, limit)
    }

    // message binary data
    buf := make([]byte, msgSize)
    _, err = io.ReadFull(r, buf)
    if err != nil {
        return nil, err
    }

    return buf, nil
}

lookup_peer.go的更多相关文章

随机推荐

  1. 深入源码解析类Route

    微软官网对这个类的说明是:提供用于定义路由及获取路由相关信息的属性和方法.这个说明已经很简要的说明了这个类的作用,下面我们就从源码的角度来看看这个类的内部是如何工作的. public class Ro ...

  2. How--to-deploy-smart-contracts-on

    The following smart contract code is only an example and is NOT to be used in Production systems. pr ...

  3. C++笔记018:构造函数的调用规则

      原创笔记,转载请注明出处! 点击[关注],关注也是一种美德~ 一.默认构造函数 两个特殊的构造函数 1.默认无参构造函数 当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空 ...

  4. index() checkbox单选问题

    index() 只对兄弟节点有用 如果这种结构要选择checkbox 时用prop附加属性 removeAttr清楚属性 $('.checkbox').prop('checked',true) $(' ...

  5. Day8 面向对象反射 item方法 打印对象信息__str__ 构析方法__del__ 程序的异常处理

    反射:通过字符串来访问到所对应的值(反射到真实的属性上). eg: class Foo: x=1 def __init__(self,name): self.name=name def f1(self ...

  6. linux/unix解压缩

    转自:http://blog.sina.com.cn/s/blog_6f2d29af01015ac6.html zip: 压缩: zip [-AcdDfFghjJKlLmoqrSTuvVwXyz$][ ...

  7. Java自学?Java编程资源大放送

    黑马程序员 北京JavaEE就业班32期教程视频+源码+资料 链接: https://pan.baidu.com/s/1VCXyNVD-LvlZyReVgzKXGg 密码:cike 黑马:Java基础 ...

  8. web.config中的configSource

    在大型项目中,可能存在第三方类库的配置如:log4.net,AOP框架Unity,WCF等,或是自定义的配置,造成web.config内容过多,不易维护,影响Config初始化. 这时我们可以使用co ...

  9. tensorflow1.0.0 弃用了几个operator写法

    除法和取模运算符(/, //, %)现已匹配 Python(flooring)语义.这也适用于 tf.div 和 tf.mod.为了获取强制的基于整数截断的行为,你可以使用 tf.truncatedi ...

  10. [Python学习之路] 猜大小游戏

    # coding =utf-8 import random def roll_dice(number=3, points=None): if points == None: points = [] w ...