parent
457908d64b
commit
b59ec79e5c
@ -0,0 +1,57 @@ |
||||
package criticality |
||||
|
||||
// Criticality is
|
||||
type Criticality string |
||||
|
||||
// criticality
|
||||
var ( |
||||
// EmptyCriticality is used to mark any invalid criticality, and the empty criticality will be parsed as the default criticality later.
|
||||
EmptyCriticality = Criticality("") |
||||
// CriticalPlus is reserved for the most critical requests, those that will result in serious user-visible impact if they fail.
|
||||
CriticalPlus = Criticality("CRITICAL_PLUS") |
||||
// Critical is the default value for requests sent from production jobs. These requests will result in user-visible impact, but the impact may be less severe than those of CRITICAL_PLUS. Services are expected to provision enough capacity for all expected CRITICAL and CRITICAL_PLUS traffic.
|
||||
Critical = Criticality("CRITICAL") |
||||
// SheddablePlus is traffic for which partial unavailability is expected. This is the default for batch jobs, which can retry requests minutes or even hours later.
|
||||
SheddablePlus = Criticality("SHEDDABLE_PLUS") |
||||
// Sheddable is traffic for which frequent partial unavailability and occasional full unavailability is expected.
|
||||
Sheddable = Criticality("SHEDDABLE") |
||||
|
||||
// higher is more critical
|
||||
_criticalityEnum = map[Criticality]int{ |
||||
CriticalPlus: 40, |
||||
Critical: 30, |
||||
SheddablePlus: 20, |
||||
Sheddable: 10, |
||||
} |
||||
|
||||
_defaultCriticality = Critical |
||||
) |
||||
|
||||
// Value is used to get criticality value, higher value is more critical.
|
||||
func Value(in Criticality) int { |
||||
v, ok := _criticalityEnum[in] |
||||
if !ok { |
||||
return _criticalityEnum[_defaultCriticality] |
||||
} |
||||
return v |
||||
} |
||||
|
||||
// Higher will compare the input criticality with self, return true if the input is more critical than self.
|
||||
func (c Criticality) Higher(in Criticality) bool { |
||||
return Value(in) > Value(c) |
||||
} |
||||
|
||||
// Parse will parse raw criticality string as valid critality. Any invalid input will parse as empty criticality.
|
||||
func Parse(raw string) Criticality { |
||||
crtl := Criticality(raw) |
||||
if _, ok := _criticalityEnum[crtl]; ok { |
||||
return crtl |
||||
} |
||||
return EmptyCriticality |
||||
} |
||||
|
||||
// Exist is used to check criticality is exist in several enumeration.
|
||||
func Exist(c Criticality) bool { |
||||
_, ok := _criticalityEnum[c] |
||||
return ok |
||||
} |
@ -0,0 +1,21 @@ |
||||
package blademaster |
||||
|
||||
import ( |
||||
criticalityPkg "github.com/bilibili/kratos/pkg/net/criticality" |
||||
"github.com/bilibili/kratos/pkg/net/metadata" |
||||
|
||||
"github.com/pkg/errors" |
||||
) |
||||
|
||||
// Criticality is
|
||||
func Criticality(pathCriticality criticalityPkg.Criticality) HandlerFunc { |
||||
if !criticalityPkg.Exist(pathCriticality) { |
||||
panic(errors.Errorf("This criticality is not exist: %s", pathCriticality)) |
||||
} |
||||
return func(ctx *Context) { |
||||
md, ok := metadata.FromContext(ctx) |
||||
if ok { |
||||
md[metadata.Criticality] = string(pathCriticality) |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,137 @@ |
||||
package blademaster |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
"io/ioutil" |
||||
"net/http" |
||||
"sync" |
||||
"sync/atomic" |
||||
"testing" |
||||
"time" |
||||
|
||||
criticalityPkg "github.com/bilibili/kratos/pkg/net/criticality" |
||||
"github.com/bilibili/kratos/pkg/net/metadata" |
||||
xtime "github.com/bilibili/kratos/pkg/time" |
||||
|
||||
"github.com/stretchr/testify/assert" |
||||
) |
||||
|
||||
var ( |
||||
sonce sync.Once |
||||
|
||||
SockAddr = "localhost:18080" |
||||
curEngine atomic.Value |
||||
) |
||||
|
||||
func uri(base, path string) string { |
||||
return fmt.Sprintf("%s://%s%s", "http", base, path) |
||||
} |
||||
|
||||
func shutdown() { |
||||
if err := curEngine.Load().(*Engine).Shutdown(context.TODO()); err != nil { |
||||
panic(err) |
||||
} |
||||
} |
||||
|
||||
func setupHandler(engine *Engine) { |
||||
// set the global timeout is 2 second
|
||||
engine.conf.Timeout = xtime.Duration(time.Second * 2) |
||||
|
||||
engine.Ping(func(ctx *Context) { |
||||
ctx.AbortWithStatus(200) |
||||
}) |
||||
|
||||
engine.GET("/criticality/api", Criticality(criticalityPkg.Critical), func(ctx *Context) { |
||||
ctx.String(200, "%s", metadata.String(ctx, metadata.Criticality)) |
||||
}) |
||||
engine.GET("/criticality/none/api", func(ctx *Context) { |
||||
ctx.String(200, "%s", metadata.String(ctx, metadata.Criticality)) |
||||
}) |
||||
} |
||||
|
||||
func startServer() { |
||||
e := DefaultServer(nil) |
||||
setupHandler(e) |
||||
go e.Run(SockAddr) |
||||
curEngine.Store(e) |
||||
time.Sleep(time.Second) |
||||
} |
||||
|
||||
func TestCriticality(t *testing.T) { |
||||
startServer() |
||||
defer shutdown() |
||||
|
||||
tests := []*struct { |
||||
path string |
||||
crtl criticalityPkg.Criticality |
||||
expected criticalityPkg.Criticality |
||||
}{ |
||||
{ |
||||
"/criticality/api", |
||||
criticalityPkg.EmptyCriticality, |
||||
criticalityPkg.Critical, |
||||
}, |
||||
{ |
||||
"/criticality/api", |
||||
criticalityPkg.CriticalPlus, |
||||
criticalityPkg.Critical, |
||||
}, |
||||
{ |
||||
"/criticality/api", |
||||
criticalityPkg.SheddablePlus, |
||||
criticalityPkg.Critical, |
||||
}, |
||||
} |
||||
client := &http.Client{} |
||||
for _, testCase := range tests { |
||||
req, err := http.NewRequest("GET", uri(SockAddr, testCase.path), nil) |
||||
assert.NoError(t, err) |
||||
req.Header.Set(_httpHeaderCriticality, string(testCase.crtl)) |
||||
resp, err := client.Do(req) |
||||
assert.NoError(t, err) |
||||
defer resp.Body.Close() |
||||
body, err := ioutil.ReadAll(resp.Body) |
||||
assert.NoError(t, err) |
||||
assert.Equal(t, testCase.expected, criticalityPkg.Criticality(body)) |
||||
} |
||||
} |
||||
|
||||
func TestNoneCriticality(t *testing.T) { |
||||
startServer() |
||||
defer shutdown() |
||||
|
||||
tests := []*struct { |
||||
path string |
||||
crtl criticalityPkg.Criticality |
||||
expected criticalityPkg.Criticality |
||||
}{ |
||||
{ |
||||
"/criticality/none/api", |
||||
criticalityPkg.EmptyCriticality, |
||||
criticalityPkg.Critical, |
||||
}, |
||||
{ |
||||
"/criticality/none/api", |
||||
criticalityPkg.CriticalPlus, |
||||
criticalityPkg.CriticalPlus, |
||||
}, |
||||
{ |
||||
"/criticality/none/api", |
||||
criticalityPkg.SheddablePlus, |
||||
criticalityPkg.SheddablePlus, |
||||
}, |
||||
} |
||||
client := &http.Client{} |
||||
for _, testCase := range tests { |
||||
req, err := http.NewRequest("GET", uri(SockAddr, testCase.path), nil) |
||||
assert.NoError(t, err) |
||||
req.Header.Set(_httpHeaderCriticality, string(testCase.crtl)) |
||||
resp, err := client.Do(req) |
||||
assert.NoError(t, err) |
||||
defer resp.Body.Close() |
||||
body, err := ioutil.ReadAll(resp.Body) |
||||
assert.NoError(t, err) |
||||
assert.Equal(t, testCase.expected, criticalityPkg.Criticality(body)) |
||||
} |
||||
} |
Loading…
Reference in new issue