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  }