dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/balancer/priority/balancer.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  /*
    19   *
    20   * Copyright 2021 gRPC authors.
    21   *
    22   */
    23  
    24  // Package priority implements the priority balancer.
    25  //
    26  // This balancer will be kept in internal until we use it in the xds balancers,
    27  // and are confident its functionalities are stable. It will then be exported
    28  // for more users.
    29  package priority
    30  
    31  import (
    32  	"encoding/json"
    33  	"fmt"
    34  	"sync"
    35  	"time"
    36  )
    37  
    38  import (
    39  	dubbogoLogger "github.com/dubbogo/gost/log/logger"
    40  
    41  	"google.golang.org/grpc/balancer"
    42  
    43  	"google.golang.org/grpc/resolver"
    44  
    45  	"google.golang.org/grpc/serviceconfig"
    46  )
    47  
    48  import (
    49  	"dubbo.apache.org/dubbo-go/v3/xds/utils/balancergroup"
    50  	"dubbo.apache.org/dubbo-go/v3/xds/utils/buffer"
    51  	"dubbo.apache.org/dubbo-go/v3/xds/utils/grpcsync"
    52  	"dubbo.apache.org/dubbo-go/v3/xds/utils/hierarchy"
    53  	"dubbo.apache.org/dubbo-go/v3/xds/utils/pretty"
    54  )
    55  
    56  // Name is the name of the priority balancer.
    57  const Name = "priority_experimental"
    58  
    59  func init() {
    60  	balancer.Register(bb{})
    61  }
    62  
    63  type bb struct{}
    64  
    65  func (bb) Build(cc balancer.ClientConn, bOpts balancer.BuildOptions) balancer.Balancer {
    66  	b := &priorityBalancer{
    67  		cc:                       cc,
    68  		done:                     grpcsync.NewEvent(),
    69  		childToPriority:          make(map[string]int),
    70  		children:                 make(map[string]*childBalancer),
    71  		childBalancerStateUpdate: buffer.NewUnbounded(),
    72  	}
    73  
    74  	b.logger = dubbogoLogger.GetLogger()
    75  	b.bg = balancergroup.New(cc, bOpts, b, b.logger)
    76  	b.bg.Start()
    77  	go b.run()
    78  	b.logger.Infof("Created")
    79  	return b
    80  }
    81  
    82  func (b bb) ParseConfig(s json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
    83  	return parseConfig(s)
    84  }
    85  
    86  func (bb) Name() string {
    87  	return Name
    88  }
    89  
    90  // timerWrapper wraps a timer with a boolean. So that when a race happens
    91  // between AfterFunc and Stop, the func is guaranteed to not execute.
    92  type timerWrapper struct {
    93  	stopped bool
    94  	timer   *time.Timer
    95  }
    96  
    97  type priorityBalancer struct {
    98  	logger                   dubbogoLogger.Logger
    99  	cc                       balancer.ClientConn
   100  	bg                       *balancergroup.BalancerGroup
   101  	done                     *grpcsync.Event
   102  	childBalancerStateUpdate *buffer.Unbounded
   103  
   104  	mu         sync.Mutex
   105  	childInUse string
   106  	// priority of the child that's current in use. Int starting from 0, and 0
   107  	// is the higher priority.
   108  	priorityInUse int
   109  	// priorities is a list of child names from higher to lower priority.
   110  	priorities []string
   111  	// childToPriority is a map from the child name to it's priority. Priority
   112  	// is an int start from 0, and 0 is the higher priority.
   113  	childToPriority map[string]int
   114  	// children is a map from child name to sub-balancers.
   115  	children map[string]*childBalancer
   116  	// The timer to give a priority some time to connect. And if the priority
   117  	// doesn't go into Ready/Failure, the next priority will be started.
   118  	//
   119  	// One timer is enough because there can be at most one priority in init
   120  	// state.
   121  	priorityInitTimer *timerWrapper
   122  }
   123  
   124  func (b *priorityBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
   125  	b.logger.Infof("Received update from resolver, balancer config: %+v", pretty.ToJSON(s.BalancerConfig))
   126  	newConfig, ok := s.BalancerConfig.(*LBConfig)
   127  	if !ok {
   128  		return fmt.Errorf("unexpected balancer config with type: %T", s.BalancerConfig)
   129  	}
   130  	addressesSplit := hierarchy.Group(s.ResolverState.Addresses)
   131  
   132  	b.mu.Lock()
   133  	defer b.mu.Unlock()
   134  	// Create and remove children, since we know all children from the config
   135  	// are used by some priority.
   136  	for name, newSubConfig := range newConfig.Children {
   137  		bb := balancer.Get(newSubConfig.Config.Name)
   138  		if bb == nil {
   139  			b.logger.Errorf("balancer name %v from config is not registered", newSubConfig.Config.Name)
   140  			continue
   141  		}
   142  
   143  		currentChild, ok := b.children[name]
   144  		if !ok {
   145  			// This is a new child, add it to the children list. But note that
   146  			// the balancer isn't built, because this child can be a low
   147  			// priority. If necessary, it will be built when syncing priorities.
   148  			cb := newChildBalancer(name, b, bb)
   149  			cb.updateConfig(newSubConfig, resolver.State{
   150  				Addresses:     addressesSplit[name],
   151  				ServiceConfig: s.ResolverState.ServiceConfig,
   152  				Attributes:    s.ResolverState.Attributes,
   153  			})
   154  			b.children[name] = cb
   155  			continue
   156  		}
   157  
   158  		// This is not a new child. But the config/addresses could change.
   159  
   160  		// The balancing policy name is changed, close the old child. But don't
   161  		// rebuild, rebuild will happen when syncing priorities.
   162  		if currentChild.bb.Name() != bb.Name() {
   163  			currentChild.stop()
   164  			currentChild.updateBuilder(bb)
   165  		}
   166  
   167  		// Update config and address, but note that this doesn't send the
   168  		// updates to child balancer (the child balancer might not be built, if
   169  		// it's a low priority).
   170  		currentChild.updateConfig(newSubConfig, resolver.State{
   171  			Addresses:     addressesSplit[name],
   172  			ServiceConfig: s.ResolverState.ServiceConfig,
   173  			Attributes:    s.ResolverState.Attributes,
   174  		})
   175  	}
   176  
   177  	// Remove child from children if it's not in new config.
   178  	for name, oldChild := range b.children {
   179  		if _, ok := newConfig.Children[name]; !ok {
   180  			oldChild.stop()
   181  		}
   182  	}
   183  
   184  	// Update priorities and handle priority changes.
   185  	b.priorities = newConfig.Priorities
   186  	b.childToPriority = make(map[string]int, len(newConfig.Priorities))
   187  	for pi, pName := range newConfig.Priorities {
   188  		b.childToPriority[pName] = pi
   189  	}
   190  	// Sync the states of all children to the new updated priorities. This
   191  	// include starting/stopping child balancers when necessary.
   192  	b.syncPriority()
   193  
   194  	return nil
   195  }
   196  
   197  func (b *priorityBalancer) ResolverError(err error) {
   198  	b.bg.ResolverError(err)
   199  }
   200  
   201  func (b *priorityBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
   202  	b.bg.UpdateSubConnState(sc, state)
   203  }
   204  
   205  func (b *priorityBalancer) Close() {
   206  	b.bg.Close()
   207  
   208  	b.mu.Lock()
   209  	defer b.mu.Unlock()
   210  	b.done.Fire()
   211  	// Clear states of the current child in use, so if there's a race in picker
   212  	// update, it will be dropped.
   213  	b.childInUse = ""
   214  	b.stopPriorityInitTimer()
   215  }
   216  
   217  func (b *priorityBalancer) ExitIdle() {
   218  	b.bg.ExitIdle()
   219  }
   220  
   221  // stopPriorityInitTimer stops the priorityInitTimer if it's not nil, and set it
   222  // to nil.
   223  //
   224  // Caller must hold b.mu.
   225  func (b *priorityBalancer) stopPriorityInitTimer() {
   226  	timerW := b.priorityInitTimer
   227  	if timerW == nil {
   228  		return
   229  	}
   230  	b.priorityInitTimer = nil
   231  	timerW.stopped = true
   232  	timerW.timer.Stop()
   233  }
   234  
   235  // UpdateState implements balancergroup.BalancerStateAggregator interface. The
   236  // balancer group sends new connectivity state and picker here.
   237  func (b *priorityBalancer) UpdateState(childName string, state balancer.State) {
   238  	b.childBalancerStateUpdate.Put(&childBalancerState{
   239  		name: childName,
   240  		s:    state,
   241  	})
   242  }
   243  
   244  type childBalancerState struct {
   245  	name string
   246  	s    balancer.State
   247  }
   248  
   249  // run handles child update in a separate goroutine, so if the child sends
   250  // updates inline (when called by parent), it won't cause deadlocks (by trying
   251  // to hold the same mutex).
   252  func (b *priorityBalancer) run() {
   253  	for {
   254  		select {
   255  		case u := <-b.childBalancerStateUpdate.Get():
   256  			b.childBalancerStateUpdate.Load()
   257  			s := u.(*childBalancerState)
   258  			// Needs to handle state update in a goroutine, because each state
   259  			// update needs to start/close child policy, could result in
   260  			// deadlock.
   261  			b.handleChildStateUpdate(s.name, s.s)
   262  		case <-b.done.Done():
   263  			return
   264  		}
   265  	}
   266  }