github.com/kubewharf/katalyst-core@v0.5.3/pkg/custom-metric/store/local/server.go (about) 1 /* 2 Copyright 2022 The Katalyst Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package local 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "net/http" 24 "strconv" 25 "strings" 26 "time" 27 28 "k8s.io/apimachinery/pkg/labels" 29 "k8s.io/apimachinery/pkg/runtime/schema" 30 "k8s.io/klog/v2" 31 32 "github.com/kubewharf/katalyst-core/pkg/custom-metric/store/data" 33 "github.com/kubewharf/katalyst-core/pkg/custom-metric/store/data/types" 34 ) 35 36 const ( 37 ServingListPath = "/store/list" 38 ServingGetPath = "/store/get" 39 ServingSetPath = "/store/set" 40 ) 41 42 const ( 43 StoreListParamObjected = "objected" 44 ) 45 46 const ( 47 StoreGETParamNamespace = "namespace" 48 StoreGETParamMetricName = "metricName" 49 StoreGETParamMetricSelector = "metricSelector" 50 StoreGETParamObjectGR = "objGR" 51 StoreGETParamObjectName = "objName" 52 StoreGETParamMObjectSelector = "objSelector" 53 StoreGETParamLatest = "latest" 54 ) 55 56 type MemoryStoreData struct{} 57 58 // Serve todo: support to use gzip to reduce the transported data through http 59 func (l *LocalMemoryMetricStore) Serve(mux *http.ServeMux) { 60 klog.Infof("local store add serve handler") 61 62 mux.HandleFunc(ServingListPath, l.handleMetricList) 63 mux.HandleFunc(ServingGetPath, l.handleMetricGet) 64 mux.HandleFunc(ServingSetPath, l.handleMetricSet) 65 } 66 67 func (l *LocalMemoryMetricStore) handleMetricList(w http.ResponseWriter, r *http.Request) { 68 if !l.syncSuccess { 69 w.WriteHeader(http.StatusNotAcceptable) 70 _, _ = fmt.Fprintf(w, "store is in initializing status") 71 return 72 } 73 74 klog.V(6).Infof("receive list requests") 75 76 if r == nil || r.Method != "GET" || r.URL == nil { 77 klog.Errorf("Request must be GET with Query URL") 78 w.WriteHeader(http.StatusBadRequest) 79 _, _ = fmt.Fprintf(w, "Request must be GET with Query URL") 80 return 81 } 82 83 start := time.Now() 84 85 var ( 86 err error 87 metricMetalList []types.MetricMeta 88 ) 89 if r.URL.Query() == nil || len(getQueryParam(r, StoreListParamObjected)) == 0 { 90 metricMetalList, err = l.ListMetricMeta(context.Background(), false) 91 } else { 92 metricMetalList, err = l.ListMetricMeta(context.Background(), true) 93 } 94 95 if err != nil { 96 klog.Errorf("get internal list err: %v", err) 97 w.WriteHeader(http.StatusNotAcceptable) 98 _, _ = fmt.Fprintf(w, "Marshal internal err: %v", err) 99 return 100 } 101 102 readFinished := time.Now() 103 104 bytes, err := json.Marshal(metricMetalList) 105 if err != nil { 106 klog.Errorf("marshal internal list err: %v", err) 107 w.WriteHeader(http.StatusNotAcceptable) 108 _, _ = fmt.Fprintf(w, "Marshal internal error: %v", err) 109 return 110 } 111 112 jsonMarshalFinished := time.Now() 113 114 w.WriteHeader(http.StatusOK) 115 _, _ = w.Write(bytes) 116 117 writeRespFinished := time.Now() 118 119 klog.V(6).Infof("list cost read: %v, json: %v, resp: %v, total %v; len %v", 120 readFinished.Sub(start), 121 jsonMarshalFinished.Sub(readFinished), 122 writeRespFinished.Sub(jsonMarshalFinished), 123 writeRespFinished.Sub(start), 124 len(metricMetalList)) 125 } 126 127 func (l *LocalMemoryMetricStore) handleMetricGet(w http.ResponseWriter, r *http.Request) { 128 if !l.syncSuccess { 129 w.WriteHeader(http.StatusNotAcceptable) 130 _, _ = fmt.Fprintf(w, "store is in initializing status") 131 return 132 } 133 134 klog.V(6).Infof("receive get requests") 135 136 if r == nil || r.Method != "GET" || r.URL == nil || r.URL.Query() == nil { 137 klog.Errorf("Request must be GET with Query URL") 138 w.WriteHeader(http.StatusBadRequest) 139 _, _ = fmt.Fprintf(w, "Request must be GET with Query URL") 140 return 141 } 142 143 start := time.Now() 144 145 // 1. parse parameters from URL Query 146 var ( 147 objGR *schema.GroupResource 148 objSelector labels.Selector = nil 149 metricSelector labels.Selector = nil 150 latest bool 151 ) 152 153 namespace := getQueryParam(r, StoreGETParamNamespace) 154 155 metricName := getQueryParam(r, StoreGETParamMetricName) 156 metricSelectorStr := getQueryParam(r, StoreGETParamMetricSelector) 157 if len(metricSelectorStr) > 0 { 158 selector, err := labels.Parse(metricSelectorStr) 159 if err != nil { 160 klog.Errorf("metric selector parsing err: %v", err) 161 w.WriteHeader(http.StatusBadRequest) 162 _, _ = fmt.Fprintf(w, "Item selector parsing %v err: %v", metricSelectorStr, err) 163 return 164 } 165 metricSelector = selector 166 } 167 168 objName := getQueryParam(r, StoreGETParamObjectName) 169 objGRStr := getQueryParam(r, StoreGETParamObjectGR) 170 if len(objGRStr) > 0 { 171 _, gr := schema.ParseResourceArg(objGRStr) 172 objGR = &gr 173 } 174 objSelectorStr := getQueryParam(r, StoreGETParamMObjectSelector) 175 if len(objSelectorStr) > 0 { 176 selector, err := labels.Parse(objSelectorStr) 177 if err != nil { 178 klog.Errorf("object selector parsing err: %v", err) 179 w.WriteHeader(http.StatusBadRequest) 180 _, _ = fmt.Fprintf(w, "Object selector parsing %v err: %v", metricSelectorStr, err) 181 return 182 } 183 objSelector = selector 184 } 185 186 latestStr := getQueryParam(r, StoreGETParamLatest) 187 if len(latestStr) > 0 { 188 var err error 189 latest, err = strconv.ParseBool(latestStr) 190 if err != nil { 191 klog.Errorf("limited parsing %v err: %v", latestStr, err) 192 w.WriteHeader(http.StatusBadRequest) 193 _, _ = fmt.Fprintf(w, "Limited parsing %v err %v", latestStr, err) 194 return 195 } 196 } 197 198 readFinished := time.Now() 199 200 // 2. get from local cache and unmarshal into writer 201 internalList, err := l.GetMetric(context.Background(), namespace, metricName, objName, objGR, objSelector, metricSelector, latest) 202 if err != nil { 203 klog.Errorf("get internal list err: %v", err) 204 w.WriteHeader(http.StatusNotAcceptable) 205 _, _ = fmt.Fprintf(w, "Marshal internal err: %v", err) 206 return 207 } 208 209 bytes, err := json.Marshal(internalList) 210 if err != nil { 211 klog.Errorf("marshal internal list err: %v", err) 212 w.WriteHeader(http.StatusNotAcceptable) 213 _, _ = fmt.Fprintf(w, "Marshal internal err: %v", err) 214 return 215 } 216 217 jsonMarshalFinished := time.Now() 218 219 w.WriteHeader(http.StatusOK) 220 _, _ = w.Write(bytes) 221 222 writeRespFinished := time.Now() 223 224 klog.Infof("get metric %v, obj %v, cost read: %v, json: %v, resp: %v, total %v; len %v", 225 metricName, objName, 226 readFinished.Sub(start), 227 jsonMarshalFinished.Sub(readFinished), 228 writeRespFinished.Sub(jsonMarshalFinished), 229 writeRespFinished.Sub(start), 230 len(internalList)) 231 } 232 233 func (l *LocalMemoryMetricStore) handleMetricSet(w http.ResponseWriter, r *http.Request) { 234 if !l.syncSuccess { 235 w.WriteHeader(http.StatusNotAcceptable) 236 _, _ = fmt.Fprintf(w, "store is in initializing status") 237 return 238 } 239 240 klog.V(6).Infof("receive set requests") 241 242 if r == nil || r.Method != "POST" || r.Body == nil { 243 klog.Errorf("Request must be POST with Body") 244 w.WriteHeader(http.StatusBadRequest) 245 _, _ = fmt.Fprintf(w, "Request must be POST with Body") 246 return 247 } 248 249 start := time.Now() 250 251 defer func() { 252 _ = r.Body.Close() 253 }() 254 var seriesList []*data.MetricSeries 255 if err := json.NewDecoder(r.Body).Decode(&seriesList); err != nil { 256 klog.Errorf("read body err: %v", err) 257 w.WriteHeader(http.StatusBadRequest) 258 _, _ = fmt.Fprintf(w, "Read body err: %v", err) 259 return 260 } 261 262 jsonMarshalFinished := time.Now() 263 264 if err := l.InsertMetric(seriesList); err != nil { 265 klog.Errorf("insert seriesList err: %v", err) 266 w.WriteHeader(http.StatusNotAcceptable) 267 _, _ = fmt.Fprintf(w, "Insert seriesList err: %v", err) 268 return 269 } 270 271 writeRespFinished := time.Now() 272 273 w.WriteHeader(http.StatusOK) 274 _, _ = w.Write([]byte("success")) 275 klog.V(6).Infof("set cost read&json: %v, resp: %v, total %v", 276 jsonMarshalFinished.Sub(start), 277 writeRespFinished.Sub(jsonMarshalFinished), 278 writeRespFinished.Sub(start)) 279 } 280 281 // getQueryParam is a common util function to trim parameters from http query; 282 // if we need to perform string trim or anything like that, do it here 283 func getQueryParam(r *http.Request, key string) string { 284 return strings.TrimSpace(r.URL.Query().Get(key)) 285 }