mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-06-03 06:30:42 +00:00
2c5a714903
* generate .pb.go * health checker conf * check logic * implement ping * fix check interval * improve check results * health check on add outbounds * fix tests * fix ping handler * fix min rtt < 0 * random alive * fix check all on add outbounds * least load strategy * conf codes optimize * improve least load strategy * improve health check on AddOutboundHandler * cleanup results with scheduler code optimize * health ping timeout default 5s * remove config of health ping round round 1 seems to be good enough * fix TestSimpleBalancer * add TestLeastLoadBalancer * add todos * lint and test fix * balancer fallback * api health stats command * add hc cmd to perform health checks * rename 'health stats' cmd to hci * many code optimizations * fix typo * select none if no match for baselines only config > prev 'select 1' behavior can achieved by baselines+expected=1 * add LeastLoadStrategy tests * don't select alive on no match, go to fallback * api hci refactor * more detailed info * ready for future new strategies * apply lint style * refactor: strategies don't need ref of balancer * change check interval unit to seconds > to reduce influence caused by what is described by new added FIXME * fix test * RouterService->RoutingService * Revert "generate .pb.go" This reverts commit 0e6fa1be889470d0ad9692f7279da45c030e1919. * make checks distributed > but `api hc` is the exception * BalancingStrategy interface optimize * fix random selects unchecked * upgrade cmd hci to bi & rename hc to bc * bi shows all balancers, while hci shows only heath-check-enabled ones * shows more info * fix test * api bi sort output * update according to review * remove checks on add outbound * refactor: move health checker inside to strategy * enables rounds setting for health ping * restore the random behavior, no ping, no pick alive > if future strategy based on HealthPing, just embed it like what LeastLoad does * apply lint style * code optimize * fix typo * update desc of bc bi * ping with head code optimize * force rouds to 1 if checks not distributed * leatload: select by standard deviations * health ping refactor * continuously applying results * config is easier to understand * checker interfaces simplifying * add maxRTT config to filter away high delay nodes * apply lint * cost for leastload * api bo to override balancer selecting * fix health ping statistics & fix test * check connectivity if ping fail * add tolerance setting & more detailed bi output * fix connectivity check * optimize bi output * should not put results when network is down * fixes @_@ * mux optimize * remove pause option of selecting overriding > it causes data racing * update bo desc * fix potential racing * simplify locking * switch sync.Mutex to avoid potential racing * add more tests * code optimize * code optimize * fix connectivity check when url not set
144 lines
3.2 KiB
Go
144 lines
3.2 KiB
Go
package router
|
|
|
|
import (
|
|
"math"
|
|
"time"
|
|
)
|
|
|
|
// HealthPingStats is the statistics of HealthPingRTTS
|
|
type HealthPingStats struct {
|
|
All int
|
|
Fail int
|
|
Deviation time.Duration
|
|
Average time.Duration
|
|
Max time.Duration
|
|
Min time.Duration
|
|
}
|
|
|
|
// HealthPingRTTS holds ping rtts for health Checker
|
|
type HealthPingRTTS struct {
|
|
idx int
|
|
cap int
|
|
validity time.Duration
|
|
rtts []*pingRTT
|
|
|
|
lastUpdateAt time.Time
|
|
stats *HealthPingStats
|
|
}
|
|
|
|
type pingRTT struct {
|
|
time time.Time
|
|
value time.Duration
|
|
}
|
|
|
|
// NewHealthPingResult returns a *HealthPingResult with specified capacity
|
|
func NewHealthPingResult(cap int, validity time.Duration) *HealthPingRTTS {
|
|
return &HealthPingRTTS{cap: cap, validity: validity}
|
|
}
|
|
|
|
// Get gets statistics of the HealthPingRTTS
|
|
func (h *HealthPingRTTS) Get() *HealthPingStats {
|
|
return h.getStatistics()
|
|
}
|
|
|
|
// GetWithCache get statistics and write cache for next call
|
|
// Make sure use Mutex.Lock() before calling it, RWMutex.RLock()
|
|
// is not an option since it writes cache
|
|
func (h *HealthPingRTTS) GetWithCache() *HealthPingStats {
|
|
lastPutAt := h.rtts[h.idx].time
|
|
now := time.Now()
|
|
if h.stats == nil || h.lastUpdateAt.Before(lastPutAt) || h.findOutdated(now) >= 0 {
|
|
h.stats = h.getStatistics()
|
|
h.lastUpdateAt = now
|
|
}
|
|
return h.stats
|
|
}
|
|
|
|
// Put puts a new rtt to the HealthPingResult
|
|
func (h *HealthPingRTTS) Put(d time.Duration) {
|
|
if h.rtts == nil {
|
|
h.rtts = make([]*pingRTT, h.cap)
|
|
for i := 0; i < h.cap; i++ {
|
|
h.rtts[i] = &pingRTT{}
|
|
}
|
|
h.idx = -1
|
|
}
|
|
h.idx = h.calcIndex(1)
|
|
now := time.Now()
|
|
h.rtts[h.idx].time = now
|
|
h.rtts[h.idx].value = d
|
|
}
|
|
|
|
func (h *HealthPingRTTS) calcIndex(step int) int {
|
|
idx := h.idx
|
|
idx += step
|
|
if idx >= h.cap {
|
|
idx %= h.cap
|
|
}
|
|
return idx
|
|
}
|
|
|
|
func (h *HealthPingRTTS) getStatistics() *HealthPingStats {
|
|
stats := &HealthPingStats{}
|
|
stats.Fail = 0
|
|
stats.Max = 0
|
|
stats.Min = rttFailed
|
|
sum := time.Duration(0)
|
|
cnt := 0
|
|
validRTTs := make([]time.Duration, 0)
|
|
for _, rtt := range h.rtts {
|
|
switch {
|
|
case rtt.value == 0 || time.Since(rtt.time) > h.validity:
|
|
continue
|
|
case rtt.value == rttFailed:
|
|
stats.Fail++
|
|
continue
|
|
}
|
|
cnt++
|
|
sum += rtt.value
|
|
validRTTs = append(validRTTs, rtt.value)
|
|
if stats.Max < rtt.value {
|
|
stats.Max = rtt.value
|
|
}
|
|
if stats.Min > rtt.value {
|
|
stats.Min = rtt.value
|
|
}
|
|
}
|
|
stats.All = cnt + stats.Fail
|
|
if cnt == 0 {
|
|
stats.Min = 0
|
|
return stats
|
|
}
|
|
stats.Average = time.Duration(int(sum) / cnt)
|
|
var std float64
|
|
if cnt < 2 {
|
|
// no enough data for standard deviation, we assume it's half of the average rtt
|
|
// if we don't do this, standard deviation of 1 round tested nodes is 0, will always
|
|
// selected before 2 or more rounds tested nodes
|
|
std = float64(stats.Average / 2)
|
|
} else {
|
|
variance := float64(0)
|
|
for _, rtt := range validRTTs {
|
|
variance += math.Pow(float64(rtt-stats.Average), 2)
|
|
}
|
|
std = math.Sqrt(variance / float64(cnt))
|
|
}
|
|
stats.Deviation = time.Duration(std)
|
|
return stats
|
|
}
|
|
|
|
func (h *HealthPingRTTS) findOutdated(now time.Time) int {
|
|
for i := h.cap - 1; i < 2*h.cap; i++ {
|
|
// from oldest to latest
|
|
idx := h.calcIndex(i)
|
|
validity := h.rtts[idx].time.Add(h.validity)
|
|
if h.lastUpdateAt.After(validity) {
|
|
return idx
|
|
}
|
|
if validity.Before(now) {
|
|
return idx
|
|
}
|
|
}
|
|
return -1
|
|
}
|