github.com/polarismesh/polaris@v1.17.8/plugin/statis/base/apicall.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 base
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"math"
    24  	"sync"
    25  	"time"
    26  
    27  	"github.com/polarismesh/polaris/common/log"
    28  	"github.com/polarismesh/polaris/common/metrics"
    29  )
    30  
    31  // APICall 接口调用
    32  type APICall struct {
    33  	Api       string
    34  	Code      int
    35  	Duration  int64
    36  	Protocol  string
    37  	Component metrics.CallMetricType
    38  }
    39  
    40  // APICallStatisItem 接口调用统计条目
    41  type APICallStatisItem struct {
    42  	API          string
    43  	Code         int
    44  	Count        int64
    45  	AccTime      int64
    46  	MinTime      int64
    47  	MaxTime      int64
    48  	Protocol     string
    49  	ZeroDuration int64 // 没有请求持续的时间,持续时间长超过阈值从 prometheus 中移除掉
    50  }
    51  
    52  func (item *APICallStatisItem) String() string {
    53  	if item.Count == 0 {
    54  		return ""
    55  	}
    56  	return fmt.Sprintf("%-48v|%12v|%12v|%12.3f|%12.3f|%12.3f|\n",
    57  		item.API, item.Code, item.Count,
    58  		float64(item.MinTime)/1e6,
    59  		float64(item.MaxTime)/1e6,
    60  		float64(item.AccTime)/float64(item.Count)/1e6,
    61  	)
    62  }
    63  
    64  // ComponentStatics statics components
    65  type ComponentStatics struct {
    66  	t       metrics.CallMetricType
    67  	acc     chan *APICall
    68  	mutex   sync.Mutex
    69  	statis  map[string]*APICallStatisItem
    70  	handler MetricsHandler
    71  }
    72  
    73  func NewComponentStatics(ctx context.Context, t metrics.CallMetricType, handler MetricsHandler) *ComponentStatics {
    74  	c := &ComponentStatics{
    75  		t:       t,
    76  		acc:     make(chan *APICall, 1024),
    77  		statis:  make(map[string]*APICallStatisItem),
    78  		handler: handler,
    79  	}
    80  	go c.run(ctx)
    81  	return c
    82  }
    83  
    84  // add 添加接口调用数据
    85  func (a *ComponentStatics) Add(ac *APICall) {
    86  	startTime := time.Now()
    87  	a.acc <- ac
    88  	passDuration := time.Since(startTime)
    89  	if passDuration >= MaxAddDuration {
    90  		log.Warnf("[APICall]add api call cost %s, exceed max %s", passDuration, MaxAddDuration)
    91  	}
    92  }
    93  
    94  // add 添加接口调用数据
    95  func (a *ComponentStatics) run(ctx context.Context) {
    96  	for {
    97  		select {
    98  		case <-ctx.Done():
    99  			return
   100  		case item := <-a.acc:
   101  			a.add(item)
   102  		}
   103  	}
   104  }
   105  
   106  func (c *ComponentStatics) add(ac *APICall) {
   107  	c.mutex.Lock()
   108  	defer c.mutex.Unlock()
   109  	index := fmt.Sprintf("%v-%v", ac.Api, ac.Code)
   110  	item, exist := c.statis[index]
   111  	if exist {
   112  		item.Count++
   113  
   114  		item.AccTime += ac.Duration
   115  		if ac.Duration < item.MinTime {
   116  			item.MinTime = ac.Duration
   117  		}
   118  		if ac.Duration > item.MaxTime {
   119  			item.MaxTime = ac.Duration
   120  		}
   121  	} else {
   122  		c.statis[index] = &APICallStatisItem{
   123  			API:      ac.Api,
   124  			Code:     ac.Code,
   125  			Count:    1,
   126  			AccTime:  ac.Duration,
   127  			MinTime:  ac.Duration,
   128  			MaxTime:  ac.Duration,
   129  			Protocol: ac.Protocol,
   130  		}
   131  	}
   132  }
   133  
   134  // collect log and print the statics messages
   135  func (c *ComponentStatics) deal() {
   136  	startTime := time.Now()
   137  	if len(c.statis) == 0 {
   138  		c.mutex.Lock()
   139  		defer c.mutex.Unlock()
   140  		c.handler(c.t, startTime, nil)
   141  		return
   142  	}
   143  
   144  	c.mutex.Lock()
   145  	defer func() {
   146  		c.mutex.Unlock()
   147  		passDuration := time.Since(startTime)
   148  		if passDuration >= MaxLogWaitDuration {
   149  			log.Warnf("[APICall]api static log duration %s, pass max %s", passDuration, MaxLogWaitDuration)
   150  		}
   151  	}()
   152  
   153  	duplicateStatis := make([]*APICallStatisItem, 0, len(c.statis))
   154  	currStatis := c.statis
   155  	c.statis = make(map[string]*APICallStatisItem)
   156  
   157  	for key, item := range currStatis {
   158  		duplicateStatis = append(duplicateStatis, item)
   159  		if item.Count == 0 {
   160  			item.ZeroDuration++
   161  			item.MinTime = 0
   162  		} else {
   163  			item.ZeroDuration = 0
   164  		}
   165  
   166  		if item.ZeroDuration <= MaxZeroDuration {
   167  			c.statis[key] = &APICallStatisItem{
   168  				API:          item.API,
   169  				Code:         item.Code,
   170  				Count:        0,
   171  				Protocol:     item.Protocol,
   172  				AccTime:      0,
   173  				MinTime:      math.MaxInt64,
   174  				MaxTime:      0,
   175  				ZeroDuration: item.ZeroDuration,
   176  			}
   177  		}
   178  	}
   179  
   180  	go func() {
   181  		c.mutex.Lock()
   182  		defer c.mutex.Unlock()
   183  		c.handler(c.t, startTime, duplicateStatis)
   184  	}()
   185  }