github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/balancer/priority/balancer.go (about)

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