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. 我的sql数据库存储过程分页- -

    以前用到数据库存储过程分页的时候都是用 not in 但是最近工作的时候,随着数据库记录的不断增大,发现not in的效率 真的不行 虽然都设置了索引,但是当记录达到10w的时候就发现不行了,都是需要 ...

  2. Oracle面试过程中常见的二十个问题

    1.冷备份和热备份的不同点以及各自的优点  解答:热备份针对归档模式的数据库,在数据库仍旧处于工作状态时进行备份.而冷备份指在数据库关闭后,进行备份,适用于所有模式的数据库.热备份的优点在于当备份时, ...

  3. VS2017安装包不占用C盘空间的方法,亲试

    问题:普通VS2017的安装方式,不论是在线安装还是下载的离线安装包,都会在安装过程中将安装包保存在C:\ProgramData\Microsoft\VisualStudio\Packages文件夹下 ...

  4. 使用swagger管理接口

    swagger 配置 1.pom 增加jar包依赖 <dependency> <groupId>io.springfox</groupId> <artifac ...

  5. GitHub awesome Resource

    各种Awesome技术资源的资源聚合: https://github.com/sindresorhus/awesome Contents Platforms Programming Languages ...

  6. 当今商业中使用的三种十分重要的IT应用系统

    本文为读书笔记,其中内容摘自<信息时代的管理信息系统>第八版第二章 当今商业中使用的三种十分重要的IT应用系统: 供应链管理(SCM) 客户关系管理(CRM) 电子协同(e-collabo ...

  7. clear read-only status问题的解决

    IDEA系工具可能会报出的错误. 解决方法见官方文档吧:Changing Read-Only Status of Files  : https://www.jetbrains.com/help/ide ...

  8. hadoop环境运行程序出现 Retrying connect to server 问题

    程序运行时出现如下问题: 从网上查资料,有说重启format的..有说/etc/hosts出问题的... 反正都试了一遍..还是有这个问题 后来看日志,发现问题是访问服务器9001端口访问不到..开始 ...

  9. 纯CSS炫酷的3D旋转

    <html><head><meta charset="utf-8"><title>纯CSS炫酷的3D旋转</title> ...

  10. Pygame常用方法

    '''import pygame# 初始化pygame库,让计算机硬件准备pygame.init()# ----------窗口相关操作-----------# 创建窗口window = pygame ...