core/base/entry.go (83 lines of code) (raw):
// 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 base
import (
"sync"
"github.com/pkg/errors"
"github.com/alibaba/sentinel-golang/logging"
)
type ExitHandler func(entry *SentinelEntry, ctx *EntryContext) error
type SentinelEntry struct {
res *ResourceWrapper
// one entry bounds with one context
ctx *EntryContext
exitHandlers []ExitHandler
// each entry holds a slot chain.
// it means this entry will go through the sc
sc *SlotChain
exitCtl sync.Once
}
func NewSentinelEntry(ctx *EntryContext, rw *ResourceWrapper, sc *SlotChain) *SentinelEntry {
return &SentinelEntry{
res: rw,
ctx: ctx,
exitHandlers: make([]ExitHandler, 0),
sc: sc,
}
}
func (e *SentinelEntry) WhenExit(exitHandler ExitHandler) {
e.exitHandlers = append(e.exitHandlers, exitHandler)
}
func (e *SentinelEntry) SetError(err error) {
if e.ctx != nil {
e.ctx.SetError(err)
}
}
func (e *SentinelEntry) SetPair(key, val interface{}) {
if e.ctx != nil {
e.ctx.SetPair(key, val)
}
}
func (e *SentinelEntry) Context() *EntryContext {
return e.ctx
}
func (e *SentinelEntry) Resource() *ResourceWrapper {
return e.res
}
type ExitOptions struct {
err error
}
type ExitOption func(*ExitOptions)
func WithError(err error) ExitOption {
return func(opts *ExitOptions) {
opts.err = err
}
}
func (e *SentinelEntry) Exit(exitOps ...ExitOption) {
var options = ExitOptions{
err: nil,
}
for _, opt := range exitOps {
opt(&options)
}
ctx := e.ctx
if ctx == nil {
return
}
if options.err != nil {
ctx.SetError(options.err)
}
e.exitCtl.Do(func() {
defer func() {
if err := recover(); err != nil {
logging.Error(errors.Errorf("%+v", err), "Sentinel internal panic in SentinelEntry.Exit()")
}
if e.sc != nil {
e.sc.RefurbishContext(ctx)
}
}()
for _, handler := range e.exitHandlers {
if err := handler(e, ctx); err != nil {
logging.Error(err, "Fail to execute exitHandler in SentinelEntry.Exit()", "resource", e.Resource().Name())
}
}
if e.sc != nil {
e.sc.exit(ctx)
}
})
}