core/system/rule_manager.go (104 lines of code) (raw):

// 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 system import ( "reflect" "sync" "github.com/alibaba/sentinel-golang/logging" "github.com/alibaba/sentinel-golang/util" "github.com/pkg/errors" ) type RuleMap map[MetricType][]*Rule // const var ( ruleMap = make(RuleMap) ruleMapMux = new(sync.RWMutex) currentRules = make([]*Rule, 0) updateRuleMux = new(sync.Mutex) ) // GetRules returns all the rules based on copy. // It doesn't take effect for system module if user changes the rule. // GetRules need to compete system module's global lock and the high performance losses of copy, // // reduce or do not call GetRules if possible func GetRules() []Rule { rules := make([]*Rule, 0, len(ruleMap)) ruleMapMux.RLock() for _, rs := range ruleMap { rules = append(rules, rs...) } ruleMapMux.RUnlock() ret := make([]Rule, 0, len(rules)) for _, r := range rules { ret = append(ret, *r) } return ret } // getRules returns all the rules。Any changes of rules take effect for system module // getRules is an internal interface. func getRules() []*Rule { ruleMapMux.RLock() defer ruleMapMux.RUnlock() rules := make([]*Rule, 0, 8) for _, rs := range ruleMap { rules = append(rules, rs...) } return rules } // LoadRules loads given system rules to the rule manager, while all previous rules will be replaced. func LoadRules(rules []*Rule) (bool, error) { updateRuleMux.Lock() defer updateRuleMux.Unlock() isEqual := reflect.DeepEqual(currentRules, rules) if isEqual { logging.Info("[System] Load rules is the same with current rules, so ignore load operation.") return false, nil } m := buildRuleMap(rules) if err := onRuleUpdate(m); err != nil { logging.Error(err, "Fail to load rules in system.LoadRules()", "rules", rules) return false, err } currentRules = rules return true, nil } // ClearRules clear all the previous rules func ClearRules() error { _, err := LoadRules(nil) return err } func onRuleUpdate(r RuleMap) error { start := util.CurrentTimeNano() ruleMapMux.Lock() ruleMap = r ruleMapMux.Unlock() logging.Debug("[System onRuleUpdate] Time statistic(ns) for updating system rule", "timeCost", util.CurrentTimeNano()-start) if len(r) > 0 { logging.Info("[SystemRuleManager] System rules loaded", "rules", r) } else { logging.Info("[SystemRuleManager] System rules were cleared") } return nil } func buildRuleMap(rules []*Rule) RuleMap { m := make(RuleMap) if len(rules) == 0 { return m } for _, rule := range rules { if err := IsValidSystemRule(rule); err != nil { logging.Warn("[System buildRuleMap] Ignoring invalid system rule", "rule", rule, "err", err.Error()) continue } rulesOfRes, exists := m[rule.MetricType] if !exists { m[rule.MetricType] = []*Rule{rule} } else { m[rule.MetricType] = append(rulesOfRes, rule) } } return m } // IsValidSystemRule determine the system rule is valid or not func IsValidSystemRule(rule *Rule) error { if rule == nil { return errors.New("nil Rule") } if rule.TriggerCount < 0 { return errors.New("negative threshold") } if rule.MetricType >= MetricTypeSize { return errors.New("invalid metric type") } if rule.MetricType == CpuUsage && rule.TriggerCount > 1 { return errors.New("invalid CPU usage, valid range is [0.0, 1.0]") } return nil }