天天看點

gRPC-10 用戶端負載均衡

代碼位址

https://github.com/wanmei002/grpc-learn/tree/master/ch09

介紹

我工作中,請求一個接口,會先從去負載均衡器中擷取一個ip,然後直接拿着ip去通路接口,在這裡呢我們相當于把負載均衡器內建到用戶端服務中,用戶端發起請求的時候,會獲得一個ip清單,根據特定的算法,選取一個IP, 向服務端發起請求

實作邏輯

  1. grpc.Dial 的時候輸入我們的服務名
  2. 實作 resolver.go/Builder接口,連接配接的時候會調用這個接口裡的方法 Build Scheme 方法
  3. 在 Build 方法裡我們根據服務名,擷取服務的ip位址清單
  4. 傳入負載均衡算法,實作用戶端負載均衡

用戶端

grpc.Dial 傳參

dl, err := grpc.Dial(fmt.Sprintf("%s:///%s", "order", "Order"),
       grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, roundrobin.Name)),
       grpc.WithInsecure())
           
  • fmt.Sprintf("%s:///%s", "order", "Order")

    第一個

    order

    是服務解析器的名字,後面要通過這個名字找到對應的 實作

    Builder

    這個接口的服務解析器, 後一個

    Order

    是微服務名 , 這兩個參數都是可以自定義的
  • grpc.WithDefaultServiceConfig(fmt.Sprintf(

    {“LoadBalancingPolicy”: “%s”}

    , roundrobin.Name))

    負載均衡算法,我們這裡使用的是

    grpc

    自帶的輪詢算法。

實作服務解析器

// 提供服務的機器
var addrs = []string{":8093", ":8094"}

type loopBuilder struct{}
// 實作 Builder 接口, 這個接口的主要作用是把提供服務的IP清單,裝載進撥号清單中
func (lb *loopBuilder) Build(target resolver.Target, cc resolver.ClientConn,
    opts resolver.BuildOptions) (resolver.Resolver, error) {
    log.Println("i am build")
    ll := &loopResolver{
        target:     target,
        cc:         cc,
        addrsStore: map[string][]string{
            LoopServiceName: addrs,
        },
    }
    ll.start()
    return ll, nil
}
// 這個是注冊服務解析器的時候提供的注冊名
func (*loopBuilder) Scheme() string {
    log.Println("i am scheme")
    return LoopScheme
}
// 實作 resolver.Resolver ,主要用于更新服務端IP清單
type loopResolver struct {
    target  resolver.Target
    cc      resolver.ClientConn
    addrsStore map[string][]string
}
// 更新撥号清單
func (ll *loopResolver) start() {
    addrList := ll.addrsStore[ll.target.Endpoint]
    log.Printf("loop resolver %+v\n", ll)
    addr := make([]resolver.Address, 0)
    for _, s := range addrList {
        addr = append(addr, resolver.Address{Addr: s})
    }
    log.Println("start start")
    
    ll.cc.UpdateState(resolver.State{Addresses: addr})
}

func (*loopResolver) ResolveNow(o resolver.ResolveNowOptions) {
    log.Println(" i am in resolveNew")
}

func (*loopResolver) Close(){
    log.Println(" i am in close ")
}

func init(){
    resolver.Register(&loopBuilder{})
}
           

服務端

注冊多個ip提供服務

func main(){
    log.Println("loop register server")
    var wg = &sync.WaitGroup{} 
    wg.Add(len(addrList))
    for _, addr := range addrList {
        go StartServer(addr, wg) // 提供服務
    }
    
    wg.Wait()
    log.Println("over")
}