github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/pickfirst.go (about)

     1  /*
     2   *
     3   * Copyright 2017 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 grpc
    20  
    21  import (
    22  	"errors"
    23  	"fmt"
    24  
    25  	"github.com/hxx258456/ccgo/grpc/balancer"
    26  	"github.com/hxx258456/ccgo/grpc/connectivity"
    27  )
    28  
    29  // PickFirstBalancerName is the name of the pick_first balancer.
    30  const PickFirstBalancerName = "pick_first"
    31  
    32  func newPickfirstBuilder() balancer.Builder {
    33  	return &pickfirstBuilder{}
    34  }
    35  
    36  type pickfirstBuilder struct{}
    37  
    38  func (*pickfirstBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) balancer.Balancer {
    39  	return &pickfirstBalancer{cc: cc}
    40  }
    41  
    42  func (*pickfirstBuilder) Name() string {
    43  	return PickFirstBalancerName
    44  }
    45  
    46  type pickfirstBalancer struct {
    47  	state connectivity.State
    48  	cc    balancer.ClientConn
    49  	sc    balancer.SubConn
    50  }
    51  
    52  func (b *pickfirstBalancer) ResolverError(err error) {
    53  	switch b.state {
    54  	case connectivity.TransientFailure, connectivity.Idle, connectivity.Connecting:
    55  		// Set a failing picker if we don't have a good picker.
    56  		b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.TransientFailure,
    57  			Picker: &picker{err: fmt.Errorf("name resolver error: %v", err)},
    58  		})
    59  	}
    60  	if logger.V(2) {
    61  		logger.Infof("pickfirstBalancer: ResolverError called with error %v", err)
    62  	}
    63  }
    64  
    65  func (b *pickfirstBalancer) UpdateClientConnState(cs balancer.ClientConnState) error {
    66  	if len(cs.ResolverState.Addresses) == 0 {
    67  		b.ResolverError(errors.New("produced zero addresses"))
    68  		return balancer.ErrBadResolverState
    69  	}
    70  	if b.sc == nil {
    71  		var err error
    72  		b.sc, err = b.cc.NewSubConn(cs.ResolverState.Addresses, balancer.NewSubConnOptions{})
    73  		if err != nil {
    74  			if logger.V(2) {
    75  				logger.Errorf("pickfirstBalancer: failed to NewSubConn: %v", err)
    76  			}
    77  			b.state = connectivity.TransientFailure
    78  			b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.TransientFailure,
    79  				Picker: &picker{err: fmt.Errorf("error creating connection: %v", err)},
    80  			})
    81  			return balancer.ErrBadResolverState
    82  		}
    83  		b.state = connectivity.Idle
    84  		b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.Idle, Picker: &picker{result: balancer.PickResult{SubConn: b.sc}}})
    85  		b.sc.Connect()
    86  	} else {
    87  		b.cc.UpdateAddresses(b.sc, cs.ResolverState.Addresses)
    88  		b.sc.Connect()
    89  	}
    90  	return nil
    91  }
    92  
    93  func (b *pickfirstBalancer) UpdateSubConnState(sc balancer.SubConn, s balancer.SubConnState) {
    94  	if logger.V(2) {
    95  		logger.Infof("pickfirstBalancer: UpdateSubConnState: %p, %v", sc, s)
    96  	}
    97  	if b.sc != sc {
    98  		if logger.V(2) {
    99  			logger.Infof("pickfirstBalancer: ignored state change because sc is not recognized")
   100  		}
   101  		return
   102  	}
   103  	b.state = s.ConnectivityState
   104  	if s.ConnectivityState == connectivity.Shutdown {
   105  		b.sc = nil
   106  		return
   107  	}
   108  
   109  	switch s.ConnectivityState {
   110  	case connectivity.Ready:
   111  		b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &picker{result: balancer.PickResult{SubConn: sc}}})
   112  	case connectivity.Connecting:
   113  		b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &picker{err: balancer.ErrNoSubConnAvailable}})
   114  	case connectivity.Idle:
   115  		b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &idlePicker{sc: sc}})
   116  	case connectivity.TransientFailure:
   117  		b.cc.UpdateState(balancer.State{
   118  			ConnectivityState: s.ConnectivityState,
   119  			Picker:            &picker{err: s.ConnectionError},
   120  		})
   121  	}
   122  }
   123  
   124  func (b *pickfirstBalancer) Close() {
   125  }
   126  
   127  func (b *pickfirstBalancer) ExitIdle() {
   128  	if b.sc != nil && b.state == connectivity.Idle {
   129  		b.sc.Connect()
   130  	}
   131  }
   132  
   133  type picker struct {
   134  	result balancer.PickResult
   135  	err    error
   136  }
   137  
   138  func (p *picker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
   139  	return p.result, p.err
   140  }
   141  
   142  // idlePicker is used when the SubConn is IDLE and kicks the SubConn into
   143  // CONNECTING when Pick is called.
   144  type idlePicker struct {
   145  	sc balancer.SubConn
   146  }
   147  
   148  func (i *idlePicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
   149  	i.sc.Connect()
   150  	return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
   151  }
   152  
   153  func init() {
   154  	balancer.Register(newPickfirstBuilder())
   155  }