package clientv3

import (
    "net/url"
    "strings"
    "sync"

    "golang.org/x/net/context"
    "google.golang.org/grpc"
    "google.golang.org/grpc/codes"
)

// ErrNoAddrAvilable is returned by Get() when the balancer does not have
// any active connection to endpoints at the time.
// This error is returned only when opts.BlockingWait is true.
var ErrNoAddrAvilable = grpc.Errorf(codes.Unavailable, "there is no address available")

// simpleBalancer does the bare minimum to expose multiple eps
// to the grpc reconnection code path
//简单均衡器
type simpleBalancer struct {
    // addrs are the client's endpoints for grpc
    addrs []grpc.Address  //nsqd 地址列表
    // notifyCh notifies grpc of the set of addresses for connecting
    notifyCh chan []grpc.Address  //链接通知地址

    // readyc closes once the first connection is up
//一旦连接上 ,就关闭链接  并且 只执行一次
    readyc    chan struct{}
    readyOnce sync.Once

    // mu protects upEps, pinAddr, and connectingAddr
    //锁  保护 upEps, pinAddr, and connectingAddr 的锁
    mu sync.RWMutex
    // upEps holds the current endpoints that have an active connection
//已经存活的链接地址
    upEps map[string]struct{}
    // upc closes when upEps transitions from empty to non-zero or the balancer closes.
//当upEps为空 转化为非空 或者均衡器关闭的时候 ,关闭此通道
    upc chan struct{}

    // grpc issues TLS cert checks using the string passed into dial so
    // that string must be the host. To recover the full scheme://host URL,
    // have a map from hosts to the original endpoint.
//host 到 endpoint 映射 map集合
    host2ep map[string]string

    // pinAddr is the currently pinned address; set to the empty string on
    // intialization and shutdown.
//当前固定的地址。当此变量被初始化或者关闭的时候  设置为空
    pinAddr string
//是否关闭的标志
    closed bool
}
//创建负载均衡器  
func newSimpleBalancer(eps []string) *simpleBalancer {
    notifyCh := make(chan []grpc.Address, 1)
    addrs := make([]grpc.Address, len(eps))
    for i := range eps {
        addrs[i].Addr = getHost(eps[i])
    }
    notifyCh <- addrs
    sb := &simpleBalancer{
        addrs:    addrs,
        notifyCh: notifyCh,
        readyc:   make(chan struct{}),
        upEps:    make(map[string]struct{}),
        upc:      make(chan struct{}),
        host2ep:  getHost2ep(eps),
    }
    return sb
}
//启动
func (b *simpleBalancer) Start(target string, config grpc.BalancerConfig) error { return nil }
//链接通知
func (b *simpleBalancer) ConnectNotify() <-chan struct{} {
    b.mu.Lock()
    defer b.mu.Unlock()
    return b.upc
}
//通过主机 转化为地址
func (b *simpleBalancer) getEndpoint(host string) string {
    b.mu.Lock()
    defer b.mu.Unlock()
    return b.host2ep[host]
}
//同上相反
func getHost2ep(eps []string) map[string]string {
    hm := make(map[string]string, len(eps))
    for i := range eps {
        _, host, _ := parseEndpoint(eps[i])
        hm[host] = eps[i]
    }
    return hm
}
//更新地址列表
func (b *simpleBalancer) updateAddrs(eps []string) {
    np := getHost2ep(eps)

    b.mu.Lock()
    defer b.mu.Unlock()

    match := len(np) == len(b.host2ep)
    for k, v := range np {
        if b.host2ep[k] != v {
            match = false
            break
        }
    }
    if match {
        // same endpoints, so no need to update address
        return
    }

    b.host2ep = np

    addrs := make([]grpc.Address, 0, len(eps))
    for i := range eps {
        addrs = append(addrs, grpc.Address{Addr: getHost(eps[i])})
    }
    b.addrs = addrs
    b.notifyCh <- addrs
}
//运行中的机器
func (b *simpleBalancer) Up(addr grpc.Address) func(error) {
    b.mu.Lock()
    defer b.mu.Unlock()

    // gRPC might call Up after it called Close. We add this check
    // to "fix" it up at application layer. Or our simplerBalancer
    // might panic since b.upc is closed.
    if b.closed {
        return func(err error) {}
    }

    if len(b.upEps) == 0 {
        // notify waiting Get()s and pin first connected address
        close(b.upc)
        b.pinAddr = addr.Addr
    }
    b.upEps[addr.Addr] = struct{}{}

    // notify client that a connection is up
    b.readyOnce.Do(func() { close(b.readyc) })

    return func(err error) {
        b.mu.Lock()
        delete(b.upEps, addr.Addr)
        if len(b.upEps) == 0 && b.pinAddr != "" {
            b.upc = make(chan struct{})
        } else if b.pinAddr == addr.Addr {
            // choose new random up endpoint
            for k := range b.upEps {
                b.pinAddr = k
                break
            }
        }
        b.mu.Unlock()
    }
}

func (b *simpleBalancer) Get(ctx context.Context, opts grpc.BalancerGetOptions) (grpc.Address, func(), error) {
    var addr string

    // If opts.BlockingWait is false (for fail-fast RPCs), it should return
    // an address it has notified via Notify immediately instead of blocking.
    if !opts.BlockingWait {
        b.mu.RLock()
        closed := b.closed
        addr = b.pinAddr
        upEps := len(b.upEps)
        b.mu.RUnlock()
        if closed {
            return grpc.Address{Addr: ""}, nil, grpc.ErrClientConnClosing
        }

        if upEps == 0 {
            return grpc.Address{Addr: ""}, nil, ErrNoAddrAvilable
        }
        return grpc.Address{Addr: addr}, func() {}, nil
    }

    for {
        b.mu.RLock()
        ch := b.upc
        b.mu.RUnlock()
        select {
        case <-ch:
        case <-ctx.Done():
            return grpc.Address{Addr: ""}, nil, ctx.Err()
        }
        b.mu.RLock()
        addr = b.pinAddr
        upEps := len(b.upEps)
        b.mu.RUnlock()
        if addr == "" {
            return grpc.Address{Addr: ""}, nil, grpc.ErrClientConnClosing
        }
        if upEps > 0 {
            break
        }
    }
    return grpc.Address{Addr: addr}, func() {}, nil
}

func (b *simpleBalancer) Notify() <-chan []grpc.Address { return b.notifyCh }

func (b *simpleBalancer) Close() error {
    b.mu.Lock()
    defer b.mu.Unlock()
    // In case gRPC calls close twice. TODO: remove the checking
    // when we are sure that gRPC wont call close twice.
    if b.closed {
        return nil
    }
    b.closed = true
    close(b.notifyCh)
    // terminate all waiting Get()s
    b.pinAddr = ""
    if len(b.upEps) == 0 {
        close(b.upc)
    }
    return nil
}

func getHost(ep string) string {
    url, uerr := url.Parse(ep)
    if uerr != nil || !strings.Contains(ep, "://") {
        return ep
    }
    return url.Host
}

balancer.go的更多相关文章

  1. sudo -u hdfs hdfs balancer出现异常 No lease on /system/balancer.id

    16/06/02 20:34:05 INFO balancer.Balancer: namenodes = [hdfs://dlhtHadoop101:8022, hdfs://dlhtHadoop1 ...

  2. 【转】HADOOP HDFS BALANCER介绍及经验总结

    转自:http://www.aboutyun.com/thread-7354-1-1.html 集群平衡介绍 Hadoop的HDFS集群非常容易出现机器与机器之间磁盘利用率不平衡的情况,比如集群中添加 ...

  3. CDH版HDFS Block Balancer方法

    命令: sudo -u hdfs hdfs balancer 默认会检查每个datanode的磁盘使用情况,对磁盘使用超过整个集群10%的datanode移动block到其他datanode达到均衡作 ...

  4. 负载均衡server load balancer

    负载均衡(Server Load Balancer,简称SLB)是对多台云服务器进行流量分发的负载均衡服务.SLB可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性. ( ...

  5. HDFS 上传文件的不平衡,Balancer问题是过慢

    至HDFS上传文件.假定从datanode开始上传文件,上传的数据将导致目前的当务之急是全datanode圆盘.这是一个分布式程序的执行是非常不利. 解决方案: 1.从其他非datanode节点上传 ...

  6. 【转载】漫谈HADOOP HDFS BALANCER

    Hadoop的HDFS集群非常容易出现机器与机器之间磁盘利用率不平衡的情况,比如集群中添加新的数据节点.当HDFS出现不平衡状况的时候,将引发很多问题,比如MR程序无法很好地利用本地计算的优势,机器之 ...

  7. 【转载】HDFS 上传文件不均衡和Balancer太慢的问题

    向HDFS上传文件,如果是从某个datanode开始上传文件,会导致上传的数据优先写满当前datanode的磁盘,这对于运行分布式程序是非常不利的. 解决的办法: 1.从其他非datanode节点上传 ...

  8. Feign报错Caused by: com.netflix.client.ClientException: Load balancer does not have available server for client

    问题描述 使用Feign调用微服务接口报错,如下: java.lang.RuntimeException: com.netflix.client.ClientException: Load balan ...

  9. Load balancer does not have available server for client

    最近在研究spring-cloud,研究zuul组件时发生下列错误: Caused by: com.netflix.client.ClientException: Load balancer does ...

随机推荐

  1. pop弹簧动画实现

    POP是一个在iOS与OS X上通用的极具扩展性的动画引擎.它在基本的静态动画的基础上增加的弹簧动画与衰减动画,使之能创造出更真实更具物理性的交互动画.POP的API可以快速的与现有的ObjC代码集成 ...

  2. 《转》xcode创建一个工程的多个taget,便于测试和发布多个版本

    背景:很多时候,我们需要在一个工程中创立多个target,也就是说我们希望同一份代码可以创建两个应用,放到模拟器或者真机上,或者是,我们平时有N多人合作开发,当测试的时候,在A这里装了一遍测A写的那块 ...

  3. IT轮子系列(三)——如何给返回类型添加注释——Swagger的使用(二)

    前言 一般对外提供API,都会统一一个返回类型,比如所有的接口都统一返回HttpResponseMessage.这样当我们在方法上添加///注释时,是无法清楚的知道都返回字段都又那些以及它们的数据类型 ...

  4. 使用HttpClient进行https连接(一)

    一.生成密钥库和证书 1.生成服务器证书库 keytool -validity 365 -genkey -v -alias uyun -keyalg RSA -keystore /opt/UEM/ke ...

  5. 利用LinkedHashMap实现简单的缓存

    update1:第二个实现,读操作不必要采用独占锁,缓存显然是读多于写,读的时候一开始用独占锁是考虑到要递增计数和更新时间戳要加锁,不过这两个变量都是采用原子变量,因此也不必采用独占锁,修改为读写锁. ...

  6. 在MySQL和PostgreSQL之外,为什么阿里要研发HybridDB数据库?

    http://www.infoq.com/cn/news/2016/12/MySQL-PostgreSQL-Greenplum 编者按 在大数据火遍IT界之前,大家对数据信息的挖掘通常聚焦在BI(Bu ...

  7. php进阶篇

    字符串调用: $name = 'eco'; echo $name; //eco //双引号会解析变量 echo "$name"; //eco //单引号不会解析变量 echo '$ ...

  8. java之Spring(AOP)前奏-动态代理设计模式(下)

    在上一章我们看到了,新增的三种类都能实现对原始功能类进行添加功能的事务处理,这三种类就是一个代理. 但是这种代理是写死的,怎样实现对任意接口添加自定义的代理呢? 我们先来看一下之前的代理实现: pub ...

  9. 利用Python脚本悄无声息的遥控室友电脑开机密码!

    整蛊一下室友就行了,切勿用于非法用途! 利用python脚本控制室友windows系统电脑的开机密码.利用random()生成随机数(密码),天知地知,密码只有你自己知道! Python代码分为cli ...

  10. C#本质论笔记

    第一章 C#概述 1.1 Helo,World 学习一种新语言最好的办法就是动手写程序.        C#编译器创建的.exe程序是一个程序集(Assembly),我们也可以创建能由另一个较大的程序 ...