github.com/polarismesh/polaris@v1.17.8/service/client_v1.go (about) 1 /** 2 * Tencent is pleased to support the open source community by making Polaris available. 3 * 4 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. 5 * 6 * Licensed under the BSD 3-Clause License (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * https://opensource.org/licenses/BSD-3-Clause 11 * 12 * Unless required by applicable law or agreed to in writing, software distributed 13 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 14 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 15 * specific language governing permissions and limitations under the License. 16 */ 17 18 package service 19 20 import ( 21 "context" 22 "fmt" 23 "strings" 24 25 "github.com/golang/protobuf/ptypes/wrappers" 26 apimodel "github.com/polarismesh/specification/source/go/api/v1/model" 27 apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage" 28 apitraffic "github.com/polarismesh/specification/source/go/api/v1/traffic_manage" 29 "go.uber.org/zap" 30 31 api "github.com/polarismesh/polaris/common/api/v1" 32 "github.com/polarismesh/polaris/common/metrics" 33 "github.com/polarismesh/polaris/common/model" 34 "github.com/polarismesh/polaris/common/utils" 35 ) 36 37 // RegisterInstance create one instance 38 func (s *Server) RegisterInstance(ctx context.Context, req *apiservice.Instance) *apiservice.Response { 39 ctx = context.WithValue(ctx, utils.ContextIsFromClient, true) 40 return s.CreateInstance(ctx, req) 41 } 42 43 // DeregisterInstance delete one instance 44 func (s *Server) DeregisterInstance(ctx context.Context, req *apiservice.Instance) *apiservice.Response { 45 ctx = context.WithValue(ctx, utils.ContextIsFromClient, true) 46 return s.DeleteInstance(ctx, req) 47 } 48 49 // ReportClient 客户端上报信息 50 func (s *Server) ReportClient(ctx context.Context, req *apiservice.Client) *apiservice.Response { 51 if s.caches == nil { 52 return api.NewResponse(apimodel.Code_ClientAPINotOpen) 53 } 54 55 // 客户端信息不写入到DB中 56 host := req.GetHost().GetValue() 57 // 从CMDB查询地理位置信息 58 if s.cmdb != nil { 59 location, err := s.cmdb.GetLocation(host) 60 if err != nil { 61 log.Errora(utils.RequestID(ctx), zap.Error(err)) 62 } 63 if location != nil { 64 req.Location = location.Proto 65 } 66 } 67 68 // save the client with unique id into store 69 if len(req.GetId().GetValue()) > 0 { 70 return s.checkAndStoreClient(ctx, req) 71 } 72 out := &apiservice.Client{ 73 Host: req.GetHost(), 74 Location: req.Location, 75 } 76 return api.NewClientResponse(apimodel.Code_ExecuteSuccess, out) 77 } 78 79 // GetPrometheusTargets Used for client acquisition service information 80 func (s *Server) GetPrometheusTargets(ctx context.Context, 81 query map[string]string) *model.PrometheusDiscoveryResponse { 82 if s.caches == nil { 83 return &model.PrometheusDiscoveryResponse{ 84 Code: api.NotFoundInstance, 85 Response: make([]model.PrometheusTarget, 0), 86 } 87 } 88 89 targets := make([]model.PrometheusTarget, 0, 8) 90 expectSchema := map[string]struct{}{ 91 "http": {}, 92 "https": {}, 93 } 94 95 s.Cache().Client().IteratorClients(func(key string, value *model.Client) bool { 96 for i := range value.Proto().Stat { 97 stat := value.Proto().Stat[i] 98 if stat.Target.GetValue() != model.StatReportPrometheus { 99 continue 100 } 101 _, ok := expectSchema[strings.ToLower(stat.Protocol.GetValue())] 102 if !ok { 103 continue 104 } 105 106 target := model.PrometheusTarget{ 107 Targets: []string{fmt.Sprintf("%s:%d", value.Proto().Host.GetValue(), stat.Port.GetValue())}, 108 Labels: map[string]string{ 109 "__metrics_path__": stat.Path.GetValue(), 110 "__scheme__": stat.Protocol.GetValue(), 111 "__meta_polaris_client_id": value.Proto().Id.GetValue(), 112 }, 113 } 114 targets = append(targets, target) 115 } 116 117 return true 118 }) 119 120 // 加入北极星集群自身 121 checkers := s.healthServer.ListCheckerServer() 122 for i := range checkers { 123 checker := checkers[i] 124 target := model.PrometheusTarget{ 125 Targets: []string{fmt.Sprintf("%s:%d", checker.Host(), metrics.GetMetricsPort())}, 126 Labels: map[string]string{ 127 "__metrics_path__": "/metrics", 128 "__scheme__": "http", 129 "__meta_polaris_client_id": checker.ID(), 130 }, 131 } 132 targets = append(targets, target) 133 } 134 135 return &model.PrometheusDiscoveryResponse{ 136 Code: api.ExecuteSuccess, 137 Response: targets, 138 } 139 } 140 141 // GetServiceWithCache 查询服务列表 142 func (s *Server) GetServiceWithCache(ctx context.Context, req *apiservice.Service) *apiservice.DiscoverResponse { 143 if s.caches == nil { 144 return api.NewDiscoverServiceResponse(apimodel.Code_ClientAPINotOpen, req) 145 } 146 if req == nil { 147 return api.NewDiscoverServiceResponse(apimodel.Code_EmptyRequest, req) 148 } 149 150 resp := api.NewDiscoverServiceResponse(apimodel.Code_ExecuteSuccess, req) 151 var ( 152 revision string 153 svcs []*model.Service 154 ) 155 156 if req.GetNamespace().GetValue() != "" { 157 revision, svcs = s.Cache().Service().ListServices(req.GetNamespace().GetValue()) 158 } else { 159 revision, svcs = s.Cache().Service().ListAllServices() 160 } 161 if revision == "" { 162 return resp 163 } 164 165 log.Debug("[Service][Discover] list servies", zap.Int("size", len(svcs)), zap.String("revision", revision)) 166 if revision == req.GetRevision().GetValue() { 167 return api.NewDiscoverServiceResponse(apimodel.Code_DataNoChange, req) 168 } 169 170 ret := make([]*apiservice.Service, 0, len(svcs)) 171 for i := range svcs { 172 ret = append(ret, &apiservice.Service{ 173 Namespace: utils.NewStringValue(svcs[i].Namespace), 174 Name: utils.NewStringValue(svcs[i].Name), 175 Metadata: svcs[i].Meta, 176 }) 177 } 178 179 resp.Services = ret 180 resp.Service = &apiservice.Service{ 181 Namespace: utils.NewStringValue(req.GetNamespace().GetValue()), 182 Name: utils.NewStringValue(req.GetName().GetValue()), 183 Revision: utils.NewStringValue(revision), 184 } 185 186 return resp 187 } 188 189 // ServiceInstancesCache 根据服务名查询服务实例列表 190 func (s *Server) ServiceInstancesCache(ctx context.Context, req *apiservice.Service) *apiservice.DiscoverResponse { 191 resp := createCommonDiscoverResponse(req, apiservice.DiscoverResponse_INSTANCE) 192 serviceName := req.GetName().GetValue() 193 namespaceName := req.GetNamespace().GetValue() 194 195 // 消费服务为了兼容,可以不带namespace,server端使用默认的namespace 196 if namespaceName == "" { 197 namespaceName = DefaultNamespace 198 req.Namespace = utils.NewStringValue(namespaceName) 199 } 200 if !s.commonCheckDiscoverRequest(req, resp) { 201 return resp 202 } 203 204 // 数据源都来自Cache,这里拿到的service,已经是源服务 205 aliasFor := s.getServiceCache(serviceName, namespaceName) 206 if aliasFor == nil { 207 log.Debugf("[Server][Service][Instance] not found name(%s) namespace(%s) service", 208 serviceName, namespaceName) 209 return api.NewDiscoverInstanceResponse(apimodel.Code_NotFoundResource, req) 210 } 211 s.RecordDiscoverStatis(aliasFor.Name, aliasFor.Namespace) 212 // 获取revision,如果revision一致,则不返回内容,直接返回一个状态码 213 revision := s.caches.Service().GetRevisionWorker().GetServiceInstanceRevision(aliasFor.ID) 214 if revision == "" { 215 // 不能直接获取,则需要重新计算,大部分情况都可以直接获取的 216 // 获取instance数据,service已经是源服务,可以直接查找cache 217 instances := s.caches.Instance().GetInstancesByServiceID(aliasFor.ID) 218 var revisionErr error 219 revision, revisionErr = s.GetServiceInstanceRevision(aliasFor.ID, instances) 220 if revisionErr != nil { 221 log.Errorf("[Server][Service][Instance] compute revision service(%s) err: %s", 222 aliasFor.ID, revisionErr.Error()) 223 return api.NewDiscoverInstanceResponse(apimodel.Code_ExecuteException, req) 224 } 225 } 226 if revision == req.GetRevision().GetValue() { 227 return api.NewDiscoverInstanceResponse(apimodel.Code_DataNoChange, req) 228 } 229 230 resp.Service = service2Api(aliasFor) 231 // 塞入源服务信息数据 232 resp.AliasFor = service2Api(aliasFor) 233 // 替换 service 名称 234 resp.Service.Name = req.GetName() 235 resp.Service.Namespace = req.GetNamespace() 236 resp.Service.Revision = utils.NewStringValue(revision) 237 // 填充instance数据 238 resp.Instances = make([]*apiservice.Instance, 0) 239 _ = s.caches.Instance(). 240 IteratorInstancesWithService(aliasFor.ID, // service已经是源服务 241 func(key string, value *model.Instance) (b bool, e error) { 242 // 注意:这里的value是cache的,不修改cache的数据,通过getInstance,浅拷贝一份数据 243 resp.Instances = append(resp.Instances, s.getInstance(req, value.Proto)) 244 return true, nil 245 }) 246 247 return resp 248 } 249 250 // GetRoutingConfigWithCache 获取缓存中的路由配置信息 251 func (s *Server) GetRoutingConfigWithCache(ctx context.Context, req *apiservice.Service) *apiservice.DiscoverResponse { 252 resp := createCommonDiscoverResponse(req, apiservice.DiscoverResponse_ROUTING) 253 if !s.commonCheckDiscoverRequest(req, resp) { 254 return resp 255 } 256 257 resp.Service = &apiservice.Service{ 258 Name: req.GetName(), 259 Namespace: req.GetNamespace(), 260 } 261 262 // 先从缓存获取ServiceID,这里返回的是源服务 263 aliasFor := s.getServiceCache(req.GetName().GetValue(), req.GetNamespace().GetValue()) 264 if aliasFor == nil { 265 aliasFor = &model.Service{ 266 Namespace: req.GetNamespace().GetValue(), 267 Name: req.GetName().GetValue(), 268 } 269 } 270 271 out, err := s.caches.RoutingConfig().GetRouterConfig(aliasFor.ID, aliasFor.Name, aliasFor.Namespace) 272 if err != nil { 273 log.Error("[Server][Service][Routing] discover routing", utils.RequestID(ctx), zap.Error(err)) 274 return api.NewDiscoverRoutingResponse(apimodel.Code_ExecuteException, req) 275 } 276 if out == nil { 277 return resp 278 } 279 280 // 获取路由数据,并对比revision 281 if out.GetRevision().GetValue() == req.GetRevision().GetValue() { 282 return api.NewDiscoverRoutingResponse(apimodel.Code_DataNoChange, req) 283 } 284 285 // 数据不一致,发生了改变 286 // 数据格式转换,service只需要返回二元组与routing的revision 287 resp.Service.Revision = out.GetRevision() 288 resp.Routing = out 289 resp.AliasFor = &apiservice.Service{ 290 Name: utils.NewStringValue(aliasFor.Name), 291 Namespace: utils.NewStringValue(aliasFor.Namespace), 292 } 293 return resp 294 } 295 296 // GetRateLimitWithCache 获取缓存中的限流规则信息 297 func (s *Server) GetRateLimitWithCache(ctx context.Context, req *apiservice.Service) *apiservice.DiscoverResponse { 298 resp := createCommonDiscoverResponse(req, apiservice.DiscoverResponse_RATE_LIMIT) 299 if !s.commonCheckDiscoverRequest(req, resp) { 300 return resp 301 } 302 303 // 获取源服务 304 aliasFor := s.getServiceCache(req.GetName().GetValue(), req.GetNamespace().GetValue()) 305 if aliasFor == nil { 306 aliasFor = &model.Service{ 307 Name: req.GetName().GetValue(), 308 Namespace: req.GetNamespace().GetValue(), 309 } 310 } 311 312 rules, revision := s.caches.RateLimit().GetRateLimitRules(model.ServiceKey{ 313 Namespace: aliasFor.Namespace, 314 Name: aliasFor.Name, 315 }) 316 if len(rules) == 0 || revision == "" { 317 return resp 318 } 319 if req.GetRevision().GetValue() == revision { 320 return api.NewDiscoverRateLimitResponse(apimodel.Code_DataNoChange, req) 321 } 322 resp.RateLimit = &apitraffic.RateLimit{ 323 Revision: utils.NewStringValue(revision), 324 Rules: []*apitraffic.Rule{}, 325 } 326 for i := range rules { 327 rateLimit, err := rateLimit2Client(req.GetName().GetValue(), req.GetNamespace().GetValue(), rules[i]) 328 if rateLimit == nil || err != nil { 329 continue 330 } 331 resp.RateLimit.Rules = append(resp.RateLimit.Rules, rateLimit) 332 } 333 334 // 塞入源服务信息数据 335 resp.AliasFor = &apiservice.Service{ 336 Namespace: utils.NewStringValue(aliasFor.Namespace), 337 Name: utils.NewStringValue(aliasFor.Name), 338 } 339 // 服务名和request保持一致 340 resp.Service = &apiservice.Service{ 341 Name: req.GetName(), 342 Namespace: req.GetNamespace(), 343 Revision: utils.NewStringValue(revision), 344 } 345 return resp 346 } 347 348 func (s *Server) GetFaultDetectWithCache(ctx context.Context, req *apiservice.Service) *apiservice.DiscoverResponse { 349 resp := createCommonDiscoverResponse(req, apiservice.DiscoverResponse_FAULT_DETECTOR) 350 if !s.commonCheckDiscoverRequest(req, resp) { 351 return resp 352 } 353 // 服务名和request保持一致 354 resp.Service = &apiservice.Service{ 355 Name: req.GetName(), 356 Namespace: req.GetNamespace(), 357 } 358 359 // 获取源服务 360 aliasFor := s.getServiceCache(req.GetName().GetValue(), req.GetNamespace().GetValue()) 361 if aliasFor == nil { 362 aliasFor = &model.Service{ 363 Namespace: req.GetNamespace().GetValue(), 364 Name: req.GetName().GetValue(), 365 } 366 } 367 368 out := s.caches.FaultDetector().GetFaultDetectConfig(aliasFor.Name, aliasFor.Namespace) 369 if out == nil || out.Revision == "" { 370 return resp 371 } 372 373 if req.GetRevision().GetValue() == out.Revision { 374 return api.NewDiscoverFaultDetectorResponse(apimodel.Code_DataNoChange, req) 375 } 376 377 // 数据不一致,发生了改变 378 var err error 379 resp.AliasFor = &apiservice.Service{ 380 Name: utils.NewStringValue(aliasFor.Name), 381 Namespace: utils.NewStringValue(aliasFor.Namespace), 382 } 383 resp.Service.Revision = utils.NewStringValue(out.Revision) 384 resp.FaultDetector, err = faultDetectRule2ClientAPI(out) 385 if err != nil { 386 log.Error(err.Error(), utils.RequestID(ctx)) 387 return api.NewDiscoverFaultDetectorResponse(apimodel.Code_ExecuteException, req) 388 } 389 return resp 390 } 391 392 // GetCircuitBreakerWithCache 获取缓存中的熔断规则信息 393 func (s *Server) GetCircuitBreakerWithCache(ctx context.Context, req *apiservice.Service) *apiservice.DiscoverResponse { 394 resp := createCommonDiscoverResponse(req, apiservice.DiscoverResponse_CIRCUIT_BREAKER) 395 if !s.commonCheckDiscoverRequest(req, resp) { 396 return resp 397 } 398 399 // 服务名和request保持一致 400 resp.Service = &apiservice.Service{ 401 Name: req.GetName(), 402 Namespace: req.GetNamespace(), 403 } 404 405 // 获取源服务 406 aliasFor := s.getServiceCache(req.GetName().GetValue(), req.GetNamespace().GetValue()) 407 if aliasFor == nil { 408 aliasFor = &model.Service{ 409 Namespace: req.GetNamespace().GetValue(), 410 Name: req.GetName().GetValue(), 411 } 412 } 413 out := s.caches.CircuitBreaker().GetCircuitBreakerConfig(aliasFor.Name, aliasFor.Namespace) 414 if out == nil || out.Revision == "" { 415 return resp 416 } 417 418 // 获取熔断规则数据,并对比revision 419 if len(req.GetRevision().GetValue()) > 0 && req.GetRevision().GetValue() == out.Revision { 420 return api.NewDiscoverCircuitBreakerResponse(apimodel.Code_DataNoChange, req) 421 } 422 423 // 数据不一致,发生了改变 424 var err error 425 resp.AliasFor = &apiservice.Service{ 426 Name: utils.NewStringValue(aliasFor.Name), 427 Namespace: utils.NewStringValue(aliasFor.Namespace), 428 } 429 resp.Service.Revision = utils.NewStringValue(out.Revision) 430 resp.CircuitBreaker, err = circuitBreaker2ClientAPI(out, req.GetName().GetValue(), req.GetNamespace().GetValue()) 431 if err != nil { 432 log.Error(err.Error(), utils.RequestID(ctx)) 433 return api.NewDiscoverCircuitBreakerResponse(apimodel.Code_ExecuteException, req) 434 } 435 return resp 436 } 437 438 func createCommonDiscoverResponse(req *apiservice.Service, 439 dT apiservice.DiscoverResponse_DiscoverResponseType) *apiservice.DiscoverResponse { 440 return &apiservice.DiscoverResponse{ 441 Code: &wrappers.UInt32Value{Value: uint32(apimodel.Code_ExecuteSuccess)}, 442 Info: &wrappers.StringValue{Value: api.Code2Info(uint32(apimodel.Code_ExecuteSuccess))}, 443 Type: dT, 444 Service: &apiservice.Service{ 445 Name: req.GetName(), 446 Namespace: req.GetNamespace(), 447 }, 448 } 449 } 450 451 // 根据ServiceID获取instances 452 func (s *Server) getInstancesCache(service *model.Service) []*model.Instance { 453 id := s.getSourceServiceID(service) 454 // TODO refer_filter还要处理一下 455 return s.caches.Instance().GetInstancesByServiceID(id) 456 } 457 458 // 获取顶级服务ID 459 // 没有顶级ID,则返回自身 460 func (s *Server) getSourceServiceID(service *model.Service) string { 461 if service == nil || service.ID == "" { 462 return "" 463 } 464 // 找到parent服务,最多两级,因此不用递归查找 465 if service.IsAlias() { 466 return service.Reference 467 } 468 469 return service.ID 470 } 471 472 // 根据服务名获取服务缓存数据 473 // 注意,如果是服务别名查询,这里会返回别名的源服务,不会返回别名 474 func (s *Server) getServiceCache(name string, namespace string) *model.Service { 475 sc := s.caches.Service() 476 service := sc.GetServiceByName(name, namespace) 477 if service == nil { 478 return nil 479 } 480 // 如果是服务别名,继续查找一下 481 if service.IsAlias() { 482 service = sc.GetServiceByID(service.Reference) 483 if service == nil { 484 return nil 485 } 486 } 487 488 if service.Meta == nil { 489 service.Meta = make(map[string]string) 490 } 491 return service 492 } 493 494 func (s *Server) commonCheckDiscoverRequest(req *apiservice.Service, resp *apiservice.DiscoverResponse) bool { 495 if s.caches == nil { 496 resp.Code = utils.NewUInt32Value(uint32(apimodel.Code_ClientAPINotOpen)) 497 resp.Info = utils.NewStringValue(api.Code2Info(resp.GetCode().GetValue())) 498 resp.Service = req 499 return false 500 } 501 if req == nil { 502 resp.Code = utils.NewUInt32Value(uint32(apimodel.Code_EmptyRequest)) 503 resp.Info = utils.NewStringValue(api.Code2Info(resp.GetCode().GetValue())) 504 resp.Service = req 505 return false 506 } 507 508 if req.GetName().GetValue() == "" { 509 resp.Code = utils.NewUInt32Value(uint32(apimodel.Code_InvalidServiceName)) 510 resp.Info = utils.NewStringValue(api.Code2Info(resp.GetCode().GetValue())) 511 resp.Service = req 512 return false 513 } 514 if req.GetNamespace().GetValue() == "" { 515 resp.Code = utils.NewUInt32Value(uint32(apimodel.Code_InvalidNamespaceName)) 516 resp.Info = utils.NewStringValue(api.Code2Info(resp.GetCode().GetValue())) 517 resp.Service = req 518 return false 519 } 520 521 return true 522 }