dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/server/rds_handler.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 server
    25  
    26  import (
    27  	"sync"
    28  )
    29  
    30  import (
    31  	"dubbo.apache.org/dubbo-go/v3/xds/client/resource"
    32  )
    33  
    34  // rdsHandlerUpdate wraps the full RouteConfigUpdate that are dynamically
    35  // queried for a given server side listener.
    36  type rdsHandlerUpdate struct {
    37  	updates map[string]resource.RouteConfigUpdate
    38  	err     error
    39  }
    40  
    41  // rdsHandler handles any RDS queries that need to be started for a given server
    42  // side listeners Filter Chains (i.e. not inline).
    43  type rdsHandler struct {
    44  	xdsC XDSClient
    45  
    46  	mu      sync.Mutex
    47  	updates map[string]resource.RouteConfigUpdate
    48  	cancels map[string]func()
    49  
    50  	// For a rdsHandler update, the only update wrapped listener cares about is
    51  	// most recent one, so this channel will be opportunistically drained before
    52  	// sending any new updates.
    53  	updateChannel chan rdsHandlerUpdate
    54  }
    55  
    56  // newRDSHandler creates a new rdsHandler to watch for RDS resources.
    57  // listenerWrapper updates the list of route names to watch by calling
    58  // updateRouteNamesToWatch() upon receipt of new Listener configuration.
    59  func newRDSHandler(xdsC XDSClient, ch chan rdsHandlerUpdate) *rdsHandler {
    60  	return &rdsHandler{
    61  		xdsC:          xdsC,
    62  		updateChannel: ch,
    63  		updates:       make(map[string]resource.RouteConfigUpdate),
    64  		cancels:       make(map[string]func()),
    65  	}
    66  }
    67  
    68  // updateRouteNamesToWatch handles a list of route names to watch for a given
    69  // server side listener (if a filter chain specifies dynamic RDS configuration).
    70  // This function handles all the logic with respect to any routes that may have
    71  // been added or deleted as compared to what was previously present.
    72  func (rh *rdsHandler) updateRouteNamesToWatch(routeNamesToWatch map[string]bool) {
    73  	rh.mu.Lock()
    74  	defer rh.mu.Unlock()
    75  	// Add and start watches for any routes for any new routes in
    76  	// routeNamesToWatch.
    77  	for routeName := range routeNamesToWatch {
    78  		if _, ok := rh.cancels[routeName]; !ok {
    79  			func(routeName string) {
    80  				rh.cancels[routeName] = rh.xdsC.WatchRouteConfig(routeName, func(update resource.RouteConfigUpdate, err error) {
    81  					rh.handleRouteUpdate(routeName, update, err)
    82  				})
    83  			}(routeName)
    84  		}
    85  	}
    86  
    87  	// Delete and cancel watches for any routes from persisted routeNamesToWatch
    88  	// that are no longer present.
    89  	for routeName := range rh.cancels {
    90  		if _, ok := routeNamesToWatch[routeName]; !ok {
    91  			rh.cancels[routeName]()
    92  			delete(rh.cancels, routeName)
    93  			delete(rh.updates, routeName)
    94  		}
    95  	}
    96  
    97  	// If the full list (determined by length) of updates are now successfully
    98  	// updated, the listener is ready to be updated.
    99  	if len(rh.updates) == len(rh.cancels) && len(routeNamesToWatch) != 0 {
   100  		drainAndPush(rh.updateChannel, rdsHandlerUpdate{updates: rh.updates})
   101  	}
   102  }
   103  
   104  // handleRouteUpdate persists the route config for a given route name, and also
   105  // sends an update to the Listener Wrapper on an error received or if the rds
   106  // handler has a full collection of updates.
   107  func (rh *rdsHandler) handleRouteUpdate(routeName string, update resource.RouteConfigUpdate, err error) {
   108  	if err != nil {
   109  		drainAndPush(rh.updateChannel, rdsHandlerUpdate{err: err})
   110  		return
   111  	}
   112  	rh.mu.Lock()
   113  	defer rh.mu.Unlock()
   114  	rh.updates[routeName] = update
   115  
   116  	// If the full list (determined by length) of updates have successfully
   117  	// updated, the listener is ready to be updated.
   118  	if len(rh.updates) == len(rh.cancels) {
   119  		drainAndPush(rh.updateChannel, rdsHandlerUpdate{updates: rh.updates})
   120  	}
   121  }
   122  
   123  func drainAndPush(ch chan rdsHandlerUpdate, update rdsHandlerUpdate) {
   124  	select {
   125  	case <-ch:
   126  	default:
   127  	}
   128  	ch <- update
   129  }
   130  
   131  // close() is meant to be called by wrapped listener when the wrapped listener
   132  // is closed, and it cleans up resources by canceling all the active RDS
   133  // watches.
   134  func (rh *rdsHandler) close() {
   135  	rh.mu.Lock()
   136  	defer rh.mu.Unlock()
   137  	for _, cancel := range rh.cancels {
   138  		cancel()
   139  	}
   140  }