github.com/polarismesh/polaris@v1.17.8/service/healthcheck/report.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 healthcheck
    19  
    20  import (
    21  	"context"
    22  	"time"
    23  
    24  	apimodel "github.com/polarismesh/specification/source/go/api/v1/model"
    25  	apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage"
    26  
    27  	api "github.com/polarismesh/polaris/common/api/v1"
    28  	"github.com/polarismesh/polaris/common/model"
    29  	"github.com/polarismesh/polaris/common/utils"
    30  	"github.com/polarismesh/polaris/plugin"
    31  )
    32  
    33  // checkHeartbeatInstance 检查心跳实例请求参数
    34  // 检查是否存在token,以及 id或者四元组
    35  // 注意:心跳上报只允许从client上报,因此token只会存在req中
    36  func checkHeartbeatInstance(req *apiservice.Instance) (string, *apiservice.Response) {
    37  	if req == nil {
    38  		return "", api.NewInstanceResponse(apimodel.Code_EmptyRequest, req)
    39  	}
    40  	if req.GetId() != nil {
    41  		if req.GetId().GetValue() == "" {
    42  			return "", api.NewInstanceResponse(apimodel.Code_InvalidInstanceID, req)
    43  		}
    44  		return req.GetId().GetValue(), nil
    45  	}
    46  	return utils.CheckInstanceTetrad(req)
    47  }
    48  
    49  const max404Count = 3
    50  
    51  func (s *Server) checkInstanceExists(ctx context.Context, id string) (int64, *model.Instance, apimodel.Code) {
    52  	ins := s.instanceCache.GetInstance(id)
    53  	if ins != nil {
    54  		return -1, ins, apimodel.Code_ExecuteSuccess
    55  	}
    56  	resp, err := s.defaultChecker.Query(ctx, &plugin.QueryRequest{
    57  		InstanceId: id,
    58  	})
    59  	if nil != err {
    60  		log.Errorf("[healthcheck]fail to query report count by id %s, err: %v", id, err)
    61  		return -1, nil, apimodel.Code_ExecuteSuccess
    62  	}
    63  	if resp.Count > max404Count {
    64  		return resp.Count, nil, apimodel.Code_NotFoundResource
    65  	}
    66  	return resp.Count, nil, apimodel.Code_ExecuteSuccess
    67  }
    68  
    69  func (s *Server) getHealthChecker(id string) plugin.HealthChecker {
    70  	insCache := s.cacheProvider.GetInstance(id)
    71  	if insCache == nil {
    72  		insCache = s.cacheProvider.GetSelfServiceInstance(id)
    73  	}
    74  	if insCache == nil {
    75  		return s.defaultChecker
    76  	}
    77  	checker, ok := s.checkers[int32(insCache.HealthCheck().GetType())]
    78  	if !ok {
    79  		return s.defaultChecker
    80  	}
    81  	return checker
    82  }
    83  
    84  func (s *Server) doReport(ctx context.Context, instance *apiservice.Instance) *apiservice.Response {
    85  	if !s.hcOpt.Open || len(s.checkers) == 0 {
    86  		return api.NewResponse(apimodel.Code_HealthCheckNotOpen)
    87  	}
    88  	id, errRsp := checkHeartbeatInstance(instance)
    89  	if errRsp != nil {
    90  		return errRsp
    91  	}
    92  	request := &plugin.ReportRequest{
    93  		QueryRequest: plugin.QueryRequest{
    94  			InstanceId: id,
    95  			Host:       instance.GetHost().GetValue(),
    96  			Port:       instance.GetPort().GetValue(),
    97  		},
    98  		LocalHost:  s.localHost,
    99  		CurTimeSec: time.Now().Unix() - s.timeAdjuster.GetDiff(),
   100  	}
   101  	code, err := s.baseReport(ctx, id, request)
   102  	if err != nil {
   103  		log.Errorf("[Heartbeat][Server] fail to do report for %s:%d, id is %s, err is %v",
   104  			instance.GetHost().GetValue(), instance.GetPort().GetValue(), id, err)
   105  		return api.NewInstanceResponse(apimodel.Code_HeartbeatException, instance)
   106  	}
   107  	return api.NewInstanceResponse(code, instance)
   108  }
   109  
   110  func (s *Server) doReports(ctx context.Context, beats []*apiservice.InstanceHeartbeat) *apiservice.Response {
   111  	if !s.hcOpt.Open || len(s.checkers) == 0 {
   112  		return api.NewResponse(apimodel.Code_HealthCheckNotOpen)
   113  	}
   114  	for i := range beats {
   115  		beat := beats[i]
   116  		request := &plugin.ReportRequest{
   117  			QueryRequest: plugin.QueryRequest{
   118  				InstanceId: beat.InstanceId,
   119  				Host:       beat.Host,
   120  				Port:       beat.Port,
   121  			},
   122  			LocalHost:  s.localHost,
   123  			CurTimeSec: time.Now().Unix() - s.timeAdjuster.GetDiff(),
   124  		}
   125  		code, err := s.baseReport(ctx, beat.InstanceId, request)
   126  		if err != nil {
   127  			log.Errorf("[Heartbeat][Server]fail to do report for %s:%d, id is %s, err is %v",
   128  				beat.GetHost(), beat.GetPort(), beat.GetInstanceId(), err)
   129  			return api.NewInstanceResponse(apimodel.Code_HeartbeatException, nil)
   130  		}
   131  		if code != apimodel.Code_ExecuteSuccess {
   132  			log.Warnf("[Heartbeat][Server] do report for %s:%d, id is %s, code is %v",
   133  				beat.GetHost(), beat.GetPort(), beat.GetInstanceId(), code)
   134  		}
   135  	}
   136  	return api.NewResponse(apimodel.Code_ExecuteSuccess)
   137  }
   138  
   139  func (s *Server) baseReport(ctx context.Context, id string, reportReq *plugin.ReportRequest) (apimodel.Code, error) {
   140  	count, ins, code := s.checkInstanceExists(ctx, id)
   141  	checker := s.getHealthChecker(id)
   142  	reportReq.Count = count + 1
   143  	err := checker.Report(ctx, reportReq)
   144  	if nil != ins {
   145  		event := &model.InstanceEvent{
   146  			Id:       id,
   147  			Instance: ins.Proto,
   148  			EType:    model.EventInstanceSendHeartbeat,
   149  		}
   150  		event.InjectMetadata(ctx)
   151  		s.publishInstanceEvent(ins.ServiceID, *event)
   152  	}
   153  	return code, err
   154  }