Repository: alibaba/sentinel-golang
Pull request #374 (2021-01-06 13:02:48)

support resource level rule replacement in circuitBreaker module
<!--  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
Support resource level rule replacement in circuitBreaker module.

### Does this pull request fix one issue?

<!--If that, add "Fixes #xxxx" below in the next line. For example, Fixes #15. Otherwise, add "NONE" -->

### Describe how you did it


### Describe how to verify it


### Special notes for reviews


Patch:
diff --git a/core/circuitbreaker/rule_manager.go b/core/circuitbreaker/rule_manager.go
index fac3147..6b2ef91 100644
--- a/core/circuitbreaker/rule_manager.go
+++ b/core/circuitbreaker/rule_manager.go
@@ -32,8 +32,8 @@ var (
 
 	breakerRules  = make(map[string][]*Rule)
 	breakers      = make(map[string][]CircuitBreaker)
-	updateMux     = &sync.RWMutex{}
-	currentRules  = make([]*Rule, 0)
+	updateMux     = new(sync.RWMutex)
+	currentRules  = make(map[string][]*Rule, 0)
 	updateRuleMux = new(sync.Mutex)
 
 	stateChangeListeners = make([]StateChangeListener, 0)
@@ -132,16 +132,54 @@ func ClearRules() error {
 // bool: was designed to indicate whether the internal map has been changed
 // error: was designed to indicate whether occurs the error.
 func LoadRules(rules []*Rule) (bool, error) {
-	// TODO in order to avoid invalid update, should check consistent with last update rules
+	resRulesMap := make(map[string][]*Rule, 16)
+	for _, rule := range rules {
+		resRules, exist := resRulesMap[rule.Resource]
+		if !exist {
+			resRules = make([]*Rule, 0, 1)
+		}
+		resRulesMap[rule.Resource] = append(resRules, rule)
+	}
+
 	updateRuleMux.Lock()
 	defer updateRuleMux.Unlock()
-	isEqual := reflect.DeepEqual(currentRules, rules)
+	isEqual := reflect.DeepEqual(currentRules, resRulesMap)
 	if isEqual {
 		logging.Info("[CircuitBreaker] Load rules is the same with current rules, so ignore load operation.")
 		return false, nil
 	}
 
-	err := onRuleUpdate(rules)
+	err := onRuleUpdate(resRulesMap)
+	return true, err
+}
+
+// LoadRulesOfResource loads the given resource's circuitBreaker rules to the rule manager, while all previous resource's rules will be replaced.
+// the first returned value indicates whether do real load operation, if the rules is the same with previous resource's rules, return false
+func LoadRulesOfResource(res string, rules []*Rule) (bool, error) {
+	if len(res) == 0 {
+		return false, errors.New("empty resource")
+	}
+	updateRuleMux.Lock()
+	defer updateRuleMux.Unlock()
+	// clear resource rules
+	if len(rules) == 0 {
+		// clear resource's currentRules
+		delete(currentRules, res)
+		// clear breakers & breakerRules
+		updateMux.Lock()
+		delete(breakers, res)
+		delete(breakerRules, res)
+		updateMux.Unlock()
+		logging.Info("[CircuitBreaker] clear resource level rules", "resource", res)
+		return true, nil
+	}
+	// load resource level rules
+	isEqual := reflect.DeepEqual(currentRules[res], rules)
+	if isEqual {
+		logging.Info("[CircuitBreaker] Load resource level rules is the same with current resource level rules, so ignore load operation.")
+		return false, nil
+	}
+	err := onResourceRuleUpdate(res, rules)
 	return true, err
 }
 
@@ -183,18 +221,8 @@ func calculateReuseIndexFor(r *Rule, oldResCbs []CircuitBreaker) (equalIdx, reus
 	return equalIdx, reuseStatIdx
 }
 
-func insertCbToCbMap(cb CircuitBreaker, res string, m map[string][]CircuitBreaker) {
-	cbsOfRes, exists := m[res]
-	if !exists {
-		cbsOfRes = make([]CircuitBreaker, 0, 1)
-		m[res] = append(cbsOfRes, cb)
-	} else {
-		m[res] = append(cbsOfRes, cb)
-	}
-}
-
 // Concurrent safe to update rules
-func onRuleUpdate(rules []*Rule) (err error) {
+func onRuleUpdate(rawResRulesMap map[string][]*Rule) (err error) {
 	defer func() {
 		if r := recover(); r != nil {
 			var ok bool
@@ -204,36 +232,26 @@ func onRuleUpdate(rules []*Rule) (err error) {
 			}
 		}
 	}()
-
-	newBreakerRules := make(map[string][]*Rule, len(rules))
-	for _, rule := range rules {
-		if rule == nil {
-			continue
-		}
-		if err := IsValid(rule); err != nil {
-			logging.Warn("[CircuitBreaker onRuleUpdate] Ignoring invalid circuit breaking rule when loading new rules", "rule", rule, "err", err.Error())
-			continue
+	// ignore invalid rules
+	validResRulesMap := make(map[string][]*Rule, len(rawResRulesMap))
+	for res, rules := range rawResRulesMap {
+		validResRules := make([]*Rule, 0, len(rules))
+		for _, rule := range rules {
+			if err := IsValidRule(rule); err != nil {
+				logging.Warn("[CircuitBreaker onRuleUpdate] Ignoring invalid circuit breaking rule when loading new rules", "rule", rule, "err", err.Error())
+				continue
+			}
+			validResRules = append(validResRules, rule)
 		}
-
-		classification := rule.ResourceName()
-		ruleSet, ok := newBreakerRules[classification]
-		if !ok {
-			ruleSet = make([]*Rule, 0, 1)
+		if len(validResRules) > 0 {
+			validResRulesMap[res] = validResRules
 		}
-		ruleSet = append(ruleSet, rule)
-		newBreakerRules[classification] = ruleSet
-	}
-
-	newBreakers := make(map[string][]CircuitBreaker, len(rules))
-	// in order to avoid growing, build newBreakers in advance
-	for res, rules := range newBreakerRules {
-		newBreakers[res] = make([]CircuitBreaker, 0, len(rules))
 	}
 
 	start := util.CurrentTimeNano()
 
 	updateMux.RLock()
-	breakersClone := make(map[string][]CircuitBreaker, len(newBreakerRules))
+	breakersClone := make(map[string][]CircuitBreaker, len(validResRulesMap))
 	for res, tcs := range breakers {
 		resTcClone := make([]CircuitBreaker, 0, len(tcs))
 		resTcClone = append(resTcClone, tcs...)
@@ -241,66 +259,75 @@ func onRuleUpdate(rules []*Rule) (err error) {
 	}
 	updateMux.RUnlock()
 
-	for res, resRules := range newBreakerRules {
-		emptyCircuitBreakerList := make([]CircuitBreaker, 0, 0)
-		for _, r := range resRules {
-			oldResCbs := breakersClone[res]
-			if oldResCbs == nil {
-				oldResCbs = emptyCircuitBreakerList
-			}
-			equalIdx, reuseStatIdx := calculateReuseIndexFor(r, oldResCbs)
-
-			// First check equals scenario
-			if equalIdx >= 0 {
-				// reuse the old cb
-				equalOldCb := oldResCbs[equalIdx]
-				insertCbToCbMap(equalOldCb, res, newBreakers)
-				// remove old cb from oldResCbs
-				breakersClone[res] = append(oldResCbs[:equalIdx], oldResCbs[equalIdx+1:]...)
-				continue
-			}
+	newBreakers := make(map[string][]CircuitBreaker, len(validResRulesMap))
+	for res, resRules := range validResRulesMap {
+		newCbsOfRes := buildResourceCircuitBreaker(res, resRules, breakersClone[res])
+		if len(newCbsOfRes) > 0 {
+			newBreakers[res] = newCbsOfRes
+			// update resource slot chain
+			misc.RegisterRuleCheckSlotForResource(res, DefaultSlot)
+			misc.RegisterStatSlotForResource(res, DefaultMetricStatSlot)
+		}
+	}
 
-			generator := cbGenFuncMap[r.Strategy]
-			if generator == nil {
-				logging.Warn("[CircuitBreaker onRuleUpdate] Ignoring the rule due to unsupported circuit breaking strategy", "rule", r)
-				continue
-			}
+	updateMux.Lock()
+	breakerRules = validResRulesMap
+	breakers = newBreakers
+	updateMux.Unlock()
+	currentRules = rawResRulesMap
 
-			var cb CircuitBreaker
-			var e error
-			if reuseStatIdx >= 0 {
-				cb, e = generator(r, oldResCbs[reuseStatIdx].BoundStat())
-			} else {
-				cb, e = generator(r, nil)
-			}
-			if cb == nil || e != nil {
-				logging.Warn("[CircuitBreaker onRuleUpdate] Ignoring the rule due to bad generated circuit breaker", "rule", r, "err", e.Error())
-				continue
-			}
+	logging.Debug("[CircuitBreaker onRuleUpdate] Time statistics(ns) for updating circuit breaker rule", "timeCost", util.CurrentTimeNano()-start)
+	logRuleUpdate(validResRulesMap)
+	return nil
+}
 
-			if reuseStatIdx >= 0 {
-				breakersClone[res] = append(oldResCbs[:reuseStatIdx], oldResCbs[reuseStatIdx+1:]...)
+func onResourceRuleUpdate(res string, rawResRules []*Rule) (err error) {
+	defer func() {
+		if r := recover(); r != nil {
+			var ok bool
+			err, ok = r.(error)
+			if !ok {
+				err = fmt.Errorf("%v", r)
 			}
-			insertCbToCbMap(cb, res, newBreakers)
 		}
-	}
+	}()
 
-	for res, cbs := range newBreakers {
-		if len(cbs) > 0 {
-			// update resource slot chain
-			misc.RegisterRuleCheckSlotForResource(res, DefaultSlot)
-			misc.RegisterStatSlotForResource(res, DefaultMetricStatSlot)
+	validResRules := make([]*Rule, 0, len(rawResRules))
+	for _, rule := range rawResRules {
+		if err := IsValidRule(rule); err != nil {
+			logging.Warn("[CircuitBreaker onResourceRuleUpdate] Ignoring invalid circuitBreaker rule", "rule", rule, "reason", err.Error())
+			continue
 		}
+		validResRules = append(validResRules, rule)
+	}
+
+	start := util.CurrentTimeNano()
+	oldResCbs := make([]CircuitBreaker, 0)
+	updateMux.RLock()
+	oldResCbs = append(oldResCbs, breakers[res]...)
+	updateMux.RUnlock()
+
+	newCbsOfRes := buildResourceCircuitBreaker(res, rawResRules, oldResCbs)
+
+	if len(newCbsOfRes) > 0 {
+		// update resource slot chain
+		misc.RegisterRuleCheckSlotForResource(res, DefaultSlot)
+		misc.RegisterStatSlotForResource(res, DefaultMetricStatSlot)
 	}
 
 	updateMux.Lock()
-	breakerRules = newBreakerRules
-	breakers = newBreakers
-	currentRules = rules
+	if len(newCbsOfRes) == 0 {
+		delete(breakerRules, res)
+		delete(breakers, res)
+	} else {
+		breakerRules[res] = validResRules
+		breakers[res] = newCbsOfRes
+	}
 	updateMux.Unlock()
+	currentRules[res] = rawResRules
 
-	logging.Debug("[CircuitBreaker onRuleUpdate] Time statistics(ns) for updating circuit breaker rule", "timeCost", util.CurrentTimeNano()-start)
-	logRuleUpdate(newBreakerRules)
+	logging.Debug("[CircuitBreaker onResourceRuleUpdate] Time statistics(ns) for updating circuit breaker rule", "timeCost", util.CurrentTimeNano()-start)
+	logging.Info("[CircuitBreaker] load resource level rules", "resource", res, "validResRules", validResRules)
 	return nil
 }
 
@@ -374,7 +401,62 @@ func RemoveCircuitBreakerGenerator(s Strategy) error {
 	return nil
 }
 
-func IsValid(r *Rule) error {
+// ClearRulesOfResource clears resource level rules in circuitBreaker module.
+func ClearRulesOfResource(res string) error {
+	_, err := LoadRulesOfResource(res, nil)
+	return err
+}
+
+// buildResourceCircuitBreaker builds CircuitBreaker slice from rules. the resource of rules must be equals to res
+func buildResourceCircuitBreaker(res string, rulesOfRes []*Rule, oldResCbs []CircuitBreaker) []CircuitBreaker {
+	newCbsOfRes := make([]CircuitBreaker, 0, len(rulesOfRes))
+	for _, r := range rulesOfRes {
+		if res != r.Resource {
+			logging.Error(errors.Errorf("unmatched resource name expect: %s, actual: %s", res, r.Resource), "Unmatched resource name in circuitBreaker.buildResourceCircuitBreaker()", "rule", r)
+			continue
+		}
+		equalIdx, reuseStatIdx := calculateReuseIndexFor(r, oldResCbs)
+
+		// First check equals scenario
+		if equalIdx >= 0 {
+			// reuse the old cb
+			equalOldCb := oldResCbs[equalIdx]
+			newCbsOfRes = append(newCbsOfRes, equalOldCb)
+			// remove old cb from oldResCbs
+			oldResCbs = append(oldResCbs[:equalIdx], oldResCbs[equalIdx+1:]...)
+			continue
+		}
+
+		generator := cbGenFuncMap[r.Strategy]
+		if generator == nil {
+			logging.Warn("[CircuitBreaker buildResourceCircuitBreaker] Ignoring the rule due to unsupported circuit breaking strategy", "rule", r)
+			continue
+		}
+
+		var cb CircuitBreaker
+		var e error
+		if reuseStatIdx >= 0 {
+			cb, e = generator(r, oldResCbs[reuseStatIdx].BoundStat())
+		} else {
+			cb, e = generator(r, nil)
+		}
+		if cb == nil || e != nil {
+			logging.Warn("[CircuitBreaker buildResourceCircuitBreaker] Ignoring the rule due to bad generated circuit breaker", "rule", r, "err", e.Error())
+			continue
+		}
+
+		if reuseStatIdx >= 0 {
+			oldResCbs = append(oldResCbs[:reuseStatIdx], oldResCbs[reuseStatIdx+1:]...)
+		}
+		newCbsOfRes = append(newCbsOfRes, cb)
+	}
+	return newCbsOfRes
+}
+
+func IsValidRule(r *Rule) error {
+	if r == nil {
+		return errors.New("nil Rule")
+	}
 	if len(r.Resource) == 0 {
 		return errors.New("empty resource name")
 	}
@@ -394,7 +476,7 @@ func IsValid(r *Rule) error {
 		return errors.New("invalid error ratio threshold (valid range: [0.0, 1.0])")
 	}
 	if r.StatSlidingWindowBucketCount != 0 && r.StatIntervalMs%r.StatSlidingWindowBucketCount != 0 {
-		logging.Warn("[CircuitBreaker IsValid] The following must be true: StatIntervalMs % StatSlidingWindowBucketCount == 0. StatSlidingWindowBucketCount will be replaced by 1", "rule", r)
+		logging.Warn("[CircuitBreaker IsValidRule] The following must be true: StatIntervalMs % StatSlidingWindowBucketCount == 0. StatSlidingWindowBucketCount will be replaced by 1", "rule", r)
 	}
 	return nil
 }
diff --git a/core/hotspot/rule_manager.go b/core/hotspot/rule_manager.go
index ee383c1..3c89b3d 100644
--- a/core/hotspot/rule_manager.go
+++ b/core/hotspot/rule_manager.go
@@ -35,7 +35,7 @@ var (
 	tcGenFuncMap  = make(map[ControlBehavior]TrafficControllerGenFunc, 4)
 	tcMap         = make(trafficControllerMap)
 	tcMux         = new(sync.RWMutex)
-	currentRules  = make([]*Rule, 0)
+	currentRules  = make(map[string][]*Rule, 0)
 	updateRuleMux = new(sync.Mutex)
 )
 
@@ -82,27 +82,36 @@ func getTrafficControllersFor(res string) []TrafficShapingController {
 	return tcMap[res]
 }
 
-// LoadRules replaces old rules with the given hotspot parameter flow control rules. Return value:
-//
-// bool: indicates whether the internal map has been changed;
-// error: indicates whether occurs the error.
+// LoadRules replaces all old hotspot param flow rules with the given rules.
+// Return value:
+//   bool: indicates whether the internal map has been changed;
+//   error: indicates whether occurs the error.
 func LoadRules(rules []*Rule) (bool, error) {
+	resRulesMap := make(map[string][]*Rule, 16)
+	for _, rule := range rules {
+		resRules, exists := resRulesMap[rule.Resource]
+		if !exists {
+			resRules = make([]*Rule, 0, 1)
+		}
+		resRulesMap[rule.Resource] = append(resRules, rule)
+	}
+
 	updateRuleMux.Lock()
 	defer updateRuleMux.Unlock()
-	isEqual := reflect.DeepEqual(currentRules, rules)
+	isEqual := reflect.DeepEqual(currentRules, resRulesMap)
 	if isEqual {
 		logging.Info("[HotSpot] Load rules is the same with current rules, so ignore load operation.")
 		return false, nil
 	}
 
-	err := onRuleUpdate(rules)
+	err := onRuleUpdate(resRulesMap)
 	return true, err
 }
 
-// GetRules returns all the rules based on copy.
-// It doesn't take effect for hotspot module if user changes the rule.
+// GetRules returns all the hotspot param flow rules based on copy.
+// It doesn't take effect for hotspot module if user changes the returned rules.
 // GetRules need to compete hotspot module's global lock and the high performance losses of copy,
-// 		reduce or do not call GetRules if possible
+// 		reduce or do not call GetRules if possible.
 func GetRules() []Rule {
 	tcMux.RLock()
 	rules := rulesFrom(tcMap)
@@ -115,10 +124,10 @@ func GetRules() []Rule {
 	return ret
 }
 
-// GetRulesOfResource returns specific resource's rules based on copy.
-// It doesn't take effect for hotspot module if user changes the rule.
+// GetRulesOfResource returns specific resource's hotspot parameter flow control rules based on copy.
+// It doesn't take effect for hotspot module if user changes the returned rules.
 // GetRulesOfResource need to compete hotspot module's global lock and the high performance losses of copy,
-// 		reduce or do not call GetRulesOfResource frequently if possible
+// 		reduce or do not call GetRulesOfResource frequently if possible.
 func GetRulesOfResource(res string) []Rule {
 	tcMux.RLock()
 	resTcs := tcMap[res]
@@ -131,13 +140,19 @@ func GetRulesOfResource(res string) []Rule {
 	return ret
 }
 
-// ClearRules clears all parameter flow rules.
+// ClearRules clears all hotspot param flow rules.
 func ClearRules() error {
 	_, err := LoadRules(nil)
 	return err
 }
 
-func onRuleUpdate(rules []*Rule) (err error) {
+// ClearRulesOfResource clears resource level hotspot param flow rules.
+func ClearRulesOfResource(res string) error {
+	_, err := LoadRulesOfResource(res, nil)
+	return err
+}
+
+func onRuleUpdate(rawResRulesMap map[string][]*Rule) (err error) {
 	defer func() {
 		if r := recover(); r != nil {
 			var ok bool
@@ -148,24 +163,20 @@ func onRuleUpdate(rules []*Rule) (err error) {
 		}
 	}()
 
-	newRuleMap := make(map[string][]*Rule, len(rules))
-	for _, r := range rules {
-		if err := IsValidRule(r); err != nil {
-			logging.Warn("[HotSpot onRuleUpdate] Ignoring invalid hotspot rule when loading new rules", "rule", r, "err", err.Error())
-			continue
+	// ignore invalid rules
+	validResRulesMap := make(map[string][]*Rule, len(rawResRulesMap))
+	for res, rules := range rawResRulesMap {
+		validResRules := make([]*Rule, 0, len(rules))
+		for _, rule := range rules {
+			if err := IsValidRule(rule); err != nil {
+				logging.Warn("[HotSpot onRuleUpdate] Ignoring invalid hotspot param flow rule when loading new rules", "rule", rule, "err", err.Error())
+				continue
+			}
+			validResRules = append(validResRules, rule)
 		}
-		res := r.ResourceName()
-		ruleSet, ok := newRuleMap[res]
-		if !ok {
-			ruleSet = make([]*Rule, 0, 1)
+		if len(validResRules) > 0 {
+			validResRulesMap[res] = validResRules
 		}
-		ruleSet = append(ruleSet, r)
-		newRuleMap[res] = ruleSet
-	}
-
-	m := make(trafficControllerMap)
-	for res, rules := range newRuleMap {
-		m[res] = make([]TrafficShapingController, 0, len(rules))
 	}
 
 	start := util.CurrentTimeNano()
@@ -179,49 +190,11 @@ func onRuleUpdate(rules []*Rule) (err error) {
 	}
 	tcMux.RUnlock()
 
-	for res, resRules := range newRuleMap {
-		emptyTcList := make([]TrafficShapingController, 0, 0)
-		for _, r := range resRules {
-			oldResTcs := tcMapClone[res]
-			if oldResTcs == nil {
-				oldResTcs = emptyTcList
-			}
-
-			equalIdx, reuseStatIdx := calculateReuseIndexFor(r, oldResTcs)
-			// there is equivalent rule in old traffic shaping controller slice
-			if equalIdx >= 0 {
-				equalOldTC := oldResTcs[equalIdx]
-				insertTcToTcMap(equalOldTC, res, m)
-				// remove old tc from old resTcs
-				tcMapClone[res] = append(oldResTcs[:equalIdx], oldResTcs[equalIdx+1:]...)
-				continue
-			}
-
-			// generate new traffic shaping controller
-			generator, supported := tcGenFuncMap[r.ControlBehavior]
-			if !supported {
-				logging.Warn("[HotSpot onRuleUpdate] Ignoring the frequent param flow rule due to unsupported control behavior", "rule", r)
-				continue
-			}
-			var tc TrafficShapingController
-			if reuseStatIdx >= 0 {
-				// generate new traffic shaping controller with reusable statistic metric.
-				tc = generator(r, oldResTcs[reuseStatIdx].BoundMetric())
-			} else {
-				tc = generator(r, nil)
-			}
-			if tc == nil {
-				logging.Debug("[HotSpot onRuleUpdate] Ignoring the frequent param flow rule due to bad generated traffic controller", "rule", r)
-				continue
-			}
-
-			//  remove the reused traffic shaping controller old res tcs
-			if reuseStatIdx >= 0 {
-				tcMapClone[res] = append(oldResTcs[:reuseStatIdx], oldResTcs[reuseStatIdx+1:]...)
-			}
-			insertTcToTcMap(tc, res, m)
-		}
+	m := make(trafficControllerMap, len(validResRulesMap))
+	for res, rules := range validResRulesMap {
+		m[res] = buildResourceTrafficShapingController(res, rules, tcMapClone[res])
 	}
+
 	for res, tcs := range m {
 		if len(tcs) > 0 {
 			// update resource slot chain
@@ -237,14 +210,102 @@ func onRuleUpdate(rules []*Rule) (err error) {
 
 	tcMux.Lock()
 	tcMap = m
-	currentRules = rules
 	tcMux.Unlock()
 
-	logging.Debug("[HotSpot onRuleUpdate] Time statistic(ns) for updating hotSpot rule", "timeCost", util.CurrentTimeNano()-start)
-	logRuleUpdate(newRuleMap)
+	currentRules = rawResRulesMap
+
+	logging.Debug("[HotSpot onRuleUpdate] Time statistic(ns) for updating hotspot param flow rules", "timeCost", util.CurrentTimeNano()-start)
+	logRuleUpdate(validResRulesMap)
+	return nil
+}
+
+func onResourceRuleUpdate(res string, rawResRules []*Rule) (err error) {
+	defer func() {
+		if r := recover(); r != nil {
+			var ok bool
+			err, ok = r.(error)
+			if !ok {
+				err = fmt.Errorf("%v", r)
+			}
+		}
+	}()
+
+	validResRules := make([]*Rule, 0, len(rawResRules))
+	for _, rule := range rawResRules {
+		if err := IsValidRule(rule); err != nil {
+			logging.Warn("[HotSpot onResourceRuleUpdate] Ignoring invalid hotspot param flow rule", "rule", rule, "reason", err.Error())
+			continue
+		}
+		validResRules = append(validResRules, rule)
+	}
+
+	start := util.CurrentTimeNano()
+	oldResTcs := make([]TrafficShapingController, 0, 8)
+	tcMux.RLock()
+	oldResTcs = append(oldResTcs, tcMap[res]...)
+	tcMux.RUnlock()
+
+	newResTcs := buildResourceTrafficShapingController(res, validResRules, oldResTcs)
+	if len(newResTcs) > 0 {
+		// update resource slot chain
+		misc.RegisterRuleCheckSlotForResource(res, DefaultSlot)
+		for _, tc := range newResTcs {
+			if tc.BoundRule().MetricType == Concurrency {
+				misc.RegisterStatSlotForResource(res, DefaultConcurrencyStatSlot)
+				break
+			}
+		}
+	}
+
+	tcMux.Lock()
+	if len(newResTcs) == 0 {
+		delete(tcMap, res)
+	} else {
+		tcMap[res] = newResTcs
+	}
+	tcMux.Unlock()
+
+	currentRules[res] = rawResRules
+
+	logging.Debug("[HotSpot onResourceRuleUpdate] Time statistic(ns) for updating hotspot param flow rules", "timeCost", util.CurrentTimeNano()-start)
+	logging.Info("[HotSpot] load resource level hotspot param flow rules", "resource", res, "validResRules", validResRules)
 	return nil
 }
 
+// LoadRulesOfResource loads the given resource's hotspot param flow rules to the rule manager,
+// while all previous resource's rules will be replaced. The first returned value indicates whether
+// do real load operation, if the rules is the same with previous resource's rules, return false.
+func LoadRulesOfResource(res string, rules []*Rule) (bool, error) {
+	if len(res) == 0 {
+		return false, errors.New("empty resource")
+	}
+
+	updateRuleMux.Lock()
+	defer updateRuleMux.Unlock()
+
+	// clear resource rules
+	if len(rules) == 0 {
+		// clear resource's currentRules
+		delete(currentRules, res)
+		// clear tcMap
+		tcMux.Lock()
+		delete(tcMap, res)
+		tcMux.Unlock()
+		logging.Info("[HotSpot] clear resource level hotspot param flow rules", "resource", res)
+		return true, nil
+	}
+
+	// load resource level rules
+	isEqual := reflect.DeepEqual(currentRules[res], rules)
+	if isEqual {
+		logging.Info("[HotSpot] Load resource level hotspot param flow rules is the same with current resource level rules, so ignore load operation.")
+		return false, nil
+	}
+
+	err := onResourceRuleUpdate(res, rules)
+	return true, err
+}
+
 func logRuleUpdate(m map[string][]*Rule) {
 	rules := make([]*Rule, 0, 8)
 	for _, rs := range m {
@@ -254,9 +315,9 @@ func logRuleUpdate(m map[string][]*Rule) {
 		rules = append(rules, rs...)
 	}
 	if len(rules) == 0 {
-		logging.Info("[HotspotRuleManager] Hotspot rules were cleared")
+		logging.Info("[HotspotRuleManager] Hotspot param flow rules were cleared")
 	} else {
-		logging.Info("[HotspotRuleManager] Hotspot rules were loaded", "rules", rules)
+		logging.Info("[HotspotRuleManager] Hotspot param flow rules were loaded", "rules", rules)
 	}
 }
 
@@ -304,14 +365,48 @@ func calculateReuseIndexFor(r *Rule, oldResTcs []TrafficShapingController) (equa
 	return equalIdx, reuseStatIdx
 }
 
-func insertTcToTcMap(tc TrafficShapingController, res string, m trafficControllerMap) {
-	tcsOfRes, exists := m[res]
-	if !exists {
-		tcsOfRes = make([]TrafficShapingController, 0, 1)
-		m[res] = append(tcsOfRes, tc)
-	} else {
-		m[res] = append(tcsOfRes, tc)
+// buildResourceTrafficShapingController builds TrafficShapingController slice from rules. the resource of rules must be equals to res.
+func buildResourceTrafficShapingController(res string, resRules []*Rule, oldResTcs []TrafficShapingController) []TrafficShapingController {
+	newTcsOfRes := make([]TrafficShapingController, 0, len(resRules))
+	for _, rule := range resRules {
+		if res != rule.Resource {
+			logging.Error(errors.Errorf("unmatched resource name, expect: %s, actual: %s", res, rule.Resource), "Unmatched resource name in hotspot.buildResourceTrafficShapingController()", "rule", rule)
+			continue
+		}
+
+		equalIdx, reuseStatIdx := calculateReuseIndexFor(rule, oldResTcs)
+		// there is equivalent rule in old traffic shaping controller slice
+		if equalIdx >= 0 {
+			equalOldTC := oldResTcs[equalIdx]
+			newTcsOfRes = append(newTcsOfRes, equalOldTC)
+			// remove old tc from old resTcs
+			oldResTcs = append(oldResTcs[:equalIdx], oldResTcs[equalIdx+1:]...)
+			continue
+		}
+
+		// generate new traffic shaping controller
+		generator, supported := tcGenFuncMap[rule.ControlBehavior]
+		if !supported {
+			logging.Warn("[HotSpot buildResourceTrafficShapingController] Ignoring the hotspot param flow rule due to unsupported control behavior", "rule", rule)
+			continue
+		}
+		var tc TrafficShapingController
+		if reuseStatIdx >= 0 {
+			// generate new traffic shaping controller with reusable statistic metric.
+			tc = generator(rule, oldResTcs[reuseStatIdx].BoundMetric())
+			// remove the reused traffic shaping controller old res tcs
+			oldResTcs = append(oldResTcs[:reuseStatIdx], oldResTcs[reuseStatIdx+1:]...)
+		} else {
+			tc = generator(rule, nil)
+		}
+		if tc == nil {
+			logging.Debug("[HotSpot buildResourceTrafficShapingController] Ignoring the hotspot param flow rule due to bad generated traffic controller", "rule", rule)
+			continue
+		}
+
+		newTcsOfRes = append(newTcsOfRes, tc)
 	}
+	return newTcsOfRes
 }
 
 func IsValidRule(rule *Rule) error {
diff --git a/core/isolation/rule_manager.go b/core/isolation/rule_manager.go
index 399a2fe..6276c23 100644
--- a/core/isolation/rule_manager.go
+++ b/core/isolation/rule_manager.go
@@ -123,7 +123,7 @@ func onResourceRuleUpdate(res string, rawResRules []*Rule) (err error) {
 	validResRules := make([]*Rule, 0, len(rawResRules))
 	for _, rule := range rawResRules {
 		if err := IsValidRule(rule); err != nil {
-			logging.Warn("[Isolation onResourceRuleUpdate] Ignoring invalid flow rule", "rule", rule, "reason", err.Error())
+			logging.Warn("[Isolation onResourceRuleUpdate] Ignoring invalid isolation rule", "rule", rule, "reason", err.Error())
 			continue
 		}
 		validResRules = append(validResRules, rule)
@@ -143,7 +143,7 @@ func onResourceRuleUpdate(res string, rawResRules []*Rule) (err error) {
 	}
 	rwMux.Unlock()
 	currentRules[res] = rawResRules
-	logging.Debug("[Isolation onResourceRuleUpdate] Time statistic(ns) for updating flow rule", "timeCost", util.CurrentTimeNano()-start)
+	logging.Debug("[Isolation onResourceRuleUpdate] Time statistic(ns) for updating isolation rule", "timeCost", util.CurrentTimeNano()-start)
 	logging.Info("[Isolation] load resource level rules", "resource", res, "validResRules", validResRules)
 	return nil
 }
diff --git a/go.mod b/go.mod
index 4d45839..92b8ea9 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,7 @@ require (
 	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/shirou/gopsutil/v3 v3.20.12
 	github.com/stretchr/testify v1.6.1
 	go.uber.org/multierr v1.5.0
 	gopkg.in/yaml.v2 v2.3.0
diff --git a/go.sum b/go.sum
index b00ee4f..10aeefc 100644
--- a/go.sum
+++ b/go.sum
@@ -249,6 +249,8 @@ github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0
 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/shirou/gopsutil/v3 v3.20.12 h1:abpcjSQRHdb3thCge/UyJty9CnvvmUHljTSrjtFU+Og=
+github.com/shirou/gopsutil/v3 v3.20.12/go.mod h1:igHnfak0qnw1biGeI2qKQvu0ZkwvEkUcCLlYhZzdr/4=
 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=


Test patch:
diff --git a/core/circuitbreaker/rule_manager_test.go b/core/circuitbreaker/rule_manager_test.go
index bbf4a17..fefe1a8 100644
--- a/core/circuitbreaker/rule_manager_test.go
+++ b/core/circuitbreaker/rule_manager_test.go
@@ -21,6 +21,12 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
+func clearData() {
+	breakerRules = make(map[string][]*Rule)
+	breakers = make(map[string][]CircuitBreaker)
+	currentRules = make(map[string][]*Rule, 0)
+}
+
 func Test_isApplicableRule_valid(t *testing.T) {
 	type args struct {
 		rule *Rule
@@ -76,7 +82,7 @@ func Test_isApplicableRule_valid(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			if got := IsValid(tt.args.rule); got != tt.want {
+			if got := IsValidRule(tt.args.rule); got != tt.want {
 				t.Errorf("RuleManager.isApplicable() = %v, want %v", got, tt.want)
 			}
 		})
@@ -94,7 +100,7 @@ func Test_isApplicableRule_invalid(t *testing.T) {
 			MaxAllowedRtMs:   5,
 			Threshold:        -1.0,
 		}
-		if got := IsValid(rule); got == nil {
+		if got := IsValidRule(rule); got == nil {
 			t.Errorf("RuleManager.isApplicable() = %v", got)
 		}
 	})
@@ -107,7 +113,7 @@ func Test_isApplicableRule_invalid(t *testing.T) {
 			StatIntervalMs:   1000,
 			Threshold:        -0.3,
 		}
-		if got := IsValid(rule); got == nil {
+		if got := IsValidRule(rule); got == nil {
 			t.Errorf("RuleManager.isApplicable() = %v", got)
 		}
 	})
@@ -120,7 +126,7 @@ func Test_isApplicableRule_invalid(t *testing.T) {
 			StatIntervalMs:   1000,
 			Threshold:        0.0,
 		}
-		if got := IsValid(rule); got == nil {
+		if got := IsValidRule(rule); got == nil {
 			t.Errorf("RuleManager.isApplicable() = %v", got)
 		}
 	})
@@ -155,23 +161,27 @@ func Test_onUpdateRules(t *testing.T) {
 			Threshold:        10.0,
 		}
 		rules = append(rules, r1, r2, r3)
-		err := onRuleUpdate(rules)
+		resRulesMap := make(map[string][]*Rule)
+		resRulesMap["abc01"] = rules
+		err := onRuleUpdate(resRulesMap)
 		if err != nil {
 			t.Fatal(err)
 		}
 		assert.True(t, len(breakers["abc01"]) == 3)
 		assert.True(t, len(breakerRules["abc01"]) == 3)
-		breakers = make(map[string][]CircuitBreaker)
-		breakerRules = make(map[string][]*Rule)
+		clearData()
 	})
 
 	t.Run("Test_onUpdateRules_invalid", func(t *testing.T) {
 		r1 := &Rule{
 			Resource: "abc",
 		}
-		err := onRuleUpdate([]*Rule{r1})
+		resRulesMap := make(map[string][]*Rule)
+		resRulesMap["abc01"] = []*Rule{r1}
+		err := onRuleUpdate(resRulesMap)
 		assert.Nil(t, err)
 		assert.True(t, len(GetRules()) == 0)
+		clearData()
 	})
 }
 
@@ -253,6 +263,7 @@ func Test_onRuleUpdate(t *testing.T) {
 		assert.True(t, reflect.DeepEqual(newCbs[1].BoundStat(), b2.BoundStat()))
 		assert.True(t, reflect.DeepEqual(newCbs[2].BoundRule(), r6))
 		assert.True(t, reflect.DeepEqual(newCbs[3].BoundRule(), r7))
+		clearData()
 	})
 }
 
@@ -305,7 +316,7 @@ func TestGetRules(t *testing.T) {
 		_, _ = LoadRules([]*Rule{r1})
 		rules := GetRules()
 		assert.True(t, len(rules) == 1 && rules[0].Resource == r1.Resource && rules[0].Strategy == r1.Strategy)
-		_ = ClearRules()
+		clearData()
 	})
 }
 
@@ -324,7 +335,7 @@ func TestGetBreakersOfResource(t *testing.T) {
 
 	cbs := getBreakersOfResource("abc")
 	assert.True(t, len(cbs) == 1 && cbs[0].BoundRule() == r1)
-	_ = ClearRules()
+	clearData()
 }
 
 func TestSetCircuitBreakerGenerator(t *testing.T) {
@@ -382,5 +393,145 @@ func TestLoadRules(t *testing.T) {
 		})
 		assert.Nil(t, err)
 		assert.False(t, ok)
+		clearData()
+	})
+}
+
+func TestLoadRulesOfResource(t *testing.T) {
+	r1 := &Rule{
+		Resource:         "abc1",
+		Strategy:         SlowRequestRatio,
+		RetryTimeoutMs:   1000,
+		MinRequestAmount: 5,
+		StatIntervalMs:   1000,
+		MaxAllowedRtMs:   20,
+		Threshold:        0.1,
+	}
+	r2 := &Rule{
+		Resource:         "abc1",
+		Strategy:         ErrorRatio,
+		RetryTimeoutMs:   1000,
+		MinRequestAmount: 5,
+		StatIntervalMs:   1000,
+		Threshold:        0.3,
+	}
+	r3 := &Rule{
+		Resource:         "abc2",
+		Strategy:         ErrorCount,
+		RetryTimeoutMs:   1000,
+		MinRequestAmount: 5,
+		StatIntervalMs:   1000,
+		Threshold:        10.0,
+	}
+	succ, err := LoadRules([]*Rule{r1, r2, r3})
+	assert.True(t, succ && err == nil)
+
+	t.Run("LoadRulesOfResource_empty_resource", func(t *testing.T) {
+		succ, err = LoadRulesOfResource("", []*Rule{r1, r2})
+		assert.True(t, !succ && err != nil)
+	})
+
+	t.Run("LoadRulesOfResource_cache_hit", func(t *testing.T) {
+		r11 := *r1
+		r12 := *r2
+		succ, err = LoadRulesOfResource("abc1", []*Rule{&r11, &r12})
+		assert.True(t, !succ && err == nil)
+	})
+
+	t.Run("LoadRulesOfResource_clear", func(t *testing.T) {
+		succ, err = LoadRulesOfResource("abc1", []*Rule{})
+		assert.True(t, succ && err == nil)
+		assert.True(t, len(breakerRules["abc1"]) == 0 && len(currentRules["abc1"]) == 0)
+		assert.True(t, len(breakerRules["abc2"]) == 1 && len(currentRules["abc2"]) == 1)
+	})
+	clearData()
+}
+
+func Test_onResourceRuleUpdate(t *testing.T) {
+	r1 := Rule{
+		Resource:         "abc1",
+		Strategy:         SlowRequestRatio,
+		RetryTimeoutMs:   1000,
+		MinRequestAmount: 5,
+		StatIntervalMs:   1000,
+		MaxAllowedRtMs:   20,
+		Threshold:        0.1,
+	}
+	r2 := Rule{
+		Resource:         "abc1",
+		Strategy:         ErrorRatio,
+		RetryTimeoutMs:   1000,
+		MinRequestAmount: 5,
+		StatIntervalMs:   1000,
+		Threshold:        0.3,
+	}
+	r3 := Rule{
+		Resource:         "abc2",
+		Strategy:         ErrorCount,
+		RetryTimeoutMs:   1000,
+		MinRequestAmount: 5,
+		StatIntervalMs:   1000,
+		Threshold:        10.0,
+	}
+	succ, err := LoadRules([]*Rule{&r1, &r2, &r3})
+	assert.True(t, succ && err == nil)
+
+	t.Run("Test_onResourceRuleUpdate_normal", func(t *testing.T) {
+		r11 := r1
+		r11.Threshold = 0.5
+		err = onResourceRuleUpdate("abc1", []*Rule{&r11})
+
+		assert.True(t, len(breakerRules["abc1"]) == 1)
+		assert.True(t, len(breakers["abc1"]) == 1)
+		assert.True(t, len(currentRules["abc1"]) == 1)
+		assert.True(t, breakers["abc1"][0].BoundRule() == &r11)
+
+		assert.True(t, len(breakerRules["abc2"]) == 1)
+		assert.True(t, len(breakers["abc2"]) == 1)
+		assert.True(t, len(currentRules["abc2"]) == 1)
+
+		clearData()
+	})
+}
+
+func TestClearRulesOfResource(t *testing.T) {
+	r1 := Rule{
+		Resource:         "abc1",
+		Strategy:         SlowRequestRatio,
+		RetryTimeoutMs:   1000,
+		MinRequestAmount: 5,
+		StatIntervalMs:   1000,
+		MaxAllowedRtMs:   20,
+		Threshold:        0.1,
+	}
+	r2 := Rule{
+		Resource:         "abc1",
+		Strategy:         ErrorRatio,
+		RetryTimeoutMs:   1000,
+		MinRequestAmount: 5,
+		StatIntervalMs:   1000,
+		Threshold:        0.3,
+	}
+	r3 := Rule{
+		Resource:         "abc2",
+		Strategy:         ErrorCount,
+		RetryTimeoutMs:   1000,
+		MinRequestAmount: 5,
+		StatIntervalMs:   1000,
+		Threshold:        10.0,
+	}
+	succ, err := LoadRules([]*Rule{&r1, &r2, &r3})
+	assert.True(t, succ && err == nil)
+
+	t.Run("TestClearRulesOfResource_normal", func(t *testing.T) {
+		assert.True(t, ClearRulesOfResource("abc1") == nil)
+
+		assert.True(t, len(breakerRules["abc1"]) == 0)
+		assert.True(t, len(breakers["abc1"]) == 0)
+		assert.True(t, len(currentRules["abc1"]) == 0)
+		assert.True(t, len(breakerRules["abc2"]) == 1)
+		assert.True(t, len(breakers["abc2"]) == 1)
+		assert.True(t, len(currentRules["abc2"]) == 1)
+		clearData()
 	})
 }
diff --git a/core/hotspot/rule_manager_test.go b/core/hotspot/rule_manager_test.go
index 9571dde..291d2cb 100644
--- a/core/hotspot/rule_manager_test.go
+++ b/core/hotspot/rule_manager_test.go
@@ -23,6 +23,11 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
+func clearData() {
+	tcMap = make(trafficControllerMap)
+	currentRules = make(map[string][]*Rule, 0)
+}
+
 func Test_tcGenFuncMap(t *testing.T) {
 	t.Run("Test_tcGenFuncMap_withoutMetric", func(t *testing.T) {
 		specific := make(map[interface{}]int64)
@@ -150,7 +155,8 @@ func Test_IsValidRule(t *testing.T) {
 }
 
 func Test_onRuleUpdate(t *testing.T) {
-	tcMap = make(trafficControllerMap)
+	clearData()
+	defer clearData()
 
 	specific := make(map[interface{}]int64)
 	specific["sss"] = 1
@@ -215,7 +221,7 @@ func Test_onRuleUpdate(t *testing.T) {
 
 	updated, err := LoadRules([]*Rule{r1, r2, r3, r4})
 	if !updated || err != nil {
-		t.Errorf("Fail to prepare data, err: %+v", err)
+		t.Fatalf("Fail to prepare data, err: %+v", err)
 	}
 	assert.True(t, len(tcMap["abc"]) == 4)
 
@@ -271,7 +277,10 @@ func Test_onRuleUpdate(t *testing.T) {
 	oldTc2MetricPtrAddr := fmt.Sprintf("%p", tcMap["abc"][1].BoundMetric())
 	fmt.Println("oldTc2MetricPtr:", oldTc2MetricPtrAddr)
 
-	err = onRuleUpdate([]*Rule{r21, r22, r23})
+	rulesMap := map[string][]*Rule{
+		"abc": {r21, r22, r23},
+	}
+	err = onRuleUpdate(rulesMap)
 	assert.True(t, err == nil)
 	assert.True(t, len(tcMap) == 1)
 	abcTcs := tcMap["abc"]
@@ -290,11 +299,12 @@ func Test_onRuleUpdate(t *testing.T) {
 	assert.True(t, newTc1PtrAddr == oldTc1PtrAddr && newTc2MetricPtrAddr == oldTc2MetricPtrAddr)
 	assert.True(t, abcTcs[0].BoundRule() == r1 && abcTcs[0] == oldTc1Ptr)
 	assert.True(t, abcTcs[1].BoundMetric() == oldTc2Ptr.BoundMetric())
-
-	tcMap = make(trafficControllerMap)
 }
 
 func TestLoadRules(t *testing.T) {
+	clearData()
+	defer clearData()
+
 	t.Run("loadSameRules", func(t *testing.T) {
 		specific := make(map[interface{}]int64)
 		specific["sss"] = 1
@@ -333,3 +343,227 @@ func TestLoadRules(t *testing.T) {
 		assert.False(t, ok)
 	})
 }
+
+func TestLoadRulesOfResource(t *testing.T) {
+	clearData()
+	defer clearData()
+
+	specific := make(map[interface{}]int64)
+	specific["sss"] = 1
+	specific["123"] = 3
+	r11 := Rule{
+		ID:                "1",
+		Resource:          "abc1",
+		MetricType:        Concurrency,
+		ControlBehavior:   Reject,
+		ParamIndex:        0,
+		Threshold:         100.0,
+		MaxQueueingTimeMs: 0,
+		BurstCount:        10,
+		DurationInSec:     1,
+		SpecificItems:     specific,
+	}
+	r12 := Rule{
+		ID:                "2",
+		Resource:          "abc1",
+		MetricType:        Concurrency,
+		ControlBehavior:   Reject,
+		ParamIndex:        0,
+		Threshold:         200.0,
+		MaxQueueingTimeMs: 0,
+		BurstCount:        10,
+		DurationInSec:     1,
+		SpecificItems:     specific,
+	}
+	r21 := Rule{
+		ID:                "3",
+		Resource:          "abc2",
+		MetricType:        Concurrency,
+		ControlBehavior:   Reject,
+		ParamIndex:        0,
+		Threshold:         100.0,
+		MaxQueueingTimeMs: 0,
+		BurstCount:        10,
+		DurationInSec:     1,
+		SpecificItems:     specific,
+	}
+	r22 := Rule{
+		ID:                "4",
+		Resource:          "abc2",
+		MetricType:        Concurrency,
+		ControlBehavior:   Reject,
+		ParamIndex:        0,
+		Threshold:         200.0,
+		MaxQueueingTimeMs: 0,
+		BurstCount:        10,
+		DurationInSec:     1,
+		SpecificItems:     specific,
+	}
+
+	succ, err := LoadRules([]*Rule{&r11, &r12, &r21, &r22})
+	assert.True(t, succ && err == nil)
+
+	t.Run("LoadRulesOfResource_empty_resource", func(t *testing.T) {
+		succ, err = LoadRulesOfResource("", []*Rule{&r11, &r12})
+		assert.True(t, !succ && err != nil)
+	})
+
+	t.Run("LoadRulesOfResource_cache_hit", func(t *testing.T) {
+		r11Copy := r11
+		r12Copy := r12
+		succ, err = LoadRulesOfResource("abc1", []*Rule{&r11Copy, &r12Copy})
+		assert.True(t, !succ && err == nil)
+	})
+
+	t.Run("LoadRulesOfResource_clear", func(t *testing.T) {
+		succ, err = LoadRulesOfResource("abc1", []*Rule{})
+		assert.True(t, succ && err == nil)
+		assert.True(t, len(tcMap["abc1"]) == 0 && len(currentRules["abc1"]) == 0)
+		assert.True(t, len(tcMap["abc2"]) == 2 && len(currentRules["abc2"]) == 2)
+	})
+}
+
+func Test_onResourceRuleUpdate(t *testing.T) {
+	clearData()
+	defer clearData()
+
+	specific := make(map[interface{}]int64)
+	specific["sss"] = 1
+	specific["123"] = 3
+	r11 := Rule{
+		ID:                "1",
+		Resource:          "abc1",
+		MetricType:        Concurrency,
+		ControlBehavior:   Reject,
+		ParamIndex:        0,
+		Threshold:         100.0,
+		MaxQueueingTimeMs: 0,
+		BurstCount:        10,
+		DurationInSec:     1,
+		SpecificItems:     specific,
+	}
+	r12 := Rule{
+		ID:                "2",
+		Resource:          "abc1",
+		MetricType:        Concurrency,
+		ControlBehavior:   Reject,
+		ParamIndex:        0,
+		Threshold:         200.0,
+		MaxQueueingTimeMs: 0,
+		BurstCount:        10,
+		DurationInSec:     1,
+		SpecificItems:     specific,
+	}
+	r21 := Rule{
+		ID:                "3",
+		Resource:          "abc2",
+		MetricType:        Concurrency,
+		ControlBehavior:   Reject,
+		ParamIndex:        0,
+		Threshold:         100.0,
+		MaxQueueingTimeMs: 0,
+		BurstCount:        10,
+		DurationInSec:     1,
+		SpecificItems:     specific,
+	}
+	r22 := Rule{
+		ID:                "4",
+		Resource:          "abc2",
+		MetricType:        Concurrency,
+		ControlBehavior:   Reject,
+		ParamIndex:        0,
+		Threshold:         200.0,
+		MaxQueueingTimeMs: 0,
+		BurstCount:        10,
+		DurationInSec:     1,
+		SpecificItems:     specific,
+	}
+
+	succ, err := LoadRules([]*Rule{&r11, &r12, &r21, &r22})
+	assert.True(t, succ && err == nil)
+
+	t.Run("Test_onResourceRuleUpdate_normal", func(t *testing.T) {
+		r11Copy := r11
+		r11Copy.Threshold = 500
+		err = onResourceRuleUpdate("abc1", []*Rule{&r11Copy})
+
+		assert.True(t, len(tcMap["abc1"]) == 1)
+		assert.True(t, len(currentRules["abc1"]) == 1)
+		assert.True(t, tcMap["abc1"][0].BoundRule() == &r11Copy)
+
+		assert.True(t, len(tcMap["abc2"]) == 2)
+		assert.True(t, len(currentRules["abc2"]) == 2)
+
+		clearData()
+	})
+}
+
+func TestClearRulesOfResource(t *testing.T) {
+	clearData()
+	defer clearData()
+
+	specific := make(map[interface{}]int64)
+	specific["sss"] = 1
+	specific["123"] = 3
+	r11 := Rule{
+		ID:                "1",
+		Resource:          "abc1",
+		MetricType:        Concurrency,
+		ControlBehavior:   Reject,
+		ParamIndex:        0,
+		Threshold:         100.0,
+		MaxQueueingTimeMs: 0,
+		BurstCount:        10,
+		DurationInSec:     1,
+		SpecificItems:     specific,
+	}
+	r12 := Rule{
+		ID:                "2",
+		Resource:          "abc1",
+		MetricType:        Concurrency,
+		ControlBehavior:   Reject,
+		ParamIndex:        0,
+		Threshold:         200.0,
+		MaxQueueingTimeMs: 0,
+		BurstCount:        10,
+		DurationInSec:     1,
+		SpecificItems:     specific,
+	}
+	r21 := Rule{
+		ID:                "3",
+		Resource:          "abc2",
+		MetricType:        Concurrency,
+		ControlBehavior:   Reject,
+		ParamIndex:        0,
+		Threshold:         100.0,
+		MaxQueueingTimeMs: 0,
+		BurstCount:        10,
+		DurationInSec:     1,
+		SpecificItems:     specific,
+	}
+	r22 := Rule{
+		ID:                "4",
+		Resource:          "abc2",
+		MetricType:        Concurrency,
+		ControlBehavior:   Reject,
+		ParamIndex:        0,
+		Threshold:         200.0,
+		MaxQueueingTimeMs: 0,
+		BurstCount:        10,
+		DurationInSec:     1,
+		SpecificItems:     specific,
+	}
+
+	succ, err := LoadRules([]*Rule{&r11, &r12, &r21, &r22})
+	assert.True(t, succ && err == nil)
+
+	t.Run("TestClearRulesOfResource_normal", func(t *testing.T) {
+		assert.True(t, ClearRulesOfResource("abc1") == nil)
+
+		assert.True(t, len(tcMap["abc1"]) == 0)
+		assert.True(t, len(currentRules["abc1"]) == 0)
+		assert.True(t, len(tcMap["abc2"]) == 2)
+		assert.True(t, len(currentRules["abc2"]) == 2)
+		clearData()
+	})
+}