Repository: alibaba/sentinel-golang
Pull request #348 (2020-12-11 09:01:19)

Memory adaptive flow control
<!--  Thanks for submitting a pull request! Here are some tips for you:
1. Please make sure you have read and understood the contributing guidelines: https://github.com/alibaba/blob/master/CONTRIBUTING.md
2. Please make sure the PR has a corresponding issue.
-->

### Describe what this PR does / why we need it

adaptive memory slot

CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/alibaba/sentinel-golang?pullRequest=348) <br/>Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/alibaba/sentinel-golang?pullRequest=348) before we can accept your contribution.<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/alibaba/sentinel-golang?pullRequest=348) it.</sub>
codecov-io: # [Codecov](https://codecov.io/gh/alibaba/sentinel-golang/pull/348?src=pr&el=h1) Report
> Merging [#348](https://codecov.io/gh/alibaba/sentinel-golang/pull/348?src=pr&el=desc) (709e38e) into [master](https://codecov.io/gh/alibaba/sentinel-golang/commit/f854c9361dec7d8433aaf57c35ea66a8892bee94?el=desc) (f854c93) will **decrease** coverage by `0.17%`.
> The diff coverage is `39.65%`.

[![Impacted file tree graph](https://codecov.io/gh/alibaba/sentinel-golang/pull/348/graphs/tree.svg?width=650&height=150&src=pr&token=SrpzbhKA3E)](https://codecov.io/gh/alibaba/sentinel-golang/pull/348?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #348      +/-   ##
==========================================
- Coverage   50.83%   50.65%   -0.18%     
==========================================
  Files          69       69              
  Lines        3842     3895      +53     
==========================================
+ Hits         1953     1973      +20     
- Misses       1621     1653      +32     
- Partials      268      269       +1     
```


| [Impacted Files](https://codecov.io/gh/alibaba/sentinel-golang/pull/348?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [core/system/rule\_manager.go](https://codecov.io/gh/alibaba/sentinel-golang/pull/348/diff?src=pr&el=tree#diff-Y29yZS9zeXN0ZW0vcnVsZV9tYW5hZ2VyLmdv) | `80.39% <ø> (ø)` | |
| [core/system/sys\_stat.go](https://codecov.io/gh/alibaba/sentinel-golang/pull/348/diff?src=pr&el=tree#diff-Y29yZS9zeXN0ZW0vc3lzX3N0YXQuZ28=) | `38.46% <7.69%> (-7.70%)` | :arrow_down: |
| [core/flow/rule.go](https://codecov.io/gh/alibaba/sentinel-golang/pull/348/diff?src=pr&el=tree#diff-Y29yZS9mbG93L3J1bGUuZ28=) | `25.00% <30.30%> (+1.74%)` | :arrow_up: |
| [core/flow/rule\_manager.go](https://codecov.io/gh/alibaba/sentinel-golang/pull/348/diff?src=pr&el=tree#diff-Y29yZS9mbG93L3J1bGVfbWFuYWdlci5nbw==) | `64.74% <100.00%> (+1.45%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/alibaba/sentinel-golang/pull/348?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/alibaba/sentinel-golang/pull/348?src=pr&el=footer). Last update [f854c93...709e38e](https://codecov.io/gh/alibaba/sentinel-golang/pull/348?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

louyuting: @AlexStocks  Could you please fix the conflicts?
louyuting: @AlexStocks  Could you please fix the conflicts?
sczyh30: @AlexStocks Could you please sign the CLA here: https://cla-assistant.io/alibaba/sentinel-golang?pullRequest=348

Patch:
diff --git a/api/init.go b/api/init.go
index eac11b7..8128760 100644
--- a/api/init.go
+++ b/api/init.go
@@ -84,8 +84,29 @@ func initCoreComponents() error {
 		}
 	}
 
-	if config.SystemStatCollectIntervalMs() > 0 {
-		system_metric.InitCollector(config.SystemStatCollectIntervalMs())
+	systemStatInterval := config.SystemStatCollectIntervalMs()
+	loadStatInterval := systemStatInterval
+	cpuStatInterval := systemStatInterval
+	memStatInterval := systemStatInterval
+
+	if config.LoadStatCollectIntervalMs() > 0 {
+		loadStatInterval = config.LoadStatCollectIntervalMs()
+	}
+	if config.CpuStatCollectIntervalMs() > 0 {
+		cpuStatInterval = config.CpuStatCollectIntervalMs()
+	}
+	if config.MemoryStatCollectIntervalMs() > 0 {
+		memStatInterval = config.MemoryStatCollectIntervalMs()
+	}
+
+	if loadStatInterval > 0 {
+		system_metric.InitLoadCollector(loadStatInterval)
+	}
+	if cpuStatInterval > 0 {
+		system_metric.InitCpuCollector(cpuStatInterval)
+	}
+	if memStatInterval > 0 {
+		system_metric.InitMemoryCollector(memStatInterval)
 	}
 
 	if config.UseCacheTime() {
diff --git a/core/base/block_error.go b/core/base/block_error.go
index c110331..e72a7a7 100644
--- a/core/base/block_error.go
+++ b/core/base/block_error.go
@@ -65,6 +65,10 @@ func NewBlockErrorWithCause(blockType BlockType, blockMsg string, rule SentinelR
 }
 
 func (e *BlockError) Error() string {
+	if e == nil {
+		return "nil *BlockError"
+	}
+
 	if len(e.blockMsg) == 0 {
 		return fmt.Sprintf("SentinelBlockError: %s", e.blockType.String())
 	}
diff --git a/core/config/config.go b/core/config/config.go
index c56de97..243cd51 100644
--- a/core/config/config.go
+++ b/core/config/config.go
@@ -220,6 +220,18 @@ func SystemStatCollectIntervalMs() uint32 {
 	return globalCfg.SystemStatCollectIntervalMs()
 }
 
+func LoadStatCollectIntervalMs() uint32 {
+	return globalCfg.LoadStatCollectIntervalMs()
+}
+
+func CpuStatCollectIntervalMs() uint32 {
+	return globalCfg.CpuStatCollectIntervalMs()
+}
+
+func MemoryStatCollectIntervalMs() uint32 {
+	return globalCfg.MemoryStatCollectIntervalMs()
+}
+
 func UseCacheTime() bool {
 	return globalCfg.UseCacheTime()
 }
diff --git a/core/config/constant.go b/core/config/constant.go
index 58b1778..5f3c17a 100644
--- a/core/config/constant.go
+++ b/core/config/constant.go
@@ -32,5 +32,8 @@ const (
 	DefaultMetricLogSingleFileMaxSize  uint64 = 1024 * 1024 * 50
 	DefaultMetricLogMaxFileAmount      uint32 = 8
 	DefaultSystemStatCollectIntervalMs uint32 = 1000
+	DefaultLoadStatCollectIntervalMs   uint32 = 1000
+	DefaultCpuStatCollectIntervalMs    uint32 = 1000
+	DefaultMemoryStatCollectIntervalMs uint32 = 150
 	DefaultWarmUpColdFactor            uint32 = 3
 )
diff --git a/core/config/entity.go b/core/config/entity.go
index 1543f8e..845dad0 100644
--- a/core/config/entity.go
+++ b/core/config/entity.go
@@ -83,6 +83,12 @@ type StatConfig struct {
 type SystemStatConfig struct {
 	// CollectIntervalMs represents the collecting interval of the system metrics collector.
 	CollectIntervalMs uint32 `yaml:"collectIntervalMs"`
+	// CollectLoadIntervalMs represents the collecting interval of the system load collector.
+	CollectLoadIntervalMs uint32 `yaml:"collectLoadIntervalMs"`
+	// CollectCpuIntervalMs represents the collecting interval of the system cpu usage collector.
+	CollectCpuIntervalMs uint32 `yaml:"collectCpuIntervalMs"`
+	// CollectMemoryIntervalMs represents the collecting interval of the system memory usage collector.
+	CollectMemoryIntervalMs uint32 `yaml:"collectMemoryIntervalMs"`
 }
 
 // NewDefaultConfig creates a new default config entity.
@@ -113,7 +119,10 @@ func NewDefaultConfig() *Entity {
 				MetricStatisticSampleCount:      base.DefaultSampleCount,
 				MetricStatisticIntervalMs:       base.DefaultIntervalMs,
 				System: SystemStatConfig{
-					CollectIntervalMs: DefaultSystemStatCollectIntervalMs,
+					CollectIntervalMs:       DefaultSystemStatCollectIntervalMs,
+					CollectLoadIntervalMs:   DefaultLoadStatCollectIntervalMs,
+					CollectCpuIntervalMs:    DefaultCpuStatCollectIntervalMs,
+					CollectMemoryIntervalMs: DefaultMemoryStatCollectIntervalMs,
 				},
 			},
 			UseCacheTime: true,
@@ -197,6 +206,18 @@ func (entity *Entity) SystemStatCollectIntervalMs() uint32 {
 	return entity.Sentinel.Stat.System.CollectIntervalMs
 }
 
+func (entity *Entity) LoadStatCollectIntervalMs() uint32 {
+	return entity.Sentinel.Stat.System.CollectLoadIntervalMs
+}
+
+func (entity *Entity) CpuStatCollectIntervalMs() uint32 {
+	return entity.Sentinel.Stat.System.CollectCpuIntervalMs
+}
+
+func (entity *Entity) MemoryStatCollectIntervalMs() uint32 {
+	return entity.Sentinel.Stat.System.CollectMemoryIntervalMs
+}
+
 func (entity *Entity) UseCacheTime() bool {
 	return entity.Sentinel.UseCacheTime
 }
diff --git a/core/flow/rule.go b/core/flow/rule.go
index 3afbf24..983210f 100644
--- a/core/flow/rule.go
+++ b/core/flow/rule.go
@@ -47,6 +47,7 @@ type TokenCalculateStrategy int32
 const (
 	Direct TokenCalculateStrategy = iota
 	WarmUp
+	MemoryAdaptive
 )
 
 func (s TokenCalculateStrategy) String() string {
@@ -55,6 +56,8 @@ func (s TokenCalculateStrategy) String() string {
 		return "Direct"
 	case WarmUp:
 		return "WarmUp"
+	case MemoryAdaptive:
+		return "MemoryAdaptive"
 	default:
 		return "Undefined"
 	}
@@ -102,6 +105,16 @@ type Rule struct {
 	// If the StatIntervalInMs user specifies can not reuse the global statistic of resource,
 	// 		sentinel will generate independent statistic structure for this rule.
 	StatIntervalInMs uint32 `json:"statIntervalInMs"`
+
+	// adaptive flow control algorithm related parameters
+	// limitation: LowMemUsageThreshold > HighMemUsageThreshold && MemHighWaterMarkBytes > MemLowWaterMarkBytes
+	// if the current memory usage is less than or equals to MemLowWaterMarkBytes, threshold == LowMemUsageThreshold
+	// if the current memory usage is more than or equals to MemHighWaterMarkBytes, threshold == HighMemUsageThreshold
+	// if  the current memory usage is in (MemLowWaterMarkBytes, MemHighWaterMarkBytes), threshold is in (HighMemUsageThreshold, LowMemUsageThreshold)
+	LowMemUsageThreshold  int64 `json:"lowMemUsageThreshold"`
+	HighMemUsageThreshold int64 `json:"highMemUsageThreshold"`
+	MemLowWaterMarkBytes  int64 `json:"memLowWaterMarkBytes"`
+	MemHighWaterMarkBytes int64 `json:"memHighWaterMarkBytes"`
 }
 
 func (r *Rule) isEqualsTo(newRule *Rule) bool {
@@ -110,8 +123,13 @@ func (r *Rule) isEqualsTo(newRule *Rule) bool {
 	}
 	if !(r.Resource == newRule.Resource && r.RelationStrategy == newRule.RelationStrategy &&
 		r.RefResource == newRule.RefResource && r.StatIntervalInMs == newRule.StatIntervalInMs &&
-		r.TokenCalculateStrategy == newRule.TokenCalculateStrategy && r.ControlBehavior == newRule.ControlBehavior && util.Float64Equals(r.Threshold, newRule.Threshold) &&
-		r.MaxQueueingTimeMs == newRule.MaxQueueingTimeMs && r.WarmUpPeriodSec == newRule.WarmUpPeriodSec && r.WarmUpColdFactor == newRule.WarmUpColdFactor) {
+		r.TokenCalculateStrategy == newRule.TokenCalculateStrategy && r.ControlBehavior == newRule.ControlBehavior &&
+		util.Float64Equals(r.Threshold, newRule.Threshold) &&
+		r.MaxQueueingTimeMs == newRule.MaxQueueingTimeMs && r.WarmUpPeriodSec == newRule.WarmUpPeriodSec &&
+		r.WarmUpColdFactor == newRule.WarmUpColdFactor &&
+		r.LowMemUsageThreshold == newRule.LowMemUsageThreshold && r.HighMemUsageThreshold == newRule.HighMemUsageThreshold &&
+		r.MemLowWaterMarkBytes == newRule.MemLowWaterMarkBytes && r.MemHighWaterMarkBytes == newRule.MemHighWaterMarkBytes) {
+
 		return false
 	}
 	return true
@@ -127,7 +145,7 @@ func (r *Rule) isStatReusable(newRule *Rule) bool {
 }
 
 func (r *Rule) needStatistic() bool {
-	return !(r.TokenCalculateStrategy == Direct && r.ControlBehavior == Throttling)
+	return !((r.TokenCalculateStrategy == Direct && r.ControlBehavior == Throttling) || (r.TokenCalculateStrategy == MemoryAdaptive && r.ControlBehavior == Throttling))
 }
 
 func (r *Rule) String() string {
@@ -135,9 +153,11 @@ func (r *Rule) String() string {
 	if err != nil {
 		// Return the fallback string
 		return fmt.Sprintf("Rule{Resource=%s, TokenCalculateStrategy=%s, ControlBehavior=%s, "+
-			"Threshold=%.2f, RelationStrategy=%s, RefResource=%s, MaxQueueingTimeMs=%d, WarmUpPeriodSec=%d, WarmUpColdFactor=%d, StatIntervalInMs=%d}",
+			"Threshold=%.2f, RelationStrategy=%s, RefResource=%s, MaxQueueingTimeMs=%d, WarmUpPeriodSec=%d, WarmUpColdFactor=%d, StatIntervalInMs=%d, "+
+			"LowMemUsageThreshold=%v, HighMemUsageThreshold=%v, MemLowWaterMarkBytes=%v, MemHighWaterMarkBytes=%v}",
 			r.Resource, r.TokenCalculateStrategy, r.ControlBehavior, r.Threshold, r.RelationStrategy, r.RefResource,
-			r.MaxQueueingTimeMs, r.WarmUpPeriodSec, r.WarmUpColdFactor, r.StatIntervalInMs)
+			r.MaxQueueingTimeMs, r.WarmUpPeriodSec, r.WarmUpColdFactor, r.StatIntervalInMs,
+			r.LowMemUsageThreshold, r.HighMemUsageThreshold, r.MemLowWaterMarkBytes, r.MemHighWaterMarkBytes)
 	}
 	return string(b)
 }
diff --git a/core/flow/rule_manager.go b/core/flow/rule_manager.go
index 4d62ed1..2bec703 100644
--- a/core/flow/rule_manager.go
+++ b/core/flow/rule_manager.go
@@ -24,6 +24,7 @@ import (
 	"github.com/alibaba/sentinel-golang/core/misc"
 	"github.com/alibaba/sentinel-golang/core/stat"
 	sbase "github.com/alibaba/sentinel-golang/core/stat/base"
+	"github.com/alibaba/sentinel-golang/core/system_metric"
 	"github.com/alibaba/sentinel-golang/logging"
 	"github.com/alibaba/sentinel-golang/util"
 	"github.com/pkg/errors"
@@ -125,6 +126,38 @@ func init() {
 		tsc.flowChecker = NewThrottlingChecker(tsc, rule.MaxQueueingTimeMs, rule.StatIntervalInMs)
 		return tsc, nil
 	}
+	tcGenFuncMap[trafficControllerGenKey{
+		tokenCalculateStrategy: MemoryAdaptive,
+		controlBehavior:        Reject,
+	}] = func(rule *Rule, boundStat *standaloneStatistic) (*TrafficShapingController, error) {
+		if boundStat == nil {
+			var err error
+			boundStat, err = generateStatFor(rule)
+			if err != nil {
+				return nil, err
+			}
+		}
+		tsc, err := NewTrafficShapingController(rule, boundStat)
+		if err != nil || tsc == nil {
+			return nil, err
+		}
+		tsc.flowCalculator = NewMemoryAdaptiveTrafficShapingCalculator(tsc, rule)
+		tsc.flowChecker = NewRejectTrafficShapingChecker(tsc, rule)
+		return tsc, nil
+	}
+	tcGenFuncMap[trafficControllerGenKey{
+		tokenCalculateStrategy: MemoryAdaptive,
+		controlBehavior:        Throttling,
+	}] = func(rule *Rule, _ *standaloneStatistic) (*TrafficShapingController, error) {
+		// MemoryAdaptive token calculate strategy and throttling control behavior don't use stat, so we just give a nop stat.
+		tsc, err := NewTrafficShapingController(rule, nopStat)
+		if err != nil || tsc == nil {
+			return nil, err
+		}
+		tsc.flowCalculator = NewMemoryAdaptiveTrafficShapingCalculator(tsc, rule)
+		tsc.flowChecker = NewThrottlingChecker(tsc, rule.MaxQueueingTimeMs, rule.StatIntervalInMs)
+		return tsc, nil
+	}
 }
 
 func logRuleUpdate(m map[string][]*Rule) {
@@ -178,7 +211,7 @@ func onRuleUpdate(rules []*Rule) (err error) {
 	tcMux.RUnlock()
 
 	for res, rulesOfRes := range resRulesMap {
-		m[res] = buildRulesOfRes(res, rulesOfRes, tcMapClone)
+		m[res] = buildResourceTrafficShapingController(res, rulesOfRes, tcMapClone)
 	}
 
 	for res, tcs := range m {
@@ -424,13 +457,13 @@ func calculateReuseIndexFor(r *Rule, oldResTcs []*TrafficShapingController) (equ
 	return equalIdx, reuseStatIdx
 }
 
-// buildRulesOfRes builds TrafficShapingController slice from rules. the resource of rules must be equals to res
-func buildRulesOfRes(res string, rulesOfRes []*Rule, m TrafficControllerMap) []*TrafficShapingController {
+// buildResourceTrafficShapingController builds TrafficShapingController slice from rules. the resource of rules must be equals to res
+func buildResourceTrafficShapingController(res string, rulesOfRes []*Rule, m TrafficControllerMap) []*TrafficShapingController {
 	newTcsOfRes := make([]*TrafficShapingController, 0, len(rulesOfRes))
 	emptyTcs := make([]*TrafficShapingController, 0, 0)
 	for _, rule := range rulesOfRes {
 		if res != rule.Resource {
-			logging.Error(errors.Errorf("unmatched resource name expect: %s, actual: %s", res, rule.Resource), "Unmatched resource name in flow.buildRulesOfRes()", "rule", rule)
+			logging.Error(errors.Errorf("unmatched resource name expect: %s, actual: %s", res, rule.Resource), "Unmatched resource name in flow.buildResourceTrafficShapingController()", "rule", rule)
 			continue
 		}
 		oldResTcs, exist := m[res]
@@ -455,7 +488,7 @@ func buildRulesOfRes(res string, rulesOfRes []*Rule, m TrafficControllerMap) []*
 			controlBehavior:        rule.ControlBehavior,
 		}]
 		if !supported || generator == nil {
-			logging.Error(errors.New("unsupported flow control strategy"), "Ignoring the rule due to unsupported control behavior in flow.buildRulesOfRes()", "rule", rule)
+			logging.Error(errors.New("unsupported flow control strategy"), "Ignoring the rule due to unsupported control behavior in flow.buildResourceTrafficShapingController()", "rule", rule)
 			continue
 		}
 		var tc *TrafficShapingController
@@ -467,7 +500,7 @@ func buildRulesOfRes(res string, rulesOfRes []*Rule, m TrafficControllerMap) []*
 		}
 
 		if tc == nil || e != nil {
-			logging.Error(errors.New("bad generated traffic controller"), "Ignoring the rule due to bad generated traffic controller in flow.buildRulesOfRes()", "rule", rule)
+			logging.Error(errors.New("bad generated traffic controller"), "Ignoring the rule due to bad generated traffic controller in flow.buildResourceTrafficShapingController()", "rule", rule)
 			continue
 		}
 		if reuseStatIdx >= 0 {
@@ -513,5 +546,31 @@ func IsValidRule(rule *Rule) error {
 	if rule.StatIntervalInMs > 10*60*1000 {
 		logging.Info("StatIntervalInMs is great than 10 minutes, less than 10 minutes is recommended.")
 	}
+	if rule.TokenCalculateStrategy == MemoryAdaptive {
+		if rule.LowMemUsageThreshold <= 0 {
+			return errors.New("rule.LowMemUsageThreshold <= 0")
+		}
+		if rule.HighMemUsageThreshold <= 0 {
+			return errors.New("rule.HighMemUsageThreshold <= 0")
+		}
+		if rule.HighMemUsageThreshold >= rule.LowMemUsageThreshold {
+			return errors.New("rule.HighMemUsageThreshold >= rule.LowMemUsageThreshold")
+		}
+
+		if rule.MemLowWaterMarkBytes <= 0 {
+			return errors.New("rule.MemLowWaterMarkBytes <= 0")
+		}
+		if rule.MemHighWaterMarkBytes <= 0 {
+			return errors.New("rule.MemHighWaterMarkBytes <= 0")
+		}
+		if rule.MemHighWaterMarkBytes > int64(system_metric.TotalMemorySize) {
+			return errors.New("rule.MemHighWaterMarkBytes should not be greater than current system's total memory size")
+		}
+		if rule.MemLowWaterMarkBytes >= rule.MemHighWaterMarkBytes {
+			// can not be equal to defeat from zero overflow
+			return errors.New("rule.MemLowWaterMarkBytes >= rule.MemHighWaterMarkBytes")
+		}
+	}
+
 	return nil
 }
diff --git a/core/flow/tc_adaptive.go b/core/flow/tc_adaptive.go
new file mode 100644
index 0000000..da40a1e
--- /dev/null
+++ b/core/flow/tc_adaptive.go
@@ -0,0 +1,52 @@
+package flow
+
+import (
+	"github.com/alibaba/sentinel-golang/core/system_metric"
+	"github.com/alibaba/sentinel-golang/logging"
+)
+
+// MemoryAdaptiveTrafficShapingCalculator is a memory adaptive traffic shaping calculator
+//
+// adaptive flow control algorithm
+// If the watermark is less than Rule.MemLowWaterMarkBytes, the threshold is Rule.LowMemUsageThreshold.
+// If the watermark is greater than Rule.MemHighWaterMarkBytes, the threshold is Rule.HighMemUsageThreshold.
+// Otherwise, the threshold is ((watermark - MemLowWaterMarkBytes)/(MemHighWaterMarkBytes - MemLowWaterMarkBytes)) *
+//	(HighMemUsageThreshold - LowMemUsageThreshold) + LowMemUsageThreshold.
+type MemoryAdaptiveTrafficShapingCalculator struct {
+	owner                 *TrafficShapingController
+	lowMemUsageThreshold  int64
+	highMemUsageThreshold int64
+	memLowWaterMark       int64
+	memHighWaterMark      int64
+}
+
+func NewMemoryAdaptiveTrafficShapingCalculator(owner *TrafficShapingController, r *Rule) *MemoryAdaptiveTrafficShapingCalculator {
+	return &MemoryAdaptiveTrafficShapingCalculator{
+		owner:                 owner,
+		lowMemUsageThreshold:  r.LowMemUsageThreshold,
+		highMemUsageThreshold: r.HighMemUsageThreshold,
+		memLowWaterMark:       r.MemLowWaterMarkBytes,
+		memHighWaterMark:      r.MemHighWaterMarkBytes,
+	}
+}
+
+func (m *MemoryAdaptiveTrafficShapingCalculator) BoundOwner() *TrafficShapingController {
+	return m.owner
+}
+
+func (m *MemoryAdaptiveTrafficShapingCalculator) CalculateAllowedTokens(_ uint32, _ int32) float64 {
+	var threshold float64
+	mem := system_metric.CurrentMemoryUsage()
+	if mem == system_metric.NotRetrievedMemoryValue {
+		logging.Warn("[MemoryAdaptiveTrafficShapingCalculator CalculateAllowedTokens]Fail to load memory usage")
+		return float64(m.lowMemUsageThreshold)
+	}
+	if mem <= m.memLowWaterMark {
+		threshold = float64(m.lowMemUsageThreshold)
+	} else if mem >= m.memHighWaterMark {
+		threshold = float64(m.highMemUsageThreshold)
+	} else {
+		threshold = (float64(m.highMemUsageThreshold-m.lowMemUsageThreshold)/float64(m.memHighWaterMark-m.memLowWaterMark))*float64(mem-m.memLowWaterMark) + float64(m.lowMemUsageThreshold)
+	}
+	return threshold
+}
diff --git a/core/flow/traffic_shaping.go b/core/flow/traffic_shaping.go
index 17049bd..7399355 100644
--- a/core/flow/traffic_shaping.go
+++ b/core/flow/traffic_shaping.go
@@ -16,6 +16,7 @@ package flow
 
 import (
 	"github.com/alibaba/sentinel-golang/core/base"
+	"github.com/alibaba/sentinel-golang/metrics"
 )
 
 // TrafficShapingCalculator calculates the actual traffic shaping threshold
@@ -73,5 +74,6 @@ func (t *TrafficShapingController) FlowCalculator() TrafficShapingCalculator {
 
 func (t *TrafficShapingController) PerformChecking(resStat base.StatNode, batchCount uint32, flag int32) *base.TokenResult {
 	allowedTokens := t.flowCalculator.CalculateAllowedTokens(batchCount, flag)
+	metrics.SetResourceFlowThreshold(t.rule.Resource, allowedTokens)
 	return t.flowChecker.DoCheck(resStat, batchCount, allowedTokens)
 }
diff --git a/core/system_metric/sys_metric_stat.go b/core/system_metric/sys_metric_stat.go
index 408800c..a07a65e 100644
--- a/core/system_metric/sys_metric_stat.go
+++ b/core/system_metric/sys_metric_stat.go
@@ -15,50 +15,81 @@
 package system_metric
 
 import (
-	"math"
+	"os"
 	"sync"
 	"sync/atomic"
 	"time"
 
 	"github.com/alibaba/sentinel-golang/logging"
+	"github.com/alibaba/sentinel-golang/metrics"
 	"github.com/alibaba/sentinel-golang/util"
-	"github.com/shirou/gopsutil/v3/cpu"
 	"github.com/shirou/gopsutil/v3/load"
+	"github.com/shirou/gopsutil/v3/mem"
+	"github.com/shirou/gopsutil/v3/process"
 )
 
 const (
-	NotRetrievedValue float64 = -1.0
+	NotRetrievedLoadValue     float64 = -1.0
+	NotRetrievedCpuUsageValue float64 = -1.0
+	NotRetrievedMemoryValue   int64   = -1
 )
 
 var (
-	currentLoad     atomic.Value
-	currentCpuUsage atomic.Value
+	currentLoad        atomic.Value
+	currentCpuUsage    atomic.Value
+	currentMemoryUsage atomic.Value
 
-	prevCpuStat *cpu.TimesStat
-	initOnce    sync.Once
+	loadStatCollectorOnce   sync.Once
+	memoryStatCollectorOnce sync.Once
+	cpuStatCollectorOnce    sync.Once
+
+	CurrentPID         = os.Getpid()
+	currentProcess     atomic.Value
+	currentProcessOnce sync.Once
+	TotalMemorySize    = getTotalMemorySize()
 
 	ssStopChan = make(chan struct{})
 )
 
 func init() {
-	currentLoad.Store(NotRetrievedValue)
-	currentCpuUsage.Store(NotRetrievedValue)
+	currentLoad.Store(NotRetrievedLoadValue)
+	currentCpuUsage.Store(NotRetrievedCpuUsageValue)
+	currentMemoryUsage.Store(NotRetrievedMemoryValue)
+
+	p, err := process.NewProcess(int32(CurrentPID))
+	if err != nil {
+		logging.Error(err, "Fail to new process when initializing system metric", "pid", CurrentPID)
+		return
+	}
+	currentProcessOnce.Do(func() {
+		currentProcess.Store(p)
+	})
+}
+
+// getMemoryStat returns the current machine's memory statistic
+func getTotalMemorySize() (total uint64) {
+	stat, err := mem.VirtualMemory()
+	if err != nil {
+		logging.Error(err, "Fail to read Virtual Memory")
+		return 0
+	}
+	return stat.Total
 }
 
-func InitCollector(intervalMs uint32) {
+func InitMemoryCollector(intervalMs uint32) {
 	if intervalMs == 0 {
 		return
 	}
-	initOnce.Do(func() {
-		// Initial retrieval.
-		retrieveAndUpdateSystemStat()
+	memoryStatCollectorOnce.Do(func() {
+		// Initial memory retrieval.
+		retrieveAndUpdateMemoryStat()
 
 		ticker := util.NewTicker(time.Duration(intervalMs) * time.Millisecond)
 		go util.RunWithRecover(func() {
 			for {
 				select {
 				case <-ticker.C():
-					retrieveAndUpdateSystemStat()
+					retrieveAndUpdateMemoryStat()
 				case <-ssStopChan:
 					ticker.Stop()
 					return
@@ -68,63 +99,127 @@ func InitCollector(intervalMs uint32) {
 	})
 }
 
-func retrieveAndUpdateSystemStat() {
-	cpuStats, err := cpu.Times(false)
+func retrieveAndUpdateMemoryStat() {
+	memoryUsedBytes, err := GetProcessMemoryStat()
 	if err != nil {
-		logging.Warn("[retrieveAndUpdateSystemStat] Failed to retrieve current CPU usage", "err", err.Error())
+		logging.Error(err, "Fail to retrieve and update cpu statistic")
+		return
 	}
-	loadStat, err := load.Avg()
-	if err != nil {
-		logging.Warn("[retrieveAndUpdateSystemStat] Failed to retrieve current system load", "err", err.Error())
+	metrics.SetProcessMemorySize(memoryUsedBytes)
+	currentMemoryUsage.Store(memoryUsedBytes)
+}
+
+// GetProcessMemoryStat gets current process's memory usage in Bytes
+func GetProcessMemoryStat() (int64, error) {
+	curProcess := currentProcess.Load()
+	if curProcess == nil {
+		p, err := process.NewProcess(int32(CurrentPID))
+		if err != nil {
+			return 0, err
+		}
+		currentProcessOnce.Do(func() {
+			currentProcess.Store(p)
+		})
+		curProcess = currentProcess.Load()
 	}
-	if len(cpuStats) > 0 {
-		curCpuStat := &cpuStats[0]
-		recordCpuUsage(prevCpuStat, curCpuStat)
-		// Cache the latest CPU stat info.
-		prevCpuStat = curCpuStat
+	p := curProcess.(*process.Process)
+	memInfo, err := p.MemoryInfo()
+	var rss int64
+	if memInfo != nil {
+		rss = int64(memInfo.RSS)
 	}
-	if loadStat != nil {
-		currentLoad.Store(loadStat.Load1)
+
+	return rss, err
+}
+
+func InitCpuCollector(intervalMs uint32) {
+	if intervalMs == 0 {
+		return
+	}
+	cpuStatCollectorOnce.Do(func() {
+		// Initial memory retrieval.
+		retrieveAndUpdateCpuStat()
+
+		ticker := util.NewTicker(time.Duration(intervalMs) * time.Millisecond)
+		go util.RunWithRecover(func() {
+			for {
+				select {
+				case <-ticker.C():
+					retrieveAndUpdateCpuStat()
+				case <-ssStopChan:
+					ticker.Stop()
+					return
+				}
+			}
+		})
+	})
+}
+
+func retrieveAndUpdateCpuStat() {
+	cpuPercent, err := getProcessCpuStat()
+	if err != nil {
+		logging.Error(err, "Fail to retrieve and update cpu statistic")
+		return
 	}
+	metrics.SetCPURatio(cpuPercent)
+	currentCpuUsage.Store(cpuPercent)
 }
 
-func recordCpuUsage(prev, curCpuStat *cpu.TimesStat) {
-	if prev != nil && curCpuStat != nil {
-		prevTotal := calculateTotalCpuTick(prev)
-		curTotal := calculateTotalCpuTick(curCpuStat)
-
-		tDiff := curTotal - prevTotal
-		var cpuUsage float64
-		if tDiff == 0 {
-			cpuUsage = 0
-		} else {
-			prevUsed := calculateUserCpuTick(prev) + calculateKernelCpuTick(prev)
-			curUsed := calculateUserCpuTick(curCpuStat) + calculateKernelCpuTick(curCpuStat)
-			cpuUsage = (curUsed - prevUsed) / tDiff
-			cpuUsage = math.Max(0.0, cpuUsage)
-			cpuUsage = math.Min(1.0, cpuUsage)
+// getProcessCpuStat gets current process's memory usage in Bytes
+func getProcessCpuStat() (float64, error) {
+	curProcess := currentProcess.Load()
+	if curProcess == nil {
+		p, err := process.NewProcess(int32(CurrentPID))
+		if err != nil {
+			return 0, err
 		}
-		currentCpuUsage.Store(cpuUsage)
+		currentProcessOnce.Do(func() {
+			currentProcess.Store(p)
+		})
+		curProcess = currentProcess.Load()
 	}
+	p := curProcess.(*process.Process)
+	return p.Percent(0)
 }
 
-func calculateTotalCpuTick(stat *cpu.TimesStat) float64 {
-	return stat.User + stat.Nice + stat.System + stat.Idle +
-		stat.Iowait + stat.Irq + stat.Softirq + stat.Steal
-}
+func InitLoadCollector(intervalMs uint32) {
+	if intervalMs == 0 {
+		return
+	}
+	loadStatCollectorOnce.Do(func() {
+		// Initial retrieval.
+		retrieveAndUpdateLoadStat()
 
-func calculateUserCpuTick(stat *cpu.TimesStat) float64 {
-	return stat.User + stat.Nice
+		ticker := util.NewTicker(time.Duration(intervalMs) * time.Millisecond)
+		go util.RunWithRecover(func() {
+			for {
+				select {
+				case <-ticker.C():
+					retrieveAndUpdateLoadStat()
+				case <-ssStopChan:
+					ticker.Stop()
+					return
+				}
+			}
+		})
+	})
 }
 
-func calculateKernelCpuTick(stat *cpu.TimesStat) float64 {
-	return stat.System + stat.Irq + stat.Softirq
+func retrieveAndUpdateLoadStat() {
+	loadStat, err := load.Avg()
+	if err != nil {
+		logging.Error(err, "[retrieveAndUpdateSystemStat] Failed to retrieve current system load")
+		return
+	}
+	if loadStat != nil {
+		currentLoad.Store(loadStat.Load1)
+	}
 }
 
 func CurrentLoad() float64 {
 	r, ok := currentLoad.Load().(float64)
 	if !ok {
-		return NotRetrievedValue
+		return NotRetrievedLoadValue
 	}
 	return r
 }
@@ -137,7 +232,7 @@ func SetSystemLoad(load float64) {
 func CurrentCpuUsage() float64 {
 	r, ok := currentCpuUsage.Load().(float64)
 	if !ok {
-		return NotRetrievedValue
+		return NotRetrievedCpuUsageValue
 	}
 	return r
 }
@@ -146,3 +241,17 @@ func CurrentCpuUsage() float64 {
 func SetSystemCpuUsage(cpuUsage float64) {
 	currentCpuUsage.Store(cpuUsage)
 }
+
+func CurrentMemoryUsage() int64 {
+	bytes, ok := currentMemoryUsage.Load().(int64)
+	if !ok {
+		return NotRetrievedMemoryValue
+	}
+
+	return bytes
+}
+
+// Note: SetSystemCpuUsage is used for unit test, the user shouldn't call this function.
+func SetSystemMemoryUsage(memoryUsage int64) {
+	currentMemoryUsage.Store(memoryUsage)
+}
diff --git a/example/flow/memory_adaptive/memory_adaptive_qps_example.go b/example/flow/memory_adaptive/memory_adaptive_qps_example.go
new file mode 100644
index 0000000..27bb5f8
--- /dev/null
+++ b/example/flow/memory_adaptive/memory_adaptive_qps_example.go
@@ -0,0 +1,99 @@
+// Copyright 1999-2020 Alibaba Group Holding Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"log"
+	"math/rand"
+	"time"
+
+	sentinel "github.com/alibaba/sentinel-golang/api"
+	"github.com/alibaba/sentinel-golang/core/base"
+	"github.com/alibaba/sentinel-golang/core/config"
+	"github.com/alibaba/sentinel-golang/core/flow"
+	"github.com/alibaba/sentinel-golang/core/system_metric"
+	"github.com/alibaba/sentinel-golang/logging"
+)
+
+const resName = "example-memory-adaptive-qps-flow-resource"
+
+func main() {
+	// We should initialize Sentinel first.
+	conf := config.NewDefaultConfig()
+	// for testing, logging output to console
+	conf.Sentinel.Log.Logger = logging.NewConsoleLogger()
+	// use mock memory usage to replace actual memory usage, so close memory collector
+	conf.Sentinel.Stat.System.CollectIntervalMs = 0
+	conf.Sentinel.Stat.System.CollectMemoryIntervalMs = 0
+
+	err := sentinel.InitWithConfig(conf)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	_, err = flow.LoadRules([]*flow.Rule{
+		{
+			Resource:               resName,
+			TokenCalculateStrategy: flow.MemoryAdaptive,
+			ControlBehavior:        flow.Reject,
+			StatIntervalInMs:       1000,
+			LowMemUsageThreshold:   1000,
+			HighMemUsageThreshold:  100,
+			// bytes
+			MemLowWaterMarkBytes:  1024,
+			MemHighWaterMarkBytes: 2048,
+		},
+	})
+	if err != nil {
+		log.Fatalf("Unexpected error: %+v", err)
+		return
+	}
+
+	// mock memory usage is 1000 bytes, so QPS threshold should be 1000
+	system_metric.SetSystemMemoryUsage(999)
+	ch := make(chan struct{})
+	for i := 0; i < 10; i++ {
+		go func() {
+			for {
+				e, b := sentinel.Entry(resName, sentinel.WithTrafficType(base.Inbound))
+				if b != nil {
+					// Blocked. We could get the block reason from the BlockError.
+					time.Sleep(time.Duration(rand.Uint64()%2) * time.Millisecond)
+				} else {
+					// Passed, wrap the logic here.
+					time.Sleep(time.Duration(rand.Uint64()%2) * time.Millisecond)
+					// Be sure the entry is exited finally.
+					e.Exit()
+				}
+			}
+		}()
+	}
+
+	// Simulate a scenario in which flow rules are updated concurrently
+	go func() {
+		time.Sleep(time.Second * 5)
+		// mock memory usage is 1536 bytes, so QPS threshold should be 550
+		system_metric.SetSystemMemoryUsage(1536)
+
+		time.Sleep(time.Second * 5)
+		// mock memory usage is 1536 bytes, so QPS threshold should be 100
+		system_metric.SetSystemMemoryUsage(2048)
+
+		time.Sleep(time.Second * 5)
+		// mock memory usage is 1536 bytes, so QPS threshold should be 100
+		system_metric.SetSystemMemoryUsage(100000)
+	}()
+	<-ch
+}
diff --git a/go.mod b/go.mod
index 607860b..4d45839 100644
--- a/go.mod
+++ b/go.mod
@@ -6,8 +6,9 @@ require (
 	github.com/fsnotify/fsnotify v1.4.7
 	github.com/google/uuid v1.1.1
 	github.com/pkg/errors v0.9.1
+	github.com/prometheus/client_golang v1.9.0
 	github.com/shirou/gopsutil/v3 v3.20.11
 	github.com/stretchr/testify v1.6.1
 	go.uber.org/multierr v1.5.0
-	gopkg.in/yaml.v2 v2.2.8
+	gopkg.in/yaml.v2 v2.3.0
 )
diff --git a/go.sum b/go.sum
index 509236d..b00ee4f 100644
--- a/go.sum
+++ b/go.sum
@@ -1,69 +1,431 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
+github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
 github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
 github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
+github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
+github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
+github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
+github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
+github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
+github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
+github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
+github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
+github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
+github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
+github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
+github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
+github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
 github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
 github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
 github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
+github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
+github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
+github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
+github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
+github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
+github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
+github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
+github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
+github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
+github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
+github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
+github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
+github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
+github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
+github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
+github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
+github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
+github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
+github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
+github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
+github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
+github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_golang v1.9.0 h1:Rrch9mh17XcxvEu9D9DEpb4isxjGBtcevQjKvxPRQIU=
+github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM=
+github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
+github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/shirou/gopsutil/v3 v3.20.11 h1:NeVf1K0cgxsWz+N3671ojRptdgzvp7BXL3KV21R0JnA=
 github.com/shirou/gopsutil/v3 v3.20.11/go.mod h1:igHnfak0qnw1biGeI2qKQvu0ZkwvEkUcCLlYhZzdr/4=
-github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
+github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
+github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
+go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
+go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
 go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
 go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
 go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
 go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
 go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
 go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5 h1:iCaAy5bMeEvwANu3YnJfWwI0kWAGkEa2RXPdweI/ysk=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e h1:AyodaIpKjppX+cBfTASF2E1US3H2JFBj920Ot3rtDjs=
+golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
 golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200103221440-774c71fcf114 h1:DnSr2mCsxyCE6ZgIkmcWUQY2R5cH/6wL7eIxEmQOMSE=
+golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
diff --git a/metrics/metrics.go b/metrics/metrics.go
new file mode 100644
index 0000000..84f9d58
--- /dev/null
+++ b/metrics/metrics.go
@@ -0,0 +1,121 @@
+// Copyright 1999-2020 Alibaba Group Holding Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package metrics
+
+import (
+	"os"
+	"path/filepath"
+	"strconv"
+	"sync"
+
+	"github.com/prometheus/client_golang/prometheus"
+)
+
+var (
+	hostName, _ = os.Hostname()
+	psName      = filepath.Base(os.Args[0])
+	pid         = os.Getegid()
+	pidString   string
+
+	registerMetrics sync.Once
+)
+
+func init() {
+	if len(hostName) != 0 {
+		hostName = "host:" + hostName
+	}
+
+	if len(psName) == 0 {
+		psName = "ps:" + psName
+	}
+
+	if pid != 0 {
+		pidString = "pid:" + strconv.Itoa(pid)
+	}
+}
+
+var (
+	CPURatio = prometheus.NewGaugeVec(
+		prometheus.GaugeOpts{
+			Name: "sentinel_process_cpu_ratio",
+			Help: "current process cpu utilization ratio",
+		},
+		[]string{"host", "process", "cpu", "process_cpu_ratio"},
+	)
+	ProcessMemorySize = prometheus.NewGaugeVec(
+		prometheus.GaugeOpts{
+			Name: "sentinel_process_memory_size",
+			Help: "current process memory size in bytes",
+		},
+		[]string{"host", "process", "pid", "total_memory_size"},
+	)
+	ResourceFlowThreshold = prometheus.NewGaugeVec(
+		prometheus.GaugeOpts{
+			Name: "sentinel_resource_flow_threshold",
+			Help: "resource flow threshold",
+		},
+		[]string{"host", "resource", "threshold"},
+	)
+
+	metrics = []prometheus.Collector{
+		CPURatio,
+		ProcessMemorySize,
+		ResourceFlowThreshold,
+	}
+)
+
+// SetCPURatio sets the # of current process's cpu ratio
+func SetCPURatio(ratio float64) {
+	CPURatio.WithLabelValues(hostName, psName, pidString, "process_cpu_ratio").Set(ratio)
+}
+
+// SetProcessMemorySize sets the # of current process's memory
+func SetProcessMemorySize(size int64) {
+	ProcessMemorySize.WithLabelValues(hostName, psName, pidString, "total_memory_size").Set(float64(size))
+}
+
+// SetResourceFlowThreshold sets the # of resource threshold
+func SetResourceFlowThreshold(resource string, threshold float64) {
+	if len(resource) != 0 {
+		resource = "rs:" + resource
+	}
+
+	ResourceFlowThreshold.WithLabelValues(hostName, resource, "threshold").Set(threshold)
+}
+
+func RegisterSentinelMetrics(registry *prometheus.Registry) {
+	if registry == nil {
+		return
+	}
+	registerMetrics.Do(func() {
+		for _, metric := range metrics {
+			registry.MustRegister(metric)
+		}
+	})
+}
+
+type resettable interface {
+	Reset()
+}
+
+// Reset all metrics to zero
+func ResetSentinelMetrics() {
+	for _, metric := range metrics {
+		rm, ok := metric.(resettable)
+		if ok {
+			rm.Reset()
+		}
+	}
+}


Test patch:
diff --git a/core/flow/rule_manager_test.go b/core/flow/rule_manager_test.go
index b9e3fac..6eb59fd 100644
--- a/core/flow/rule_manager_test.go
+++ b/core/flow/rule_manager_test.go
@@ -245,8 +245,8 @@ func Test_generateStatFor(t *testing.T) {
 	})
 }
 
-func Test_buildRulesOfRes(t *testing.T) {
-	t.Run("Test_buildRulesOfRes_no_reuse_stat", func(t *testing.T) {
+func Test_buildResourceTrafficShapingController(t *testing.T) {
+	t.Run("Test_buildResourceTrafficShapingController_no_reuse_stat", func(t *testing.T) {
 		r1 := &Rule{
 			Resource:               "abc1",
 			Threshold:              100,
@@ -263,7 +263,7 @@ func Test_buildRulesOfRes(t *testing.T) {
 			MaxQueueingTimeMs:      10,
 		}
 		assert.True(t, len(tcMap["abc1"]) == 0)
-		tcs := buildRulesOfRes("abc1", []*Rule{r1, r2}, tcMap)
+		tcs := buildResourceTrafficShapingController("abc1", []*Rule{r1, r2}, tcMap)
 		assert.True(t, len(tcs) == 2)
 		assert.True(t, tcs[0].BoundRule() == r1)
 		assert.True(t, tcs[1].BoundRule() == r2)
@@ -271,7 +271,7 @@ func Test_buildRulesOfRes(t *testing.T) {
 		assert.True(t, reflect.DeepEqual(tcs[1].BoundRule(), r2))
 	})
 
-	t.Run("Test_buildRulesOfRes_reuse_stat", func(t *testing.T) {
+	t.Run("Test_buildResourceTrafficShapingController_reuse_stat", func(t *testing.T) {
 		// use nop statistics because of no need statistics
 		r0 := &Rule{
 			Resource:               "abc1",
@@ -413,7 +413,7 @@ func Test_buildRulesOfRes(t *testing.T) {
 			StatIntervalInMs:       50000,
 		}
 
-		tcs := buildRulesOfRes("abc1", []*Rule{r12, r22, r32, r42}, tcMap)
+		tcs := buildResourceTrafficShapingController("abc1", []*Rule{r12, r22, r32, r42}, tcMap)
 		assert.True(t, len(tcs) == 4)
 
 		assert.True(t, tcs[0].BoundRule() == r12)
@@ -456,3 +456,40 @@ func TestLoadRules(t *testing.T) {
 		assert.False(t, ok)
 	})
 }
+
+func TestIsValidRule(t *testing.T) {
+	rule1 := &Rule{
+		Resource:               "hello0",
+		TokenCalculateStrategy: MemoryAdaptive,
+		ControlBehavior:        Reject,
+		StatIntervalInMs:       10,
+		LowMemUsageThreshold:   2,
+		HighMemUsageThreshold:  1,
+		MemLowWaterMarkBytes:   1,
+		MemHighWaterMarkBytes:  2,
+	}
+	assert.Nil(t, IsValidRule(rule1))
+
+	rule1.LowMemUsageThreshold = 9
+	rule1.HighMemUsageThreshold = 9
+	assert.NotNil(t, IsValidRule(rule1))
+	rule1.LowMemUsageThreshold = 10
+	assert.Nil(t, IsValidRule(rule1))
+
+	rule1.MemLowWaterMarkBytes = 0
+	assert.NotNil(t, IsValidRule(rule1))
+	rule1.MemLowWaterMarkBytes = 100 * 1024 * 1024
+	rule1.MemHighWaterMarkBytes = 300 * 1024 * 1024
+	assert.Nil(t, IsValidRule(rule1))
+
+	rule1.MemHighWaterMarkBytes = 0
+	assert.NotNil(t, IsValidRule(rule1))
+	rule1.MemHighWaterMarkBytes = 300 * 1024 * 1024
+	assert.Nil(t, IsValidRule(rule1))
+
+	rule1.MemLowWaterMarkBytes = 100 * 1024 * 1024
+	rule1.MemHighWaterMarkBytes = 30 * 1024 * 1024
+	assert.NotNil(t, IsValidRule(rule1))
+	rule1.MemHighWaterMarkBytes = 300 * 1024 * 1024
+	assert.Nil(t, IsValidRule(rule1))
+}
diff --git a/core/flow/tc_adaptive_test.go b/core/flow/tc_adaptive_test.go
new file mode 100644
index 0000000..d7fb6d3
--- /dev/null
+++ b/core/flow/tc_adaptive_test.go
@@ -0,0 +1,29 @@
+package flow
+
+import (
+	"testing"
+
+	"github.com/alibaba/sentinel-golang/core/system_metric"
+	"github.com/alibaba/sentinel-golang/util"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestMemoryAdaptiveTrafficShapingCalculator_CalculateAllowedTokens(t *testing.T) {
+	tc1 := &MemoryAdaptiveTrafficShapingCalculator{
+		owner:                 nil,
+		lowMemUsageThreshold:  1000,
+		highMemUsageThreshold: 100,
+		memLowWaterMark:       1024,
+		memHighWaterMark:      2048,
+	}
+	system_metric.SetSystemMemoryUsage(100)
+	assert.True(t, util.Float64Equals(tc1.CalculateAllowedTokens(0, 0), float64(tc1.lowMemUsageThreshold)))
+	system_metric.SetSystemMemoryUsage(1024)
+	assert.True(t, util.Float64Equals(tc1.CalculateAllowedTokens(0, 0), float64(tc1.lowMemUsageThreshold)))
+	system_metric.SetSystemMemoryUsage(1536)
+	assert.True(t, util.Float64Equals(tc1.CalculateAllowedTokens(0, 0), 550))
+	system_metric.SetSystemMemoryUsage(2048)
+	assert.True(t, util.Float64Equals(tc1.CalculateAllowedTokens(0, 0), 100))
+	system_metric.SetSystemMemoryUsage(3072)
+	assert.True(t, util.Float64Equals(tc1.CalculateAllowedTokens(0, 0), 100))
+}
diff --git a/core/system/slot_test.go b/core/system/slot_test.go
index 47ed852..aa918aa 100644
--- a/core/system/slot_test.go
+++ b/core/system/slot_test.go
@@ -81,7 +81,7 @@ func TestDoCheckRuleLoad(t *testing.T) {
 
 	t.Run("TrueLoad", func(t *testing.T) {
 		isOK, _, v := sas.doCheckRule(rule)
-		assert.True(t, util.Float64Equals(system_metric.NotRetrievedValue, v))
+		assert.True(t, util.Float64Equals(system_metric.NotRetrievedLoadValue, v))
 		assert.Equal(t, true, isOK)
 	})
 
@@ -91,7 +91,7 @@ func TestDoCheckRuleLoad(t *testing.T) {
 		isOK, _, v := sas.doCheckRule(rule)
 		assert.Equal(t, true, isOK)
 		assert.True(t, util.Float64Equals(float64(1.0), v))
-		system_metric.SetSystemLoad(system_metric.NotRetrievedValue)
+		system_metric.SetSystemLoad(system_metric.NotRetrievedLoadValue)
 	})
 }
 
@@ -104,7 +104,7 @@ func TestDoCheckRuleCpuUsage(t *testing.T) {
 
 	t.Run("TrueCpuUsage", func(t *testing.T) {
 		isOK, _, v := sas.doCheckRule(rule)
-		assert.True(t, util.Float64Equals(system_metric.NotRetrievedValue, v))
+		assert.True(t, util.Float64Equals(system_metric.NotRetrievedCpuUsageValue, v))
 		assert.Equal(t, true, isOK)
 	})
 
@@ -114,7 +114,7 @@ func TestDoCheckRuleCpuUsage(t *testing.T) {
 		isOK, _, v := sas.doCheckRule(rule)
 		assert.Equal(t, true, isOK)
 		assert.True(t, util.Float64Equals(0.8, v))
-		system_metric.SetSystemCpuUsage(system_metric.NotRetrievedValue)
+		system_metric.SetSystemCpuUsage(system_metric.NotRetrievedCpuUsageValue)
 	})
 }
 
diff --git a/core/system_metric/sys_metric_stat_test.go b/core/system_metric/sys_metric_stat_test.go
index ce0a82a..e3a6cdf 100644
--- a/core/system_metric/sys_metric_stat_test.go
+++ b/core/system_metric/sys_metric_stat_test.go
@@ -15,58 +15,19 @@
 package system_metric
 
 import (
+	"sync"
 	"testing"
+	"time"
 
 	"github.com/alibaba/sentinel-golang/util"
-	"github.com/shirou/gopsutil/v3/cpu"
 	"github.com/stretchr/testify/assert"
 )
 
-func Test_recordCpuUsage(t *testing.T) {
-	defer currentCpuUsage.Store(NotRetrievedValue)
-
-	var emptyStat *cpu.TimesStat = nil
-	// total: 2260, user+nice: 950, system+irqs=210
-	prev := &cpu.TimesStat{
-		CPU:     "all",
-		User:    900,
-		System:  200,
-		Idle:    300,
-		Nice:    50,
-		Iowait:  100,
-		Irq:     5,
-		Softirq: 5,
-		Steal:   700,
-	}
-	// total: 4180, user+nice: 1600, system+irqs=430
-	cur := &cpu.TimesStat{
-		CPU:     "all",
-		User:    1500,
-		System:  400,
-		Idle:    400,
-		Nice:    100,
-		Iowait:  150,
-		Irq:     15,
-		Softirq: 15,
-		Steal:   1600,
-	}
-	expected := float64(1600+430-950-210) / (4180 - 2260)
-
-	recordCpuUsage(emptyStat, cur)
-	assert.True(t, util.Float64Equals(NotRetrievedValue, CurrentCpuUsage()))
-
-	recordCpuUsage(prev, prev)
-	assert.True(t, util.Float64Equals(0.0, CurrentCpuUsage()))
-
-	recordCpuUsage(prev, cur)
-	assert.InEpsilon(t, expected, CurrentCpuUsage(), 0.001)
-}
-
 func TestCurrentLoad(t *testing.T) {
-	defer currentLoad.Store(NotRetrievedValue)
+	defer currentLoad.Store(NotRetrievedLoadValue)
 
 	cLoad := CurrentLoad()
-	assert.True(t, util.Float64Equals(NotRetrievedValue, cLoad))
+	assert.True(t, util.Float64Equals(NotRetrievedLoadValue, cLoad))
 
 	v := float64(1.0)
 	currentLoad.Store(v)
@@ -75,13 +36,50 @@ func TestCurrentLoad(t *testing.T) {
 }
 
 func TestCurrentCpuUsage(t *testing.T) {
-	defer currentCpuUsage.Store(NotRetrievedValue)
+	defer currentCpuUsage.Store(NotRetrievedCpuUsageValue)
 
 	cpuUsage := CurrentCpuUsage()
-	assert.Equal(t, NotRetrievedValue, cpuUsage)
+	assert.Equal(t, NotRetrievedCpuUsageValue, cpuUsage)
 
 	v := float64(0.3)
 	currentCpuUsage.Store(v)
 	cpuUsage = CurrentCpuUsage()
 	assert.True(t, util.Float64Equals(v, cpuUsage))
 }
+
+func Test_getProcessCpuStat(t *testing.T) {
+	wg := &sync.WaitGroup{}
+	wg.Add(1)
+	go func() {
+		i := 0
+		wg.Done()
+		for i < 10000000000 {
+			i++
+			if i == 1000000000 {
+				i = 0
+			}
+		}
+	}()
+	wg.Wait()
+
+	got, err := getProcessCpuStat()
+	if err != nil {
+		t.Error(err)
+	}
+	assert.True(t, int(got) == 0)
+	time.Sleep(time.Millisecond * 200)
+
+	got, err = getProcessCpuStat()
+	if err != nil {
+		t.Error(err)
+	}
+	assert.True(t, int(got) > 0)
+	time.Sleep(time.Millisecond * 200)
+
+	got, err = getProcessCpuStat()
+	if err != nil {
+		t.Error(err)
+	}
+	assert.True(t, int(got) > 0)
+	time.Sleep(time.Millisecond * 200)
+}
diff --git a/tests/api/api_entry_integration_test.go b/tests/api/api_entry_integration_test.go
new file mode 100644
index 0000000..6ede278
--- /dev/null
+++ b/tests/api/api_entry_integration_test.go
@@ -0,0 +1,129 @@
+package api
+
+import (
+	"log"
+	"runtime/debug"
+	"testing"
+	"time"
+
+	"github.com/alibaba/sentinel-golang/api"
+	"github.com/alibaba/sentinel-golang/core/base"
+	"github.com/alibaba/sentinel-golang/core/config"
+	"github.com/alibaba/sentinel-golang/core/flow"
+	"github.com/alibaba/sentinel-golang/core/system_metric"
+	"github.com/alibaba/sentinel-golang/logging"
+	"github.com/alibaba/sentinel-golang/util"
+	"github.com/stretchr/testify/assert"
+)
+
+func initSentinel() {
+	// We should initialize Sentinel first.
+	conf := config.NewDefaultConfig()
+	// for testing, logging output to console
+	conf.Sentinel.Log.Logger = logging.NewConsoleLogger()
+	conf.Sentinel.Log.Metric.FlushIntervalSec = 0
+	conf.Sentinel.Stat.System.CollectIntervalMs = 0
+	conf.Sentinel.Stat.System.CollectMemoryIntervalMs = 0
+	conf.Sentinel.Stat.System.CollectCpuIntervalMs = 0
+	conf.Sentinel.Stat.System.CollectLoadIntervalMs = 0
+	err := api.InitWithConfig(conf)
+	if err != nil {
+		log.Fatal(err)
+	}
+}
+
+func TestAdaptiveFlowControl(t *testing.T) {
+	initSentinel()
+	util.SetClock(util.NewMockClock())
+
+	rs := "hello0"
+	rule := flow.Rule{
+		Resource:               rs,
+		TokenCalculateStrategy: flow.MemoryAdaptive,
+		ControlBehavior:        flow.Reject,
+		StatIntervalInMs:       1000,
+		LowMemUsageThreshold:   5,
+		HighMemUsageThreshold:  1,
+		MemLowWaterMarkBytes:   1 * 1024,
+		MemHighWaterMarkBytes:  2 * 1024,
+	}
+	rule1 := rule
+	ok, err := flow.LoadRules([]*flow.Rule{&rule1})
+	assert.True(t, ok)
+	assert.Nil(t, err)
+
+	// mock memory usage < MemLowWaterMarkBytes, QPS threshold is 2
+	system_metric.SetSystemMemoryUsage(512)
+	for i := 0; i < 5; i++ {
+		entry, blockError := api.Entry(rs, api.WithTrafficType(base.Inbound))
+		assert.Nil(t, blockError)
+		if blockError != nil {
+			t.Errorf("entry error:%+v", blockError)
+		}
+		entry.Exit()
+	}
+	_, blockError := api.Entry(rs, api.WithTrafficType(base.Inbound))
+	assert.NotNil(t, blockError)
+	if blockError != nil {
+		t.Logf("entry error:%+v, caused: %+v", blockError.Error(), blockError.TriggeredRule())
+	}
+
+	// clear statistic
+	util.Sleep(time.Second * 2)
+	// QPS threshold is 3
+	system_metric.SetSystemMemoryUsage(1536)
+	for i := 0; i < 3; i++ {
+		entry, blockError := api.Entry(rs, api.WithTrafficType(base.Inbound))
+		assert.Nil(t, blockError)
+		if blockError != nil {
+			t.Errorf("entry error:%+v", blockError)
+		}
+		entry.Exit()
+	}
+	_, blockError = api.Entry(rs, api.WithTrafficType(base.Inbound))
+	assert.NotNil(t, blockError)
+	if blockError != nil {
+		t.Logf("entry error:%+v, caused: %+v", blockError.Error(), blockError.TriggeredRule())
+	}
+
+	// clear statistic
+	util.Sleep(time.Second * 2)
+	t.Log("start to test memory based adaptive flow control")
+	// QPS threshold is 3
+	system_metric.SetSystemMemoryUsage(2049)
+	for i := 0; i < 1; i++ {
+		entry, blockError := api.Entry(rs, api.WithTrafficType(base.Inbound))
+		assert.Nil(t, blockError)
+		if blockError != nil {
+			t.Errorf("entry error:%+v", blockError)
+		}
+		entry.Exit()
+	}
+	_, blockError = api.Entry(rs, api.WithTrafficType(base.Inbound))
+	assert.NotNil(t, blockError)
+	if blockError != nil {
+		t.Logf("entry error:%+v, caused: %+v", blockError.Error(), blockError.TriggeredRule())
+	}
+}
+
+func TestAdaptiveFlowControl2(t *testing.T) {
+	debug.SetGCPercent(-1)
+	initSentinel()
+	rs := "hello0"
+	rule := flow.Rule{
+		Resource:               rs,
+		TokenCalculateStrategy: flow.MemoryAdaptive,
+		ControlBehavior:        flow.Reject,
+		StatIntervalInMs:       1000,
+		LowMemUsageThreshold:   150,
+		HighMemUsageThreshold:  10,
+		MemLowWaterMarkBytes:   100998840320,
+		MemHighWaterMarkBytes:  268435456000,
+	}
+	ok, err := flow.LoadRules([]*flow.Rule{&rule})
+	assert.True(t, ok)
+	assert.Nil(t, err)
+	system_metric.SetSystemMemoryUsage(136794800128)
+	_, blockError := api.Entry(rs, api.WithTrafficType(base.Inbound))
+	assert.Nil(t, blockError)
+}
diff --git a/tests/benchmark/env_init.go b/tests/benchmark/env_init.go
index 228aeb2..c531e0b 100644
--- a/tests/benchmark/env_init.go
+++ b/tests/benchmark/env_init.go
@@ -29,6 +29,9 @@ func InitSentinel() {
 	conf.Sentinel.Log.Logger = logging.NewConsoleLogger()
 	conf.Sentinel.Log.Metric.FlushIntervalSec = 0
 	conf.Sentinel.Stat.System.CollectIntervalMs = 0
+	conf.Sentinel.Stat.System.CollectMemoryIntervalMs = 0
+	conf.Sentinel.Stat.System.CollectCpuIntervalMs = 0
+	conf.Sentinel.Stat.System.CollectLoadIntervalMs = 0
 	err := sentinel.InitWithConfig(conf)
 	if err != nil {
 		log.Fatal(err)