阿里双11同款,流量防卫兵 Sentinel go 源码解读( 五 )

3.4 Exit
sentinel 对 Resource 进行 Check 后 , 其后续逻辑执行顺序是:

  • 1 如果 RuleCheckSlot.Check() 判定 pass 通过则执行 StatSlot.OnEntryPassed() , 否则 RuleCheckSlot.Check() 判定 reject 则执行 StatSlot.OnEntryBlocked();
  • 2 如果 RuleCheckSlot.Check() 判定 pass 通过 , 则执行本次 Action;
  • 3 如果 RuleCheckSlot.Check() 判定 pass 通过 , 则执行 SentinelEntry.Exit() --> SlotChain.ext() --> StatSlot.OnCompleted()。
第三步骤的调用链路如下:
StatSlot.OnCompleted()
// core/flow/standalone_stat_slot.gotype StandaloneStatSlot struct {}func (s StandaloneStatSlot) OnEntryPassed(ctx *base.EntryContext) {res := ctx.Resource.Name()for _, tc := range getTrafficControllerListFor(res) {if !tc.boundStat.reuseResourceStat {if tc.boundStat.writeOnlyMetric != nil {tc.boundStat.writeOnlyMetric.AddCount(base.MetricEventPass, int64(ctx.Input.AcquireCount))}}}}func (s StandaloneStatSlot) OnEntryBlocked(ctx *base.EntryContext, blockError *base.BlockError) {// Do nothing}func (s StandaloneStatSlot) OnCompleted(ctx *base.EntryContext) {// Do nothing}SlotChain.exit()
// core/base/slot_chain.gotype SlotChain struct {}func (sc *SlotChain) exit(ctx *EntryContext) {// The OnCompleted is called only when entry passedif ctx.IsBlocked() {return}for _, s := range sc.stats {s.OnCompleted(ctx)}}SentinelEntry.Exit()
// core/base/entry.gotype SentinelEntry struct {sc *SlotChainexitCtl sync.Once}func (e *SentinelEntry) Exit() {e.exitCtl.Do(func() {if e.sc != nil {e.sc.exit(ctx)}})}从上面执行可见 , StatSlot.OnCompleted() 是在 Action 【如一次 RPC 的请求-响应 Invokation】完成之后调用的 。 如果有的组件需要计算一次 Action 的时间耗费 RT , 就在其对应的 StatSlot.OnCompleted() 中依据 EntryContext.startTime 完成时间耗费计算 。
3.5 SlotChain
Sentinel 本质是一个流控包 , 不仅提供了限流功能 , 还提供了众多其他诸如自适应流量保护、熔断降级、冷启动、全局流量 Metrics 结果等功能流控组件 , Sentinel-Go 包定义了一个 SlotChain 实体存储其所有的流控组件 。
// core/base/slot_chain.go// SlotChain hold all system slots and customized slot.// SlotChain support plug-in slots developed by developer.type SlotChain struct {statPres[]StatPrepareSlotruleChecks []RuleCheckSlotstats[]StatSlot}// The entrance of slot chain// Return the TokenResult and nil if internal panic.func (sc *SlotChain) Entry(ctx *EntryContext) *TokenResult {// execute prepare slotsps := sc.statPresif len(sps) > 0 {for _, s := range sps {s.Prepare(ctx)}}// execute rule based checking slotrcs := sc.ruleChecksvar ruleCheckRet *TokenResultif len(rcs) > 0 {for _, s := range rcs {sr := s.Check(ctx)if sr == nil {// nil equals to check passcontinue}// check slot resultif sr.IsBlocked() {ruleCheckRet = srbreak}}}if ruleCheckRet == nil {ctx.RuleCheckResult.ResetToPass()} else {ctx.RuleCheckResult = ruleCheckRet}// execute statistic slotss := sc.statsruleCheckRet = ctx.RuleCheckResultif len(ss) > 0 {for _, s := range ss {// indicate the result of rule based checking slot.if !ruleCheckRet.IsBlocked() {s.OnEntryPassed(ctx)} else {// The block error should not be nil.s.OnEntryBlocked(ctx, ruleCheckRet.blockErr)}}}return ruleCheckRet}func (sc *SlotChain) exit(ctx *EntryContext) {if ctx == nil || ctx.Entry() == nil {logging.Error(errors.New("nil EntryContext or SentinelEntry"), "")return}// The OnCompleted is called only when entry passedif ctx.IsBlocked() {return}for _, s := range sc.stats {s.OnCompleted(ctx)}// relieve the context here}建议:Sentinel 包针对某个 Resource 无法确知其使用了那个组件 , 在运行时会针对某个 Resource 的 EntryContext 依次执行所有的组件的 Rule 。 Sentinel-golang 为何不给用户相关用户提供一个接口让其设置使用的流控组件集合 , 以减少下面函数 SlotChain.Entry() 中执行 RuleCheckSlot.Check() 执行次数?相关改进已经提交到 pr 264【补充 , 代码已合并 , 据负责人压测后回复 sentinel-go 效率整体提升 15%】 。
globalSlotChain
Sentinel-Go 定义了一个 SlotChain 的 package 级别的全局私有变量 globalSlotChain 用于存储其所有的流控组件对象 。 相关代码示例如下 。 因本文只关注限流组件 , 所以下面只给出了限流组件的注册代码 。
// api/slot_chain.gofunc BuildDefaultSlotChain() *base.SlotChain {sc := base.NewSlotChain()sc.AddStatPrepareSlotLast( --tt-darkmode-color: #6A737D;">Entry
在 Sentinel-Go 对外的最重要的入口函数 api/api.go:Entry() 中 , globalSlotChain 会作为 EntryOptions 的 SlotChain 参数被使用 。
// api/api.go// Entry is the basic API of Sentinel.func Entry(resource string, opts ...EntryOption) (*base.SentinelEntry, *base.BlockError) {options := entryOptsPool.Get().(*EntryOptions)options.slotChain = globalSlotChainreturn entry(resource, options)}