dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/client/controller/version/v2/loadreport.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 2020 gRPC authors.
    21   *
    22   */
    23  
    24  package v2
    25  
    26  import (
    27  	"context"
    28  	"errors"
    29  	"fmt"
    30  	"time"
    31  )
    32  
    33  import (
    34  	v2corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
    35  	v2endpointpb "github.com/envoyproxy/go-control-plane/envoy/api/v2/endpoint"
    36  	lrspb "github.com/envoyproxy/go-control-plane/envoy/service/load_stats/v2"
    37  
    38  	"github.com/golang/protobuf/proto"
    39  	"github.com/golang/protobuf/ptypes"
    40  
    41  	"google.golang.org/grpc"
    42  )
    43  
    44  import (
    45  	"dubbo.apache.org/dubbo-go/v3/xds/client/load"
    46  	"dubbo.apache.org/dubbo-go/v3/xds/client/resource"
    47  	"dubbo.apache.org/dubbo-go/v3/xds/utils/pretty"
    48  )
    49  
    50  const clientFeatureLRSSendAllClusters = "envoy.lrs.supports_send_all_clusters"
    51  
    52  type lrsStream lrspb.LoadReportingService_StreamLoadStatsClient
    53  
    54  func (v2c *client) NewLoadStatsStream(ctx context.Context, cc *grpc.ClientConn) (grpc.ClientStream, error) {
    55  	c := lrspb.NewLoadReportingServiceClient(cc)
    56  	return c.StreamLoadStats(ctx)
    57  }
    58  
    59  func (v2c *client) SendFirstLoadStatsRequest(s grpc.ClientStream) error {
    60  	stream, ok := s.(lrsStream)
    61  	if !ok {
    62  		return fmt.Errorf("lrs: Attempt to send request on unsupported stream type: %T", s)
    63  	}
    64  	node := proto.Clone(v2c.nodeProto).(*v2corepb.Node)
    65  	if node == nil {
    66  		node = &v2corepb.Node{}
    67  	}
    68  	node.ClientFeatures = append(node.ClientFeatures, clientFeatureLRSSendAllClusters)
    69  
    70  	req := &lrspb.LoadStatsRequest{Node: node}
    71  	v2c.logger.Infof("lrs: sending init LoadStatsRequest: %v", pretty.ToJSON(req))
    72  	return stream.Send(req)
    73  }
    74  
    75  func (v2c *client) HandleLoadStatsResponse(s grpc.ClientStream) ([]string, time.Duration, error) {
    76  	stream, ok := s.(lrsStream)
    77  	if !ok {
    78  		return nil, 0, fmt.Errorf("lrs: Attempt to receive response on unsupported stream type: %T", s)
    79  	}
    80  
    81  	resp, err := stream.Recv()
    82  	if err != nil {
    83  		return nil, 0, fmt.Errorf("lrs: failed to receive first response: %v", err)
    84  	}
    85  	v2c.logger.Infof("lrs: received first LoadStatsResponse: %+v", pretty.ToJSON(resp))
    86  
    87  	interval, err := ptypes.Duration(resp.GetLoadReportingInterval())
    88  	if err != nil {
    89  		return nil, 0, fmt.Errorf("lrs: failed to convert report interval: %v", err)
    90  	}
    91  
    92  	if resp.ReportEndpointGranularity {
    93  		// TODO: fixme to support per endpoint loads.
    94  		return nil, 0, errors.New("lrs: endpoint loads requested, but not supported by current implementation")
    95  	}
    96  
    97  	clusters := resp.Clusters
    98  	if resp.SendAllClusters {
    99  		// Return nil to send stats for all clusters.
   100  		clusters = nil
   101  	}
   102  
   103  	return clusters, interval, nil
   104  }
   105  
   106  func (v2c *client) SendLoadStatsRequest(s grpc.ClientStream, loads []*load.Data) error {
   107  	stream, ok := s.(lrsStream)
   108  	if !ok {
   109  		return fmt.Errorf("lrs: Attempt to send request on unsupported stream type: %T", s)
   110  	}
   111  
   112  	clusterStats := make([]*v2endpointpb.ClusterStats, 0, len(loads))
   113  	for _, sd := range loads {
   114  		droppedReqs := make([]*v2endpointpb.ClusterStats_DroppedRequests, 0, len(sd.Drops))
   115  		for category, count := range sd.Drops {
   116  			droppedReqs = append(droppedReqs, &v2endpointpb.ClusterStats_DroppedRequests{
   117  				Category:     category,
   118  				DroppedCount: count,
   119  			})
   120  		}
   121  		localityStats := make([]*v2endpointpb.UpstreamLocalityStats, 0, len(sd.LocalityStats))
   122  		for l, localityData := range sd.LocalityStats {
   123  			lid, err := resource.LocalityIDFromString(l)
   124  			if err != nil {
   125  				return err
   126  			}
   127  			loadMetricStats := make([]*v2endpointpb.EndpointLoadMetricStats, 0, len(localityData.LoadStats))
   128  			for name, loadData := range localityData.LoadStats {
   129  				loadMetricStats = append(loadMetricStats, &v2endpointpb.EndpointLoadMetricStats{
   130  					MetricName:                    name,
   131  					NumRequestsFinishedWithMetric: loadData.Count,
   132  					TotalMetricValue:              loadData.Sum,
   133  				})
   134  			}
   135  			localityStats = append(localityStats, &v2endpointpb.UpstreamLocalityStats{
   136  				Locality: &v2corepb.Locality{
   137  					Region:  lid.Region,
   138  					Zone:    lid.Zone,
   139  					SubZone: lid.SubZone,
   140  				},
   141  				TotalSuccessfulRequests: localityData.RequestStats.Succeeded,
   142  				TotalRequestsInProgress: localityData.RequestStats.InProgress,
   143  				TotalErrorRequests:      localityData.RequestStats.Errored,
   144  				LoadMetricStats:         loadMetricStats,
   145  				UpstreamEndpointStats:   nil, // TODO: populate for per endpoint loads.
   146  			})
   147  		}
   148  
   149  		clusterStats = append(clusterStats, &v2endpointpb.ClusterStats{
   150  			ClusterName:           sd.Cluster,
   151  			ClusterServiceName:    sd.Service,
   152  			UpstreamLocalityStats: localityStats,
   153  			TotalDroppedRequests:  sd.TotalDrops,
   154  			DroppedRequests:       droppedReqs,
   155  			LoadReportInterval:    ptypes.DurationProto(sd.ReportInterval),
   156  		})
   157  
   158  	}
   159  
   160  	req := &lrspb.LoadStatsRequest{ClusterStats: clusterStats}
   161  	v2c.logger.Infof("lrs: sending LRS loads: %+v", pretty.ToJSON(req))
   162  	return stream.Send(req)
   163  }