-
Notifications
You must be signed in to change notification settings - Fork 438
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- add fallback rule - support fallback for gin and go-zero web - support mock for system rule
- Loading branch information
1 parent
0807185
commit 62502e7
Showing
19 changed files
with
5,765 additions
and
337 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// 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 fallback | ||
|
||
type TargetResourceType int64 | ||
|
||
const ( | ||
WebResourceType TargetResourceType = 1 | ||
RpcResourceType TargetResourceType = 2 | ||
) | ||
|
||
type FunctionType int64 | ||
|
||
const ( | ||
FlowType FunctionType = 1 | ||
Isolation FunctionType = 6 | ||
HotspotRpc FunctionType = 4 | ||
HotspotHttp FunctionType = 11 | ||
) | ||
|
||
type Rule struct { | ||
TargetResourceType TargetResourceType `json:"targetResourceType"` | ||
TargetMap map[string][]FunctionType `json:"targetMap"` | ||
FallbackBehavior interface{} `json:"fallbackBehavior"` | ||
} | ||
|
||
type WebBlockFallbackBehavior struct { | ||
WebFallbackMode int64 `json:"webFallbackMode"` // 0: return, 1: redirect | ||
WebRespStatusCode int64 `json:"webRespStatusCode"` | ||
WebRespMessage string `json:"webRespMessage"` | ||
WebRespContentType int64 `json:"webRespContentType"` // 0: test, 1: json | ||
WebRedirectUrl string `json:"webRedirectUrl"` | ||
} | ||
|
||
type RpcBlockFallbackBehavior struct { | ||
RpcFallbackMode int64 `json:"rpcFallbackMode"` | ||
RpcFallbackCacheMode int64 `json:"rpcFallbackCacheMode"` | ||
RpcRespFallbackClassName string `json:"rpcRespFallbackClassName"` | ||
RpcFallbackExceptionMessage string `json:"rpcFallbackExceptionMessage"` | ||
RpcRespContentBody string `json:"rpcRespContentBody"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
// 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 fallback | ||
|
||
import ( | ||
"encoding/json" | ||
"github.com/alibaba/sentinel-golang/logging" | ||
"github.com/alibaba/sentinel-golang/util" | ||
"reflect" | ||
"sync" | ||
) | ||
|
||
var ( | ||
webRuleMap = make(map[string]map[FunctionType]*WebBlockFallbackBehavior) | ||
rpcRuleMap = make(map[string]map[FunctionType]*RpcBlockFallbackBehavior) | ||
currentWebRules = make(map[string]map[FunctionType]*WebBlockFallbackBehavior) | ||
currentRpcRules = make(map[string]map[FunctionType]*RpcBlockFallbackBehavior) | ||
webRwMux = &sync.RWMutex{} | ||
rpcRwMux = &sync.RWMutex{} | ||
updateRuleMux = &sync.RWMutex{} | ||
) | ||
|
||
func isValidWebFallbackBehavior(behavior *WebBlockFallbackBehavior) bool { | ||
if behavior == nil { | ||
return false | ||
} | ||
if behavior.WebRespContentType != 0 && behavior.WebRespContentType != 1 { | ||
return false | ||
} | ||
if behavior.WebRespStatusCode < 0 || behavior.WebRespStatusCode > 600 { | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
func isValidRpcFallbackBehavior(behavior *RpcBlockFallbackBehavior) bool { | ||
if behavior == nil { | ||
return false | ||
} | ||
if behavior.RpcFallbackMode != 0 && behavior.RpcFallbackMode != 1 { | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
func LoadRules(rules []*Rule) (bool, error) { | ||
resWebRuleMap := make(map[string]map[FunctionType]*WebBlockFallbackBehavior) | ||
resRpcRuleMap := make(map[string]map[FunctionType]*RpcBlockFallbackBehavior) | ||
for _, rule := range rules { | ||
b, err := json.Marshal(rule.FallbackBehavior) | ||
if err != nil { | ||
logging.Warn("[Fallback] marshal web fall back behavior failed", "reason", err.Error()) | ||
continue | ||
} | ||
switch rule.TargetResourceType { | ||
case WebResourceType: | ||
var webBehavior *WebBlockFallbackBehavior | ||
err := json.Unmarshal(b, &webBehavior) | ||
if err != nil { | ||
logging.Warn("[Fallback] unmarshal web fall back behavior failed", "reason", err.Error()) | ||
continue | ||
} | ||
if !isValidWebFallbackBehavior(webBehavior) { | ||
logging.Warn("[Fallback] invalid web fall back behavior", "behavior", webBehavior) | ||
continue | ||
} | ||
|
||
for resource, funcTypeList := range rule.TargetMap { | ||
if resource == "" || len(funcTypeList) == 0 { | ||
continue | ||
} | ||
var behaviorMap map[FunctionType]*WebBlockFallbackBehavior | ||
var ok bool | ||
if behaviorMap, ok = resWebRuleMap[resource]; !ok { | ||
behaviorMap = make(map[FunctionType]*WebBlockFallbackBehavior) | ||
resWebRuleMap[resource] = behaviorMap | ||
} | ||
|
||
for _, functionType := range funcTypeList { | ||
behaviorMap[functionType] = webBehavior | ||
} | ||
} | ||
case RpcResourceType: | ||
var rpcBehavior *RpcBlockFallbackBehavior | ||
err := json.Unmarshal(b, &rpcBehavior) | ||
if err != nil { | ||
logging.Warn("[Fallback] unmarshal rpc fall back behavior failed", "reason", err.Error()) | ||
continue | ||
} | ||
if !isValidRpcFallbackBehavior(rpcBehavior) { | ||
logging.Warn("[Fallback] invalid rpc fall back behavior", "behavior", rpcBehavior) | ||
continue | ||
} | ||
|
||
for resource, funcTypeList := range rule.TargetMap { | ||
var behaviorMap map[FunctionType]*RpcBlockFallbackBehavior | ||
var ok bool | ||
if behaviorMap, ok = resRpcRuleMap[resource]; !ok { | ||
behaviorMap = make(map[FunctionType]*RpcBlockFallbackBehavior) | ||
resRpcRuleMap[resource] = behaviorMap | ||
} | ||
|
||
for _, functionType := range funcTypeList { | ||
behaviorMap[functionType] = rpcBehavior | ||
} | ||
} | ||
default: | ||
logging.Warn("[Fallback] unsupported resource type", "resourceType", rule.TargetResourceType) | ||
continue | ||
} | ||
} | ||
|
||
updateRuleMux.Lock() | ||
defer updateRuleMux.Unlock() | ||
var err error | ||
var updated bool | ||
isEqual := reflect.DeepEqual(currentWebRules, resWebRuleMap) | ||
if !isEqual { | ||
updateErr := onWebRuleUpdate(resWebRuleMap) | ||
if updateErr != nil { | ||
logging.Error(updateErr, "[Fallback] update web rule failed") | ||
err = updateErr | ||
} else { | ||
updated = true | ||
} | ||
} else { | ||
logging.Info("[Fallback] Web load rules is the same with current rules, so ignore load operation.") | ||
} | ||
isEqual = reflect.DeepEqual(currentRpcRules, resRpcRuleMap) | ||
if !isEqual { | ||
updateErr := onRpcRuleUpdate(resRpcRuleMap) | ||
if updateErr != nil { | ||
logging.Error(updateErr, "[Fallback] update rpc rule failed") | ||
err = updateErr | ||
} else { | ||
updated = true | ||
} | ||
} else { | ||
logging.Info("[Fallback] Rpc load rules is the same with current rules, so ignore load operation.") | ||
} | ||
return updated, err | ||
} | ||
|
||
func onWebRuleUpdate(rawWebRuleMap map[string]map[FunctionType]*WebBlockFallbackBehavior) error { | ||
start := util.CurrentTimeNano() | ||
webRwMux.Lock() | ||
webRuleMap = rawWebRuleMap | ||
webRwMux.Unlock() | ||
currentWebRules = rawWebRuleMap | ||
logging.Debug("[Fallback onWebRuleUpdate] Time statistic(ns) for updating web fallback rule", "timeCost", util.CurrentTimeNano()-start) | ||
return nil | ||
} | ||
|
||
func onRpcRuleUpdate(rawRpcRuleMap map[string]map[FunctionType]*RpcBlockFallbackBehavior) error { | ||
start := util.CurrentTimeNano() | ||
rpcRwMux.Lock() | ||
rpcRuleMap = rawRpcRuleMap | ||
rpcRwMux.Unlock() | ||
currentRpcRules = rawRpcRuleMap | ||
logging.Debug("[Fallback onRpcRuleUpdate] Time statistic(ns) for updating rpc fallback rule", "timeCost", util.CurrentTimeNano()-start) | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// 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 fallback | ||
|
||
import ( | ||
"github.com/stretchr/testify/assert" | ||
"testing" | ||
) | ||
|
||
func TestLoadRule(t *testing.T) { | ||
_, err := LoadRules([]*Rule{ | ||
{ | ||
TargetResourceType: WebResourceType, | ||
TargetMap: map[string][]FunctionType{ | ||
"/greet": { | ||
FlowType, | ||
Isolation, | ||
}, | ||
}, | ||
FallbackBehavior: []byte("{\"webFallbackMode\":0,\"webRespContentType\":1,\"webRespMessage\":\"{\\n \\\"abc\\\": 123\\n}\",\"webRespStatusCode\":433}"), | ||
}, | ||
{ | ||
TargetResourceType: WebResourceType, | ||
TargetMap: map[string][]FunctionType{ | ||
"/greet": { | ||
HotspotHttp, | ||
}, | ||
}, | ||
FallbackBehavior: []byte("{\"webFallbackMode\":0,\"webRespContentType\":1,\"webRespMessage\":\"{\\n \\\"abc\\\": 123\\n}\",\"webRespStatusCode\":434}"), | ||
}, | ||
{ | ||
TargetResourceType: WebResourceType, | ||
TargetMap: map[string][]FunctionType{ | ||
"/api/users/:id": { | ||
FlowType, | ||
}, | ||
}, | ||
FallbackBehavior: []byte("{\"webFallbackMode\":0,\"webRespContentType\":1,\"webRespMessage\":\"{\\n \\\"abc\\\": 123\\n}\",\"webRespStatusCode\":400}"), | ||
}, | ||
}) | ||
assert.NoError(t, err) | ||
|
||
assert.Equal(t, 2, len(webRuleMap)) | ||
assert.Equal(t, 0, len(rpcRuleMap)) | ||
|
||
funcTypeMap := webRuleMap["/greet"] | ||
assert.Equal(t, 3, len(funcTypeMap)) | ||
|
||
assert.Equal(t, funcTypeMap[FlowType].WebRespStatusCode, int64(433)) | ||
assert.Equal(t, funcTypeMap[HotspotHttp].WebRespStatusCode, int64(434)) | ||
|
||
funcTypeMap = webRuleMap["/api/users/:id"] | ||
assert.Equal(t, 1, len(funcTypeMap)) | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// 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 fallback | ||
|
||
import ( | ||
"github.com/alibaba/sentinel-golang/core/base" | ||
) | ||
|
||
var blockType2FuncTypeMap = map[base.BlockType]FunctionType{ | ||
base.BlockTypeFlow: FlowType, | ||
base.BlockTypeIsolation: Isolation, | ||
base.BlockTypeHotSpotParamFlow: HotspotRpc, | ||
} | ||
|
||
func getFuncTypeByBlockType(blockType base.BlockType) (FunctionType, bool) { | ||
funcType, ok := blockType2FuncTypeMap[blockType] | ||
return funcType, ok | ||
} | ||
|
||
func GetWebFallbackBehavior(resource string, blockType base.BlockType) (*WebBlockFallbackBehavior, bool) { | ||
functionType, ok := getFuncTypeByBlockType(blockType) | ||
if !ok { | ||
return nil, false | ||
} | ||
|
||
webRwMux.RLock() | ||
defer webRwMux.RUnlock() | ||
|
||
funcMap, ok := webRuleMap[resource] | ||
if !ok { | ||
return nil, false | ||
} | ||
behavior, ok := funcMap[functionType] | ||
if !ok { | ||
return nil, false | ||
} | ||
return behavior, true | ||
} | ||
|
||
func GetRpcFallbackBehavior(resource string, blockType base.BlockType) (*RpcBlockFallbackBehavior, bool) { | ||
functionType, ok := getFuncTypeByBlockType(blockType) | ||
if !ok { | ||
return nil, false | ||
} | ||
|
||
rpcRwMux.RLock() | ||
defer rpcRwMux.RUnlock() | ||
|
||
funcMap, ok := rpcRuleMap[resource] | ||
if !ok { | ||
return nil, false | ||
} | ||
behavior, ok := funcMap[functionType] | ||
if !ok { | ||
return nil, false | ||
} | ||
return behavior, true | ||
} |
Oops, something went wrong.