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. 一个div在另一个div中水平垂直的方法

    html <div id="main"> <div id="box"> 一个div在另一个div中垂直居中实现方法 </div&g ...

  2. SQLSERVER 执行过的语句查询

    total_worker_time AS [总消耗CPU 时间(ms)], execution_count [运行次数], qs.total_worker_time AS [平均消耗CPU 时间(ms ...

  3. ASP.NET Core 使用UrlFirewall对请求进行过滤

    一. 前言 UrlFirewall 是一个开源.轻便的对http请求进行过滤的中间件,可使用在webapi或者网关(比如Ocelot),由我本人编写,并且开源在github:https://githu ...

  4. RHEL7.0 Docker离线安装以及实战笔记

    1.概述 最近在琢磨一个事--在RHEL 7.0系统上离线安装使用Docker.然后配置JAVAEE环境,发布Web服务.在网上查了资料,大多数是在线安装的,其他的要么是环境不同,要么资料包找不到了. ...

  5. 继续死磕SDRAM控制器

    SDRAM控制器 博主上一篇介绍了一些SDRAM的基本原理是否有必要学习使用纯Verilog写一个SDRAM控制器,接下来记录SDRAM控制器的工作原理.首先是上电初始化. 上电初始化 时序图中,tR ...

  6. 通过jQuery源码学习javascript(三)

    承接上两篇继续写下去.我尽量把我明白的地方给大家说清楚.有些大家的提问我也有点搞不明白,如果有人能解答,再好不过了 疑问  第一篇中有位博友提出了以下的问题,我也不太明白,如果有明白的,能否告知一.二 ...

  7. Ajax+jquery+jaso传输数据

    所谓的Ajax.这里我们讲二种方法  方法一:(微软有自带Ajax框架)  在Asp.net里微软有自己的Ajax框架.就是在页面后台.cs文件里引入 using System.Web.Service ...

  8. 升级Centos 7/6内核版本到4.12.4的方法

    一.查看那系统内核版本 二.升级内核 三.修改grub中默认的内核版本 四.重启系统并查看系统内核 公司打算上Docker服务,目前需要安装运行环境,Docker新的功能除了需要Centos 7系统之 ...

  9. Day7 类的继承和继承实现的原理

    继承可以分为但继承,多继承. 继承的基本形式 class ParentClass1(object): #定义父类 pass class ParentClass2: #定义父类 pass class S ...

  10. git 使用简易指南