api/api.go (133 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 api
import (
"sync"
"github.com/alibaba/sentinel-golang/core/base"
)
var entryOptsPool = sync.Pool{
New: func() interface{} {
return &EntryOptions{
resourceType: base.ResTypeCommon,
entryType: base.Outbound,
batchCount: 1,
flag: 0,
slotChain: nil,
args: nil,
attachments: nil,
}
},
}
// EntryOptions represents the options of a Sentinel resource entry.
type EntryOptions struct {
resourceType base.ResourceType
entryType base.TrafficType
batchCount uint32
flag int32
slotChain *base.SlotChain
args []interface{}
attachments map[interface{}]interface{}
}
func (o *EntryOptions) Reset() {
o.resourceType = base.ResTypeCommon
o.entryType = base.Outbound
o.batchCount = 1
o.flag = 0
o.slotChain = nil
o.args = o.args[:0]
o.attachments = nil
}
type EntryOption func(*EntryOptions)
// WithResourceType sets the resource entry with the given resource type.
func WithResourceType(resourceType base.ResourceType) EntryOption {
return func(opts *EntryOptions) {
opts.resourceType = resourceType
}
}
// WithTrafficType sets the resource entry with the given traffic type.
func WithTrafficType(entryType base.TrafficType) EntryOption {
return func(opts *EntryOptions) {
opts.entryType = entryType
}
}
// DEPRECATED: use WithBatchCount instead.
// WithAcquireCount sets the resource entry with the given batch count (by default 1).
func WithAcquireCount(acquireCount uint32) EntryOption {
return func(opts *EntryOptions) {
opts.batchCount = acquireCount
}
}
// WithBatchCount sets the resource entry with the given batch count (by default 1).
func WithBatchCount(batchCount uint32) EntryOption {
return func(opts *EntryOptions) {
opts.batchCount = batchCount
}
}
// WithFlag sets the resource entry with the given additional flag.
func WithFlag(flag int32) EntryOption {
return func(opts *EntryOptions) {
opts.flag = flag
}
}
// WithArgs sets the resource entry with the given additional parameters.
func WithArgs(args ...interface{}) EntryOption {
return func(opts *EntryOptions) {
opts.args = append(opts.args, args...)
}
}
// WithSlotChain sets the slot chain.
func WithSlotChain(chain *base.SlotChain) EntryOption {
return func(opts *EntryOptions) {
opts.slotChain = chain
}
}
// WithAttachment set the resource entry with the given k-v pair
func WithAttachment(key interface{}, value interface{}) EntryOption {
return func(opts *EntryOptions) {
if opts.attachments == nil {
opts.attachments = make(map[interface{}]interface{}, 8)
}
opts.attachments[key] = value
}
}
// WithAttachments set the resource entry with the given k-v pairs
func WithAttachments(data map[interface{}]interface{}) EntryOption {
return func(opts *EntryOptions) {
if opts.attachments == nil {
opts.attachments = make(map[interface{}]interface{}, len(data))
}
for key, value := range data {
opts.attachments[key] = value
}
}
}
// Entry is the basic API of Sentinel.
func Entry(resource string, opts ...EntryOption) (*base.SentinelEntry, *base.BlockError) {
options := entryOptsPool.Get().(*EntryOptions)
defer func() {
options.Reset()
entryOptsPool.Put(options)
}()
for _, opt := range opts {
opt(options)
}
if options.slotChain == nil {
options.slotChain = GlobalSlotChain()
}
return entry(resource, options)
}
func entry(resource string, options *EntryOptions) (*base.SentinelEntry, *base.BlockError) {
rw := base.NewResourceWrapper(resource, options.resourceType, options.entryType)
sc := options.slotChain
if sc == nil {
return base.NewSentinelEntry(nil, rw, nil), nil
}
// Get context from pool.
ctx := sc.GetPooledContext()
ctx.Resource = rw
ctx.Input.BatchCount = options.batchCount
ctx.Input.Flag = options.flag
if len(options.args) != 0 {
ctx.Input.Args = options.args
}
if len(options.attachments) != 0 {
ctx.Input.Attachments = options.attachments
}
e := base.NewSentinelEntry(ctx, rw, sc)
ctx.SetEntry(e)
r := sc.Entry(ctx)
if r == nil {
// This indicates internal error in some slots, so just pass
return e, nil
}
if r.Status() == base.ResultStatusBlocked {
// r will be put to Pool in calling Exit()
// must finish the lifecycle of r.
blockErr := base.NewBlockErrorFromDeepCopy(r.BlockError())
e.Exit()
return nil, blockErr
}
return e, nil
}