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 }