微服务的
稳定性
Sentinel特性
碰到的
一些“坑”
展望未来
云原生
核心组件设计
01
02
03
04
06
流控降级
最佳实践
05
流量激增
不稳定的服务
不稳定的下游服务导致RPC超时或则抛出异常,导致自身不可用,进而导致级联失败。
微服务的
稳定性
Sentinel特性
碰到的
一些“坑”
展望未来
云原生
核心组件设计
02
01
03
04
06
流控降级
最佳实践
05
流量控制
速率控制
熔断降级
热点限流
系统负载保护
动态规则配置:Etcd、Consul......
监控日志
规则配置
Dubbo-go
gRPC
gin
echo
自定义扩展
资源
规则
资源
规则
规则
规则
规则
规则
定义资源
配置规则
Guard
Slot chain
prepare slot / check slot / stat slot
Resource Prepare Slot
SystemSlot
CircuitBreakerSlot
FlowSlot
HotParamsSlot
Rules
resource1
statNode
resource2
statNode
resource3
statNode
Stat Slot
0
1
2
3
4
5
6
......
timestamp:158242177000
passQPS:1200
blockedQPS:30
completedQPS:1190
errorQPS:10
rt:30
timestamp:158242178000
passQPS:1100
blockedQPS:30
completedQPS:1090
errorQPS:10
rt:30
微服务的
稳定性
Sentinel特性
碰到的
一些“坑”
展望未来
云原生
核心组件设计
03
01
02
04
06
流控降级
最佳实践
05
customize bucket to
reuse sliding Windows
func (la *LeapArray) currentBucketOfTime(now uint64, bg BucketGenerator) (*BucketWrap, error) {
idx := la.calculateTimeIdx(now)
bucketStart := calculateStartTime(now, la.bucketLengthInMs)
//spin to get the current BucketWrap
for {
old := la.array.get(idx)
if old == nil {
new BucketWrap
if la.array.compareAndSet(idx, nil, newWrap) {
return newWrap, nil
} else {
runtime.Gosched()
}
} else if bucketStart == atomic.LoadUint64(&old.BucketStart) {
return old, nil
} else if bucketStart > atomic.LoadUint64(&old.BucketStart) {
if la.updateLock.TryLock() {
old = bg.ResetBucketTo(old, bucketStart)
la.updateLock.Unlock()
return old, nil
} else {
runtime.Gosched()
}
} else if bucketStart < atomic.LoadUint64(&old.BucketStart) {
// reserve for some special case (e.g. when occupying "future" buckets).
return nil, error
}
}
}
Utilize atomic to reduce the the granularity of lock
Strategy
cb1
cb2
......
cb1
cb2
......
cb1
cb2
......
resource1
resource2
resource3
Rescource CB Map
Entry
get resource's cb slice
try pass
based on state
Load the newer Rules
statistics equivalent
TokenResult
Probe if timeout
Exit
handle request completion:
1. update statistic
2. update cb state machine
3. notify state change
OnTransformToClosed
OnTransformToOpen
OnTransformToHalfOpen
stat
cb1
cb2
cb3
Statistic of CB
stat
stat
circuit breaking based on state machine
Sentinel is based on rules
Config Center
Etcd / Conful...
Push
Sentinel Dashboard/
Config Center Dashboard/
K8s CRD
Sentinel Datasource
Watch && Notify
Rule Manager
Update Rules
Machine1
Sentinel Datasource
Rule Manager
Update Rules
Machine2
Sentinel Datasource
Rule Manager
Update Rules
Machine3
Datasource scenario
type PropertyConverter func(src []byte) (interface{}, error)
type PropertyUpdater func(data interface{}) error
type DefaultPropertyHandler struct {
lastUpdateProperty interface{}
converter PropertyConverter
updater PropertyUpdater
}
func (h *DefaultPropertyHandler) Handle(src []byte) error {
defer func() {
if err := recover(); err != nil && logger != nil {
logger.Panicf(......)
}
}()
realProperty, err := h.converter(src)
if err != nil {
return err
}
isConsistent := h.isPropertyConsistent(realProperty)
if isConsistent {
return nil
}
return h.updater(realProperty)
}
CPU:Intel(R) Xeon(R) CPU E5-2640 v3 @ 2.60GHz (32 Cores)
OS:Red Hat 4.8.2-16
golang version: 1.14.3
测试单协程/并发接入 Sentinel 与不接入 Sentinel 吞吐量的对比。通过执行一些 CPU 密集型操作(数组排序)来模拟不同 QPS 下性能表现:
单线程测试不同Base QPS下性能表现
1)单机 QPS 非常大(16W+)场景,Sentinel 带来的性能损耗会比较大。这种情况业务逻辑本身的耗时非常小,而 Sentinel 一系列的统计、检查操作会消耗一定的时间。常见的场景有缓存读取操作。
2)单机 QPS 在 6W 以下的时候,Sentinel 的性能损耗就比较小了,对大多数场景来说都适用。
排序数组长度是200时候(Base QPS:169204),并发4/8/16/32/32+并发下的性能表现:
内存损耗:6000 个resource循环跑(单机的极端场景,目前默认最多支持 6000 个resource)
轻量级
微服务的
稳定性
Sentinel特性
碰到的
一些“坑”
展望未来
云原生
核心组件设计
04
01
02
03
06
流控降级
最佳实践
05
type AtomicBucketArray struct {
// The base address of real data array
base unsafe.Pointer
// The length of slice(array)
// Readonly
length int
data []*Bucket
}
sliHeader := (*util.SliceHeader)(unsafe.Pointer(&a.data))
a.base = unsafe.Pointer((**Bucket)(sliHeader.Data))
WHERE?
WHY?
data race is undefined behavior
Case Study:
Sentinel core slot chain pipeline
type RuleCheckSlot interface {
// Check function do some validation
// It can break off the slot pipeline
// Each TokenResult will return check result
Check(ctx *EntryContext) *TokenResult
}
type TrafficShapingChecker interface {
DoCheck(node base.StatNode, acquireCount uint32, threshold float64) *base.TokenResult
}
type CircuitBreaker interface {
Check(ctx *base.EntryContext) *base.TokenResult
}
Sentinel's check is based on rules
The number of temporary variables, TokenResult,
is very large.
So Pool
exit
Let's reappear scenario:
type EntryContext struct {
....
// RuleCheckResult represents the result of
// the Entry slot chain
RuleCheckResult *TokenResult
....
}
Troubleshooting:
Case Study:
微服务的
稳定性
Sentinel特性
碰到的
一些“坑”
展望未来
云原生
核心组件设计
05
01
02
03
06
流控降级
最佳实践
04
RPC architecture
RPC Provider
RPC Consumer
微服务的
稳定性
Sentinel特性
碰到的
一些“坑”
展望未来
云原生
核心组件设计
06
01
02
03
05
流控降级
最佳实践
04
弹性指标:
Autoscale instead of flow control?
参与贡献途径
激励: