github.com/dbernstein1/tyk@v2.9.0-beta9-dl-apic+incompatible/gateway/api_healthcheck.go (about)

     1  package gateway
     2  
     3  import (
     4  	"strconv"
     5  	"strings"
     6  	"time"
     7  
     8  	"github.com/TykTechnologies/tyk/config"
     9  	"github.com/TykTechnologies/tyk/storage"
    10  )
    11  
    12  type HealthPrefix string
    13  
    14  const (
    15  	Throttle          HealthPrefix = "Throttle"
    16  	QuotaViolation    HealthPrefix = "QuotaViolation"
    17  	KeyFailure        HealthPrefix = "KeyFailure"
    18  	RequestLog        HealthPrefix = "Request"
    19  	BlockedRequestLog HealthPrefix = "BlockedRequest"
    20  )
    21  
    22  type HealthChecker interface {
    23  	Init(storage.Handler)
    24  	ApiHealthValues() (HealthCheckValues, error)
    25  	StoreCounterVal(HealthPrefix, string)
    26  }
    27  
    28  type HealthCheckValues struct {
    29  	ThrottledRequestsPS float64 `bson:"throttle_reqests_per_second,omitempty" json:"throttle_reqests_per_second"`
    30  	QuotaViolationsPS   float64 `bson:"quota_violations_per_second,omitempty" json:"quota_violations_per_second"`
    31  	KeyFailuresPS       float64 `bson:"key_failures_per_second,omitempty" json:"key_failures_per_second"`
    32  	AvgUpstreamLatency  float64 `bson:"average_upstream_latency,omitempty" json:"average_upstream_latency"`
    33  	AvgRequestsPS       float64 `bson:"average_requests_per_second,omitempty" json:"average_requests_per_second"`
    34  }
    35  
    36  type DefaultHealthChecker struct {
    37  	storage storage.Handler
    38  	APIID   string
    39  }
    40  
    41  func (h *DefaultHealthChecker) Init(storeType storage.Handler) {
    42  	if !config.Global().HealthCheck.EnableHealthChecks {
    43  		return
    44  	}
    45  
    46  	log.Info("Initializing HealthChecker")
    47  	h.storage = storeType
    48  	h.storage.Connect()
    49  }
    50  
    51  func (h *DefaultHealthChecker) CreateKeyName(subKey HealthPrefix) string {
    52  	// Key should be API-ID.SubKey.123456789
    53  	return h.APIID + "." + string(subKey)
    54  }
    55  
    56  // reportHealthValue is a shortcut we can use throughout the app to push a health check value
    57  func reportHealthValue(spec *APISpec, counter HealthPrefix, value string) {
    58  	if !spec.GlobalConfig.HealthCheck.EnableHealthChecks {
    59  		return
    60  	}
    61  
    62  	spec.Health.StoreCounterVal(counter, value)
    63  }
    64  
    65  func (h *DefaultHealthChecker) StoreCounterVal(counterType HealthPrefix, value string) {
    66  	searchStr := h.CreateKeyName(counterType)
    67  	log.Debug("Adding Healthcheck to: ", searchStr)
    68  	log.Debug("Val is: ", value)
    69  	//go h.storage.SetKey(searchStr, value, config.Global.HealthCheck.HealthCheckValueTimeout)
    70  	if value != "-1" {
    71  		// need to ensure uniqueness
    72  		now_string := strconv.Itoa(int(time.Now().UnixNano()))
    73  		value = now_string + "." + value
    74  		log.Debug("Set value to: ", value)
    75  	}
    76  	go h.storage.SetRollingWindow(searchStr, config.Global().HealthCheck.HealthCheckValueTimeout, value, false)
    77  }
    78  
    79  func (h *DefaultHealthChecker) getAvgCount(prefix HealthPrefix) float64 {
    80  	searchStr := h.CreateKeyName(prefix)
    81  	log.Debug("Searching for: ", searchStr)
    82  
    83  	count, _ := h.storage.SetRollingWindow(searchStr, config.Global().HealthCheck.HealthCheckValueTimeout, "-1", false)
    84  	log.Debug("Count is: ", count)
    85  	divisor := float64(config.Global().HealthCheck.HealthCheckValueTimeout)
    86  	if divisor == 0 {
    87  		log.Warning("The Health Check sample timeout is set to 0, samples will never be deleted!!!")
    88  		divisor = 60.0
    89  	}
    90  	if count > 0 {
    91  		return roundValue((float64(count) - 1) / divisor)
    92  	}
    93  
    94  	return 0.00
    95  }
    96  
    97  func roundValue(untruncated float64) float64 {
    98  	return float64(int(untruncated*100)) / 100
    99  }
   100  
   101  func (h *DefaultHealthChecker) ApiHealthValues() (HealthCheckValues, error) {
   102  	values := HealthCheckValues{}
   103  
   104  	// Get the counted / average values
   105  	values.ThrottledRequestsPS = h.getAvgCount(Throttle)
   106  	values.QuotaViolationsPS = h.getAvgCount(QuotaViolation)
   107  	values.KeyFailuresPS = h.getAvgCount(KeyFailure)
   108  	values.AvgRequestsPS = h.getAvgCount(RequestLog)
   109  
   110  	// Get the micro latency graph, an average upstream latency
   111  	searchStr := h.APIID + "." + string(RequestLog)
   112  	log.Debug("Searching KV for: ", searchStr)
   113  	_, vals := h.storage.SetRollingWindow(searchStr, config.Global().HealthCheck.HealthCheckValueTimeout, "-1", false)
   114  	log.Debug("Found: ", vals)
   115  	if len(vals) == 0 {
   116  		return values, nil
   117  	}
   118  	var runningTotal int
   119  	for _, v := range vals {
   120  		s := string(v.([]byte))
   121  		log.Debug("V is: ", s)
   122  		splitValues := strings.Split(s, ".")
   123  		if len(splitValues) > 1 {
   124  			vInt, err := strconv.Atoi(splitValues[1])
   125  			if err != nil {
   126  				log.Error("Couldn't convert tracked latency value to Int, vl is: ", err)
   127  			} else {
   128  				runningTotal += vInt
   129  			}
   130  		}
   131  
   132  	}
   133  	values.AvgUpstreamLatency = roundValue(float64(runningTotal / len(vals)))
   134  	return values, nil
   135  }