Skip to content

Commit 2af768b

Browse files
Feat governance gray v3 (#565)
* feat: go agent - add fallback rule - support fallback for gin and go-zero web - support mock for system rule * fix: unit test
1 parent 0807185 commit 2af768b

File tree

13 files changed

+549
-6
lines changed

13 files changed

+549
-6
lines changed

core/fallback/rule.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright 1999-2020 Alibaba Group Holding Ltd.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package fallback
16+
17+
type TargetResourceType int64
18+
19+
const (
20+
WebResourceType TargetResourceType = 1
21+
RpcResourceType TargetResourceType = 2
22+
)
23+
24+
type FunctionType int64
25+
26+
const (
27+
FlowType FunctionType = 1
28+
Isolation FunctionType = 6
29+
HotspotRpc FunctionType = 4
30+
HotspotHttp FunctionType = 11
31+
)
32+
33+
type Rule struct {
34+
TargetResourceType TargetResourceType `json:"targetResourceType"`
35+
TargetMap map[string][]FunctionType `json:"targetMap"`
36+
FallbackBehavior interface{} `json:"fallbackBehavior"`
37+
}
38+
39+
type WebBlockFallbackBehavior struct {
40+
WebFallbackMode int64 `json:"webFallbackMode"` // 0: return, 1: redirect
41+
WebRespStatusCode int64 `json:"webRespStatusCode"`
42+
WebRespMessage string `json:"webRespMessage"`
43+
WebRespContentType int64 `json:"webRespContentType"` // 0: test, 1: json
44+
WebRedirectUrl string `json:"webRedirectUrl"`
45+
}
46+
47+
type RpcBlockFallbackBehavior struct {
48+
RpcFallbackMode int64 `json:"rpcFallbackMode"`
49+
RpcFallbackCacheMode int64 `json:"rpcFallbackCacheMode"`
50+
RpcRespFallbackClassName string `json:"rpcRespFallbackClassName"`
51+
RpcFallbackExceptionMessage string `json:"rpcFallbackExceptionMessage"`
52+
RpcRespContentBody string `json:"rpcRespContentBody"`
53+
}

core/fallback/rule_manager.go

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
// Copyright 1999-2020 Alibaba Group Holding Ltd.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package fallback
16+
17+
import (
18+
"encoding/json"
19+
"github.com/alibaba/sentinel-golang/logging"
20+
"github.com/alibaba/sentinel-golang/util"
21+
"reflect"
22+
"sync"
23+
)
24+
25+
var (
26+
webRuleMap = make(map[string]map[FunctionType]*WebBlockFallbackBehavior)
27+
rpcRuleMap = make(map[string]map[FunctionType]*RpcBlockFallbackBehavior)
28+
currentWebRules = make(map[string]map[FunctionType]*WebBlockFallbackBehavior)
29+
currentRpcRules = make(map[string]map[FunctionType]*RpcBlockFallbackBehavior)
30+
webRwMux = &sync.RWMutex{}
31+
rpcRwMux = &sync.RWMutex{}
32+
updateRuleMux = &sync.RWMutex{}
33+
)
34+
35+
func isValidWebFallbackBehavior(behavior *WebBlockFallbackBehavior) bool {
36+
if behavior == nil {
37+
return false
38+
}
39+
if behavior.WebRespContentType != 0 && behavior.WebRespContentType != 1 {
40+
return false
41+
}
42+
if behavior.WebRespStatusCode < 0 || behavior.WebRespStatusCode > 600 {
43+
return false
44+
}
45+
return true
46+
}
47+
48+
func isValidRpcFallbackBehavior(behavior *RpcBlockFallbackBehavior) bool {
49+
if behavior == nil {
50+
return false
51+
}
52+
if behavior.RpcFallbackMode != 0 && behavior.RpcFallbackMode != 1 {
53+
return false
54+
}
55+
return true
56+
}
57+
58+
func LoadRules(rules []*Rule) (bool, error) {
59+
resWebRuleMap := make(map[string]map[FunctionType]*WebBlockFallbackBehavior)
60+
resRpcRuleMap := make(map[string]map[FunctionType]*RpcBlockFallbackBehavior)
61+
for _, rule := range rules {
62+
b, err := json.Marshal(rule.FallbackBehavior)
63+
if err != nil {
64+
logging.Warn("[Fallback] marshal web fall back behavior failed", "reason", err.Error())
65+
continue
66+
}
67+
switch rule.TargetResourceType {
68+
case WebResourceType:
69+
var webBehavior *WebBlockFallbackBehavior
70+
err := json.Unmarshal(b, &webBehavior)
71+
if err != nil {
72+
logging.Warn("[Fallback] unmarshal web fall back behavior failed", "reason", err.Error())
73+
continue
74+
}
75+
if !isValidWebFallbackBehavior(webBehavior) {
76+
logging.Warn("[Fallback] invalid web fall back behavior", "behavior", webBehavior)
77+
continue
78+
}
79+
80+
for resource, funcTypeList := range rule.TargetMap {
81+
if resource == "" || len(funcTypeList) == 0 {
82+
continue
83+
}
84+
var behaviorMap map[FunctionType]*WebBlockFallbackBehavior
85+
var ok bool
86+
if behaviorMap, ok = resWebRuleMap[resource]; !ok {
87+
behaviorMap = make(map[FunctionType]*WebBlockFallbackBehavior)
88+
resWebRuleMap[resource] = behaviorMap
89+
}
90+
91+
for _, functionType := range funcTypeList {
92+
behaviorMap[functionType] = webBehavior
93+
}
94+
}
95+
case RpcResourceType:
96+
var rpcBehavior *RpcBlockFallbackBehavior
97+
err := json.Unmarshal(b, &rpcBehavior)
98+
if err != nil {
99+
logging.Warn("[Fallback] unmarshal rpc fall back behavior failed", "reason", err.Error())
100+
continue
101+
}
102+
if !isValidRpcFallbackBehavior(rpcBehavior) {
103+
logging.Warn("[Fallback] invalid rpc fall back behavior", "behavior", rpcBehavior)
104+
continue
105+
}
106+
107+
for resource, funcTypeList := range rule.TargetMap {
108+
var behaviorMap map[FunctionType]*RpcBlockFallbackBehavior
109+
var ok bool
110+
if behaviorMap, ok = resRpcRuleMap[resource]; !ok {
111+
behaviorMap = make(map[FunctionType]*RpcBlockFallbackBehavior)
112+
resRpcRuleMap[resource] = behaviorMap
113+
}
114+
115+
for _, functionType := range funcTypeList {
116+
behaviorMap[functionType] = rpcBehavior
117+
}
118+
}
119+
default:
120+
logging.Warn("[Fallback] unsupported resource type", "resourceType", rule.TargetResourceType)
121+
continue
122+
}
123+
}
124+
125+
updateRuleMux.Lock()
126+
defer updateRuleMux.Unlock()
127+
var err error
128+
var updated bool
129+
isEqual := reflect.DeepEqual(currentWebRules, resWebRuleMap)
130+
if !isEqual {
131+
updateErr := onWebRuleUpdate(resWebRuleMap)
132+
if updateErr != nil {
133+
logging.Error(updateErr, "[Fallback] update web rule failed")
134+
err = updateErr
135+
} else {
136+
updated = true
137+
}
138+
} else {
139+
logging.Info("[Fallback] Web load rules is the same with current rules, so ignore load operation.")
140+
}
141+
isEqual = reflect.DeepEqual(currentRpcRules, resRpcRuleMap)
142+
if !isEqual {
143+
updateErr := onRpcRuleUpdate(resRpcRuleMap)
144+
if updateErr != nil {
145+
logging.Error(updateErr, "[Fallback] update rpc rule failed")
146+
err = updateErr
147+
} else {
148+
updated = true
149+
}
150+
} else {
151+
logging.Info("[Fallback] Rpc load rules is the same with current rules, so ignore load operation.")
152+
}
153+
return updated, err
154+
}
155+
156+
func onWebRuleUpdate(rawWebRuleMap map[string]map[FunctionType]*WebBlockFallbackBehavior) error {
157+
start := util.CurrentTimeNano()
158+
webRwMux.Lock()
159+
webRuleMap = rawWebRuleMap
160+
webRwMux.Unlock()
161+
currentWebRules = rawWebRuleMap
162+
logging.Debug("[Fallback onWebRuleUpdate] Time statistic(ns) for updating web fallback rule", "timeCost", util.CurrentTimeNano()-start)
163+
return nil
164+
}
165+
166+
func onRpcRuleUpdate(rawRpcRuleMap map[string]map[FunctionType]*RpcBlockFallbackBehavior) error {
167+
start := util.CurrentTimeNano()
168+
rpcRwMux.Lock()
169+
rpcRuleMap = rawRpcRuleMap
170+
rpcRwMux.Unlock()
171+
currentRpcRules = rawRpcRuleMap
172+
logging.Debug("[Fallback onRpcRuleUpdate] Time statistic(ns) for updating rpc fallback rule", "timeCost", util.CurrentTimeNano()-start)
173+
return nil
174+
}

core/fallback/rule_manager_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright 1999-2020 Alibaba Group Holding Ltd.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package fallback
16+
17+
import (
18+
"github.com/stretchr/testify/assert"
19+
"testing"
20+
)
21+
22+
func TestLoadRule(t *testing.T) {
23+
_, err := LoadRules([]*Rule{
24+
{
25+
TargetResourceType: WebResourceType,
26+
TargetMap: map[string][]FunctionType{
27+
"/greet": {
28+
FlowType,
29+
Isolation,
30+
},
31+
},
32+
FallbackBehavior: &WebBlockFallbackBehavior{
33+
WebFallbackMode: 0,
34+
WebRespContentType: 0,
35+
WebRespStatusCode: 433,
36+
WebRespMessage: "1234599",
37+
},
38+
},
39+
{
40+
TargetResourceType: WebResourceType,
41+
TargetMap: map[string][]FunctionType{
42+
"/greet": {
43+
HotspotHttp,
44+
},
45+
},
46+
FallbackBehavior: &WebBlockFallbackBehavior{
47+
WebFallbackMode: 0,
48+
WebRespContentType: 1,
49+
WebRespStatusCode: 434,
50+
WebRespMessage: "{\n \"abc\": 123\n}",
51+
},
52+
},
53+
{
54+
TargetResourceType: WebResourceType,
55+
TargetMap: map[string][]FunctionType{
56+
"/api/users/:id": {
57+
FlowType,
58+
},
59+
},
60+
FallbackBehavior: &WebBlockFallbackBehavior{
61+
WebFallbackMode: 0,
62+
WebRespContentType: 1,
63+
WebRespStatusCode: 434,
64+
WebRespMessage: "{\n \"abc\": 123\n}",
65+
},
66+
},
67+
})
68+
assert.NoError(t, err)
69+
70+
assert.Equal(t, 2, len(webRuleMap))
71+
assert.Equal(t, 0, len(rpcRuleMap))
72+
73+
funcTypeMap := webRuleMap["/greet"]
74+
assert.Equal(t, 3, len(funcTypeMap))
75+
76+
assert.Equal(t, funcTypeMap[FlowType].WebRespStatusCode, int64(433))
77+
assert.Equal(t, funcTypeMap[HotspotHttp].WebRespStatusCode, int64(434))
78+
79+
funcTypeMap = webRuleMap["/api/users/:id"]
80+
assert.Equal(t, 1, len(funcTypeMap))
81+
82+
}

core/fallback/slot.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright 1999-2020 Alibaba Group Holding Ltd.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package fallback
16+
17+
import (
18+
"github.com/alibaba/sentinel-golang/core/base"
19+
)
20+
21+
var blockType2FuncTypeMap = map[base.BlockType]FunctionType{
22+
base.BlockTypeFlow: FlowType,
23+
base.BlockTypeIsolation: Isolation,
24+
base.BlockTypeHotSpotParamFlow: HotspotRpc,
25+
}
26+
27+
func getFuncTypeByBlockType(blockType base.BlockType) (FunctionType, bool) {
28+
funcType, ok := blockType2FuncTypeMap[blockType]
29+
return funcType, ok
30+
}
31+
32+
func GetWebFallbackBehavior(resource string, blockType base.BlockType) (*WebBlockFallbackBehavior, bool) {
33+
functionType, ok := getFuncTypeByBlockType(blockType)
34+
if !ok {
35+
return nil, false
36+
}
37+
38+
webRwMux.RLock()
39+
defer webRwMux.RUnlock()
40+
41+
funcMap, ok := webRuleMap[resource]
42+
if !ok {
43+
return nil, false
44+
}
45+
behavior, ok := funcMap[functionType]
46+
if !ok {
47+
return nil, false
48+
}
49+
return behavior, true
50+
}
51+
52+
func GetRpcFallbackBehavior(resource string, blockType base.BlockType) (*RpcBlockFallbackBehavior, bool) {
53+
functionType, ok := getFuncTypeByBlockType(blockType)
54+
if !ok {
55+
return nil, false
56+
}
57+
58+
rpcRwMux.RLock()
59+
defer rpcRwMux.RUnlock()
60+
61+
funcMap, ok := rpcRuleMap[resource]
62+
if !ok {
63+
return nil, false
64+
}
65+
behavior, ok := funcMap[functionType]
66+
if !ok {
67+
return nil, false
68+
}
69+
return behavior, true
70+
}

0 commit comments

Comments
 (0)