简单的负载平衡器无法正常工作

时间:2017-08-25 12:52:22

标签: go

我从google io 2010获取了loadbalancer代码,并添加了针对Balancer的优先级队列和同步锁定的实现。我故意将workFn函数延迟设置为大于requester,因此我可以看到如何增加挂起值。我在cli中运行它并注意到在所有工作人员启动后,程序因所有工人的待定值 1 而停止,并且没有显示任何内容。错误在哪里我无法弄清楚,有时completed只召唤一次或两次。看起来<-b.done在特定情况下未得到妥善处理。

package main

import (
    "container/heap"
    "fmt"
    "math/rand"
    "os"
    "sync"
    "time"
)

var nWorker int32 = 6

func main() {
    rchanel := make(chan Request)
    workers := Pool{
        {make(chan Request), 0, 0},
        {make(chan Request), 0, 1},
        {make(chan Request), 0, 2},
        {make(chan Request), 0, 3},
        {make(chan Request), 0, 4},
        {make(chan Request), 0, 5},
    }
    doneChan := make(chan *Worker)
    balancer := Balancer{workers, sync.Mutex{}, doneChan}
    for _, elem := range workers {
        go elem.work(doneChan)
    }
    go balancer.balance(rchanel)
    go requester(rchanel)

    var input string
    fmt.Scanln(&input)
}

type Request struct {
    fn func() int
    c  chan int
}

func requester(work chan Request) {
    c := make(chan int)
    for {
        time.Sleep(time.Duration(rand.Int31n(nWorker)) * 2e4)
        work <- Request{workFn, c}
        go func() {
            result := <-c
            fmt.Fprintf(os.Stderr, "Done: %v \n", result)
        }()
    }
}

func workFn() int {
    val := rand.Int31n(nWorker)
    time.Sleep(time.Duration(val) * 2e8)
    return int(val)
}

type Worker struct {
    requests chan Request
    pending  int
    index    int
}

func (w *Worker) work(done chan *Worker) {
    for {
        req := <-w.requests
        req.c <- req.fn()
        done <- w
    }
}

type Pool []*Worker

func (p Pool) Less(i, j int) bool {
    return p[i].pending < p[j].pending
}
func (p Pool) Swap(i, j int) {
    p[i], p[j] = p[j], p[i]
    p[i].index = i
    p[j].index = j
}
func (p Pool) Len() int { return len(p) }
func (p *Pool) Push(x interface{}) {
    n := len(*p)
    worker := x.(*Worker)
    worker.index = n
    *p = append(*p, worker)
}
func (p *Pool) Pop() interface{} {
    old := *p
    n := len(old)
    item := old[n-1]
    item.index = -1
    *p = old[0 : n-1]
    return item
}

type Balancer struct {
    pool Pool
    mu   sync.Mutex
    done chan *Worker
}

func (b *Balancer) dispatch(req Request) {
    b.mu.Lock()
    w := heap.Pop(&b.pool).(*Worker)
    w.requests <- req
    w.pending++
    heap.Push(&b.pool, w)
    b.mu.Unlock()
}
func (b *Balancer) completed(w *Worker) {
    b.mu.Lock()
    w.pending--
    heap.Remove(&b.pool, w.index)
    heap.Push(&b.pool, w)
    b.mu.Unlock()
}

func (b *Balancer) balance(work chan Request) {
    for {
        select {
        case req := <-work:
            b.dispatch(req)
            b.printStatus()
        case w := <-b.done:
            b.completed(w)
            b.printStatus()
        }
    }
}

func (b *Balancer) printStatus() {
    fmt.Fprintf(os.Stderr, "Status: %v %v %v %v %v %v\n", b.pool[0].pending, b.pool[1].pending, b.pool[2].pending, b.pool[3].pending, b.pool[4].pending, b.pool[5].pending)
}

1 个答案:

答案 0 :(得分:1)

问题是balance() goroutine最终在dispatch()上的w.requests <- req被阻止,同时Workerwork()上阻止了done <- w {1}},为运行balance()的goroutine产生死锁。

这是您需要的修复程序。 balance()需要在内部使用goroutine。这将解决问题,因为现在dispatch()completed()中的例程阻塞无关紧要,balance()的主例程将继续select来自channel 1}} S上。

注意:这在游乐场不起作用,因为它会永远持续下去。

func (b *Balancer) balance(work chan Request) {
    for {
        select {
        case req := <-work:
            go func() {
                b.dispatch(req)
                b.printStatus()
            }()
        case w := <-b.done:
            go func() {
                b.completed(w)
                b.printStatus()
            }()
        }
    }
}

现在可以同时进行printStatus次调用,它也需要使用mutex,否则您将获得随机panic

func (b *Balancer) printStatus() {
    b.mu.Lock()
    fmt.Fprintf(os.Stderr, "Status: %v %v %v %v %v %v\n", b.pool[0].pending, b.pool[1].pending, b.pool[2].pending, b.pool[3].pending, b.pool[4].pending, b.pool[5].pending)
    b.mu.Unlock()
}

现在,如果我能弄清楚为什么pending值会不断增加......据我所知,Worker.work()应该只允许pending为{{1}或0因为1必须等待Worker才能从done <- w获得另一个Request。我相信这是理想的结果,不是吗?