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. “万能数据库查询分析器” 5.03发布,访问EXCEL将自动为表名前后加上中括弧

        "万能数据库查询分析器" 5.03发布,访问EXCEL将自动为表名前后加上中括弧 1          引言    中国本土程序员马根峰推出的个人作品----万能数据库查询 ...

  2. Linux的eth0,eth1,eth2,lo详解

    eth0,eth1,eth2……代表网卡一,网卡二,网卡三……lo代表127.0.0.1,即localhost 参考:Linux命令:ifconfig 功能说明:显示或设置网络设备 语 法:ifcon ...

  3. Memcache 运行情况

    Memcache Memcache是danga.com的一个开源项目,它是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的Hash表,能够用来存储各种格式的数据. 查看当前的me ...

  4. 关于mysql保存数据的时候报问题分析  普通的字符串或者表情都是占位3个字节,所以utf8足够用了,但是移动端的表情符号占位是4个字节,普通的utf8就不够用了,为了应对无线互联网的机遇和挑战、避免 emoji 表情符号带来的问题、涉及无线相关的 MySQL 数据库建议都提前采用 utstring value:'\xF0\x9F\x98\x82\xF0\x9F...' for ...

    问题分析 普通的字符串或者表情都是占位3个字节,所以utf8足够用了,但是移动端的表情符号占位是4个字节,普通的utf8就不够用了,为了应对无线互联网的机遇和挑战.避免 emoji 表情符号带来的问题 ...

  5. FFPLAY的原理(七)

    同步音频 现在我们已经有了一个比较像样的播放器.所以让我们看一下还有哪些零碎的东西没处理.上次,我们掩饰了一点同步问题,也就是同步音频到视频而不是其它的同 步方式.我们将采用和视频一样的方式:做一个内 ...

  6. Open Source BI Platform List

    资源入口: awesome-business-intelligence https://github.com/thenaturalist/awesome-business-intelligence h ...

  7. 浅谈 RxAndroid + Retrofit + Databinding

    http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0131/3930.html 最近 RxAndroid .MVP.MVVM 一直是 And ...

  8. DDGScreenShot—图片擦除功能

    写在前面 图片擦除功能,也是运用图片的绘制功能, 将图片绘制后,拿到相应的图片.当然,有一涨底图更明显 实现代码如下 /** ** 用手势擦除图片 - imageView --传图片 - bgView ...

  9. 分布式爬虫框架XXL-CRAWLER

    <分布式爬虫框架XXL-CRAWLER> 一.简介 1.1 概述 XXL-CRAWLER 是一个分布式爬虫框架.一行代码开发一个分布式爬虫,拥有"多线程.异步.IP动态代理.分布 ...

  10. 洛谷 P2764 解题报告

    P2764 最小路径覆盖问题 问题描述: 给定有向图\(G=(V,E)\).设\(P\) 是\(G\) 的一个简单路(顶点不相交)的集合.如果\(V\) 中每个顶点恰好在\(P\) 的一条路上,则称\ ...