From 983452aa1e855f189928a12aab296c3406461857 Mon Sep 17 00:00:00 2001 From: louyuting <1849491904@qq.com> Date: Wed, 3 Jun 2020 23:53:40 +0800 Subject: [PATCH 1/3] optimize performance. 1. pool EntryOption 2. optimize EntryContext pool 3. optimize time.Now --- api/api.go | 64 +++++++++++++++++++++++++++++------------ api/init.go | 5 ++++ core/base/context.go | 20 +++++++------ core/base/slot_chain.go | 7 +++++ core/config/config.go | 4 +++ core/config/entity.go | 3 ++ util/time.go | 4 +++ util/time_test.go | 16 +++++++++++ util/time_ticker.go | 23 +++++++++++++++ util/util_test.go | 16 ++++------- 10 files changed, 125 insertions(+), 37 deletions(-) create mode 100644 util/time_ticker.go diff --git a/api/api.go b/api/api.go index ecaade3b..e832fb94 100644 --- a/api/api.go +++ b/api/api.go @@ -1,9 +1,25 @@ 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, + acquireCount: 1, + flag: 0, + slotChain: nil, + args: nil, + attachments: nil, + } + }, +} + // EntryOptions represents the options of a Sentinel resource entry. type EntryOptions struct { resourceType base.ResourceType @@ -15,6 +31,16 @@ type EntryOptions struct { attachments map[interface{}]interface{} } +func (o *EntryOptions) Reset() { + o.resourceType = base.ResTypeCommon + o.entryType = base.Outbound + o.acquireCount = 1 + o.flag = 0 + o.slotChain = nil + o.args = nil + o.attachments = nil +} + type EntryOption func(*EntryOptions) // WithResourceType sets the resource entry with the given resource type. @@ -55,6 +81,9 @@ func WithArgs(args ...interface{}) EntryOption { // 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{}) + } opts.attachments[key] = value } } @@ -62,6 +91,9 @@ func WithAttachment(key interface{}, value interface{}) EntryOption { // WithAttachment 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{}) + } for key, value := range data { opts.attachments[key] = value } @@ -70,20 +102,14 @@ func WithAttachments(data map[interface{}]interface{}) EntryOption { // Entry is the basic API of Sentinel. func Entry(resource string, opts ...EntryOption) (*base.SentinelEntry, *base.BlockError) { - var options = EntryOptions{ - resourceType: base.ResTypeCommon, - entryType: base.Outbound, - acquireCount: 1, - flag: 0, - slotChain: globalSlotChain, - args: []interface{}{}, - attachments: make(map[interface{}]interface{}), - } + options := entryOptsPool.Get().(*EntryOptions) + options.slotChain = globalSlotChain + for _, opt := range opts { - opt(&options) + opt(options) } - return entry(resource, &options) + return entry(resource, options) } func entry(resource string, options *EntryOptions) (*base.SentinelEntry, *base.BlockError) { @@ -96,14 +122,16 @@ func entry(resource string, options *EntryOptions) (*base.SentinelEntry, *base.B // Get context from pool. ctx := sc.GetPooledContext() ctx.Resource = rw - ctx.Input = &base.SentinelInput{ - AcquireCount: options.acquireCount, - Flag: options.flag, - Args: options.args, - Attachments: options.attachments, + ctx.Input.AcquireCount = options.acquireCount + ctx.Input.Flag = options.flag + if len(options.args) != 0 { + ctx.Input.Args = options.args } - ctx.Data = make(map[interface{}]interface{}) - + if len(options.attachments) != 0 { + ctx.Input.Attachments = options.attachments + } + options.Reset() + entryOptsPool.Put(options) e := base.NewSentinelEntry(ctx, rw, sc) r := sc.Entry(ctx) if r == nil { diff --git a/api/init.go b/api/init.go index 10c3d481..9af51cc1 100644 --- a/api/init.go +++ b/api/init.go @@ -3,6 +3,8 @@ package api import ( "fmt" + "github.com/alibaba/sentinel-golang/util" + "github.com/alibaba/sentinel-golang/core/config" "github.com/alibaba/sentinel-golang/core/log/metric" "github.com/alibaba/sentinel-golang/core/system" @@ -43,5 +45,8 @@ func initCoreComponents() (err error) { } system.InitCollector(config.SystemStatCollectIntervalMs()) + if config.UseTimeTicker() { + util.StartTimeTicker() + } return err } diff --git a/core/base/context.go b/core/base/context.go index dc597cdb..47e7257e 100644 --- a/core/base/context.go +++ b/core/base/context.go @@ -65,12 +65,14 @@ type SentinelInput struct { Attachments map[interface{}]interface{} } -func newEmptyInput() *SentinelInput { - return &SentinelInput{ - AcquireCount: 1, - Flag: 0, - Args: make([]interface{}, 0, 0), - Attachments: make(map[interface{}]interface{}), +func (i *SentinelInput) reset() { + i.AcquireCount = 1 + i.Flag = 0 + if len(i.Args) != 0 { + i.Args = make([]interface{}, 0) + } + if len(i.Attachments) != 0 { + i.Attachments = make(map[interface{}]interface{}) } } @@ -82,11 +84,13 @@ func (ctx *EntryContext) Reset() { ctx.rt = 0 ctx.Resource = nil ctx.StatNode = nil - ctx.Input = nil + ctx.Input.reset() if ctx.RuleCheckResult == nil { ctx.RuleCheckResult = NewTokenResultPass() } else { ctx.RuleCheckResult.ResetToPass() } - ctx.Data = nil + if len(ctx.Data) != 0 { + ctx.Data = make(map[interface{}]interface{}) + } } diff --git a/core/base/slot_chain.go b/core/base/slot_chain.go index f59502eb..c6a39dcf 100644 --- a/core/base/slot_chain.go +++ b/core/base/slot_chain.go @@ -66,6 +66,13 @@ func NewSlotChain() *SlotChain { New: func() interface{} { ctx := NewEmptyEntryContext() ctx.RuleCheckResult = NewTokenResultPass() + ctx.Data = make(map[interface{}]interface{}) + ctx.Input = &SentinelInput{ + AcquireCount: 1, + Flag: 0, + Args: make([]interface{}, 0), + Attachments: make(map[interface{}]interface{}), + } return ctx }, }, diff --git a/core/config/config.go b/core/config/config.go index 168907ec..b4feab03 100644 --- a/core/config/config.go +++ b/core/config/config.go @@ -180,3 +180,7 @@ func MetricLogMaxFileAmount() uint32 { func SystemStatCollectIntervalMs() uint32 { return globalCfg.Sentinel.Stat.System.CollectIntervalMs } + +func UseTimeTicker() bool { + return globalCfg.Sentinel.UseTimeTicker +} diff --git a/core/config/entity.go b/core/config/entity.go index 4e700657..a9563ccc 100644 --- a/core/config/entity.go +++ b/core/config/entity.go @@ -23,6 +23,8 @@ type SentinelConfig struct { Log LogConfig // Stat represents configuration items related to statistics. Stat StatConfig + // UseTimeTicker indicates whether use time ticker to cache now time(ms) + UseTimeTicker bool } // LogConfig represent the configuration of logging in Sentinel. @@ -79,6 +81,7 @@ func NewDefaultConfig() *Entity { CollectIntervalMs: DefaultSystemStatCollectIntervalMs, }, }, + UseTimeTicker: true, }, } } diff --git a/util/time.go b/util/time.go index 86419363..560ee81d 100644 --- a/util/time.go +++ b/util/time.go @@ -24,6 +24,10 @@ func FormatDate(tsMillis uint64) string { // Returns the current Unix timestamp in milliseconds. func CurrentTimeMillis() uint64 { + tickerNow := CurrentTimeMillWithTicker() + if tickerNow > uint64(0) { + return tickerNow + } return uint64(time.Now().UnixNano()) / UnixTimeUnitOffset } diff --git a/util/time_test.go b/util/time_test.go index d0572868..922c20b7 100644 --- a/util/time_test.go +++ b/util/time_test.go @@ -91,3 +91,19 @@ func TestCurrentTimeNano(t *testing.T) { got := CurrentTimeNano() fmt.Println(got) } + +func BenchmarkCurrentTimeInMs(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + CurrentTimeMillis() + } +} + +func BenchmarkCurrentTimeInMsWithTicker(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + CurrentTimeMillWithTicker() + } +} diff --git a/util/time_ticker.go b/util/time_ticker.go new file mode 100644 index 00000000..3a12fdff --- /dev/null +++ b/util/time_ticker.go @@ -0,0 +1,23 @@ +package util + +import ( + "sync/atomic" + "time" +) + +var nowInMs = uint64(0) + +func StartTimeTicker() { + atomic.StoreUint64(&nowInMs, uint64(time.Now().UnixNano())/UnixTimeUnitOffset) + go func() { + for { + now := uint64(time.Now().UnixNano()) / UnixTimeUnitOffset + atomic.StoreUint64(&nowInMs, now) + time.Sleep(time.Millisecond) + } + }() +} + +func CurrentTimeMillWithTicker() uint64 { + return atomic.LoadUint64(&nowInMs) +} diff --git a/util/util_test.go b/util/util_test.go index 11694e60..0f4c9fa4 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -1,13 +1,7 @@ package util -import ( - "testing" - - "github.com/alibaba/sentinel-golang/logging" -) - -func TestWithRecoverGo(t *testing.T) { - go RunWithRecover(func() { - panic("internal error!\n") - }, logging.GetDefaultLogger()) -} +//func TestWithRecoverGo(t *testing.T) { +// go RunWithRecover(func() { +// panic("internal error!\n") +// }, logging.GetDefaultLogger()) +//} From b272bcce00394241e5b7c4e55cf9d64a9e113591 Mon Sep 17 00:00:00 2001 From: louyuting <1849491904@qq.com> Date: Wed, 3 Jun 2020 23:57:04 +0800 Subject: [PATCH 2/3] remove unused ut --- api/init.go | 3 +-- util/util_test.go | 7 ------- 2 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 util/util_test.go diff --git a/api/init.go b/api/init.go index 9af51cc1..a37d9384 100644 --- a/api/init.go +++ b/api/init.go @@ -3,11 +3,10 @@ package api import ( "fmt" - "github.com/alibaba/sentinel-golang/util" - "github.com/alibaba/sentinel-golang/core/config" "github.com/alibaba/sentinel-golang/core/log/metric" "github.com/alibaba/sentinel-golang/core/system" + "github.com/alibaba/sentinel-golang/util" ) // InitDefault initializes Sentinel using the configuration from system diff --git a/util/util_test.go b/util/util_test.go deleted file mode 100644 index 0f4c9fa4..00000000 --- a/util/util_test.go +++ /dev/null @@ -1,7 +0,0 @@ -package util - -//func TestWithRecoverGo(t *testing.T) { -// go RunWithRecover(func() { -// panic("internal error!\n") -// }, logging.GetDefaultLogger()) -//} From 2c26ed10c267bb65f7ff5b36a8d4d8432a0ae417 Mon Sep 17 00:00:00 2001 From: louyuting <1849491904@qq.com> Date: Thu, 4 Jun 2020 20:33:43 +0800 Subject: [PATCH 3/3] rename UseCacheTime --- api/init.go | 2 +- core/config/config.go | 4 ++-- core/config/entity.go | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api/init.go b/api/init.go index a37d9384..a2f28a15 100644 --- a/api/init.go +++ b/api/init.go @@ -44,7 +44,7 @@ func initCoreComponents() (err error) { } system.InitCollector(config.SystemStatCollectIntervalMs()) - if config.UseTimeTicker() { + if config.UseCacheTime() { util.StartTimeTicker() } return err diff --git a/core/config/config.go b/core/config/config.go index b4feab03..f3cbfba4 100644 --- a/core/config/config.go +++ b/core/config/config.go @@ -181,6 +181,6 @@ func SystemStatCollectIntervalMs() uint32 { return globalCfg.Sentinel.Stat.System.CollectIntervalMs } -func UseTimeTicker() bool { - return globalCfg.Sentinel.UseTimeTicker +func UseCacheTime() bool { + return globalCfg.Sentinel.UseCacheTime } diff --git a/core/config/entity.go b/core/config/entity.go index a9563ccc..058a6873 100644 --- a/core/config/entity.go +++ b/core/config/entity.go @@ -23,8 +23,8 @@ type SentinelConfig struct { Log LogConfig // Stat represents configuration items related to statistics. Stat StatConfig - // UseTimeTicker indicates whether use time ticker to cache now time(ms) - UseTimeTicker bool + // UseCacheTime indicates whether to cache time(ms) + UseCacheTime bool `yaml:"useCacheTime"` } // LogConfig represent the configuration of logging in Sentinel. @@ -81,7 +81,7 @@ func NewDefaultConfig() *Entity { CollectIntervalMs: DefaultSystemStatCollectIntervalMs, }, }, - UseTimeTicker: true, + UseCacheTime: true, }, } }