github.com/polarismesh/polaris@v1.17.8/cache/service/instance_query.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  	"encoding/json"
    22  	"sort"
    23  	"strconv"
    24  	"strings"
    25  
    26  	"go.uber.org/zap"
    27  
    28  	types "github.com/polarismesh/polaris/cache/api"
    29  	"github.com/polarismesh/polaris/common/model"
    30  	"github.com/polarismesh/polaris/common/utils"
    31  )
    32  
    33  // InstanceSearchArgs .
    34  type InstanceSearchArgs struct {
    35  	SvcName      *string
    36  	SvcNs        *string
    37  	InstanceID   *string
    38  	Hosts        map[string]struct{}
    39  	Port         *uint32
    40  	Protocol     *string
    41  	Version      *string
    42  	Region       *string
    43  	Zone         *string
    44  	Campus       *string
    45  	Weight       *uint32
    46  	HealthStatus *bool
    47  	Isolate      *bool
    48  	MetaFilter   map[string]string
    49  }
    50  
    51  func (args *InstanceSearchArgs) String() string {
    52  	//nolint: errchkjson
    53  	data, _ := json.Marshal(args)
    54  	return string(data)
    55  }
    56  
    57  func parseInstanceSearchArgs(filter, metaFilter map[string]string) *InstanceSearchArgs {
    58  	args := &InstanceSearchArgs{
    59  		MetaFilter: metaFilter,
    60  	}
    61  
    62  	if searchSvcName, hasSvc := filter["name"]; hasSvc {
    63  		args.SvcName = &searchSvcName
    64  	}
    65  	if searchNamespace, hasNamespace := filter["namespace"]; hasNamespace {
    66  		args.SvcNs = &searchNamespace
    67  	}
    68  	if id, hasId := filter["id"]; hasId {
    69  		args.InstanceID = &id
    70  	}
    71  	if protocol, hasProtocol := filter["protocol"]; hasProtocol {
    72  		args.Protocol = &protocol
    73  	}
    74  	if version, hasVersion := filter["version"]; hasVersion {
    75  		args.Version = &version
    76  	}
    77  	if region, hasRegion := filter["cmdb_region"]; hasRegion {
    78  		args.Region = &region
    79  	}
    80  	if campus, hasIdc := filter["cmdb_idc"]; hasIdc {
    81  		args.Campus = &campus
    82  	}
    83  	if zone, hasZone := filter["cmdb_zone"]; hasZone {
    84  		args.Zone = &zone
    85  	}
    86  
    87  	if hosts, hasHosts := filter["host"]; hasHosts {
    88  		hostMap := map[string]struct{}{}
    89  		hostItems := strings.Split(hosts, ",")
    90  		for i := range hostItems {
    91  			hostVal := strings.TrimSpace(hostItems[i])
    92  			if len(hostVal) == 0 {
    93  				continue
    94  			}
    95  			hostMap[hostVal] = struct{}{}
    96  		}
    97  		args.Hosts = hostMap
    98  	}
    99  
   100  	if portStr, ok := filter["port"]; ok {
   101  		if v, err := strconv.ParseUint(portStr, 10, 64); err == nil {
   102  			port := uint32(v)
   103  			args.Port = &port
   104  		}
   105  	}
   106  	if weightStr, ok := filter["weight"]; ok {
   107  		if v, err := strconv.ParseUint(weightStr, 10, 64); err == nil {
   108  			weight := uint32(v)
   109  			args.Weight = &weight
   110  		}
   111  	}
   112  	if isolateStr, ok := filter["isolate"]; ok {
   113  		if v, err := strconv.ParseBool(isolateStr); err == nil {
   114  			isolate := v
   115  			args.Isolate = &isolate
   116  		}
   117  	}
   118  	if healthStatusStr, ok := filter["health_status"]; ok {
   119  		if v, err := strconv.ParseBool(healthStatusStr); err == nil {
   120  			healthStatus := v
   121  			args.HealthStatus = &healthStatus
   122  		}
   123  	}
   124  	if healthyStr, ok := filter["healthy"]; ok {
   125  		if v, err := strconv.ParseBool(healthyStr); err == nil {
   126  			healthStatus := v
   127  			args.HealthStatus = &healthStatus
   128  		}
   129  	}
   130  	return args
   131  }
   132  
   133  // forceQueryUpdate 为了确保读取的数据是最新的,这里需要做一个强制 update 的动作进行数据读取处理
   134  func (ic *instanceCache) forceQueryUpdate() error {
   135  	err, shared := ic.singleUpdate()
   136  	// shared == true,表示当前已经有正在 update 执行的任务,这个任务不一定能够读取到最新的数据
   137  	// 为了避免读取到脏数据,在发起一次 singleUpdate
   138  	if shared {
   139  		naminglog.Debug("[Server][Instances][Query] force query update second")
   140  		err, _ = ic.singleUpdate()
   141  	}
   142  	return err
   143  }
   144  
   145  func (ic *instanceCache) QueryInstances(filter, metaFilter map[string]string,
   146  	offset, limit uint32) (uint32, []*model.Instance, error) {
   147  	if err := ic.forceQueryUpdate(); err != nil {
   148  		return 0, nil, err
   149  	}
   150  	var (
   151  		tempInstances = make([]*model.Instance, 0, 32)
   152  		args          = parseInstanceSearchArgs(filter, metaFilter)
   153  	)
   154  	naminglog.Info("[Server][Instances][Query] instances filter parameters", zap.String("args", args.String()))
   155  
   156  	svcCache, _ := ic.BaseCache.CacheMgr.GetCacher(types.CacheService).(*serviceCache)
   157  	_ = ic.IteratorInstances(func(key string, value *model.Instance) (bool, error) {
   158  		svc := svcCache.GetOrLoadServiceByID(value.ServiceID)
   159  		if svc == nil {
   160  			return true, nil
   161  		}
   162  		if args.SvcName != nil && !utils.IsWildMatch(svc.Name, *args.SvcName) {
   163  			return true, nil
   164  		}
   165  		if args.SvcNs != nil && !utils.IsWildMatch(svc.Namespace, *args.SvcNs) {
   166  			return true, nil
   167  		}
   168  		if args.InstanceID != nil && !utils.IsWildMatch(value.Proto.GetId().GetValue(), *args.InstanceID) {
   169  			return true, nil
   170  		}
   171  		if len(args.Hosts) != 0 {
   172  			if _, ok := args.Hosts[value.Proto.GetHost().GetValue()]; !ok {
   173  				return true, nil
   174  			}
   175  		}
   176  		if args.Port != nil && value.Proto.GetPort().GetValue() != *args.Port {
   177  			return true, nil
   178  		}
   179  		if args.Isolate != nil && value.Proto.GetIsolate().GetValue() != *args.Isolate {
   180  			return true, nil
   181  		}
   182  		if args.HealthStatus != nil && value.Proto.GetHealthy().GetValue() != *args.HealthStatus {
   183  			return true, nil
   184  		}
   185  		if args.Weight != nil && value.Proto.GetWeight().GetValue() != *args.Weight {
   186  			return true, nil
   187  		}
   188  		if args.Region != nil && value.Proto.GetLocation().GetRegion().GetValue() != *args.Region {
   189  			return true, nil
   190  		}
   191  		if args.Zone != nil && value.Proto.GetLocation().GetZone().GetValue() != *args.Zone {
   192  			return true, nil
   193  		}
   194  		if args.Campus != nil && value.Proto.GetLocation().GetCampus().GetValue() != *args.Campus {
   195  			return true, nil
   196  		}
   197  		if args.Protocol != nil && value.Proto.GetProtocol().GetValue() != *args.Protocol {
   198  			return true, nil
   199  		}
   200  		if args.Version != nil && value.Proto.GetVersion().GetValue() != *args.Version {
   201  			return true, nil
   202  		}
   203  		if len(metaFilter) > 0 {
   204  			for k, v := range metaFilter {
   205  				insV, ok := value.Proto.GetMetadata()[k]
   206  				if !ok || insV != v {
   207  					return true, nil
   208  				}
   209  			}
   210  		}
   211  		tempInstances = append(tempInstances, value)
   212  		return true, nil
   213  	})
   214  
   215  	total, ret := ic.doPage(tempInstances, offset, limit)
   216  	return total, ret, nil
   217  }
   218  
   219  func (ic *instanceCache) doPage(ins []*model.Instance, offset, limit uint32) (uint32, []*model.Instance) {
   220  	total := uint32(len(ins))
   221  	if offset > total {
   222  		return total, []*model.Instance{}
   223  	}
   224  	if offset+limit > total {
   225  		return total, ins[offset:]
   226  	}
   227  
   228  	sort.Slice(ins, func(i, j int) bool {
   229  		return ins[i].ModifyTime.After(ins[j].ModifyTime)
   230  	})
   231  
   232  	return total, ins[offset : offset+limit]
   233  }