From f7db23d76b139c7f4c517d4f426c461e15f0889b Mon Sep 17 00:00:00 2001 From: kercylan98 Date: Thu, 1 Jun 2023 14:27:08 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E6=8A=A5=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- report/README.md | 17 +++++++++++++++ report/reporter.go | 32 +++++++++++++++++++++++++++++ report/reporter_options.go | 25 ++++++++++++++++++++++ report/reporter_strategy.go | 41 +++++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+) create mode 100644 report/README.md create mode 100644 report/reporter.go create mode 100644 report/reporter_options.go create mode 100644 report/reporter_strategy.go diff --git a/report/README.md b/report/README.md new file mode 100644 index 0000000..f5bcf73 --- /dev/null +++ b/report/README.md @@ -0,0 +1,17 @@ +# Report +用于数据上报,其中数据埋点是线程安全的 + +# 全局埋点 GlobalBuried +全局埋点适用于活跃用户数、用户总量等全局的数据统计 + +# 数据埋点 DataBuried +数据埋点适合进行用户数据、交易数据等存在多id情况的数据统计 + +# 跨进程上报 +通常数据埋点会占用一些系统资源而妨碍主进程的运行,这时候可以通过将上报工作独立出来减轻主进程负担 + +默认情况下的埋点数据是存储在执行进程的内存中的,可以通过可选项自定义存储位置,例如`Redis` + +> 实现思路,以`Redis`为例: +> - 在主进程创建埋点,并将数据读写更改为`Redis` +> - 上报进程中创建上报器,按照特定策略从`Redis`读取数据进行上报 diff --git a/report/reporter.go b/report/reporter.go new file mode 100644 index 0000000..7f87aa4 --- /dev/null +++ b/report/reporter.go @@ -0,0 +1,32 @@ +package report + +import "github.com/kercylan98/minotaur/utils/timer" + +func NewReporter(reportHandle func() error, options ...ReporterOption) *Reporter { + reporter := &Reporter{ + reportHandle: reportHandle, + } + for _, option := range options { + option(reporter) + } + if reporter.ticker == nil { + reporter.ticker = timer.GetTicker(50) + } + for _, strategy := range reporter.strategies { + strategy(reporter) + } + return reporter +} + +// Reporter 数据上报器 +type Reporter struct { + ticker *timer.Ticker + strategies []ReporterStrategy + reportHandle func() error + errorHandle func(reporter *Reporter, err error) +} + +// Report 上报 +func (slf *Reporter) Report() error { + return slf.reportHandle() +} diff --git a/report/reporter_options.go b/report/reporter_options.go new file mode 100644 index 0000000..2738794 --- /dev/null +++ b/report/reporter_options.go @@ -0,0 +1,25 @@ +package report + +import "github.com/kercylan98/minotaur/utils/timer" + +type ReporterOption func(reporter *Reporter) + +// WithReporterTicker 通过特定的定时器创建上报器 +func WithReporterTicker(ticker *timer.Ticker) ReporterOption { + return func(reporter *Reporter) { + reporter.ticker = ticker + } +} + +// WithReporterStrategies 通过特定上报策略进行创建 +func WithReporterStrategies(strategies ...ReporterStrategy) ReporterOption { + return func(reporter *Reporter) { + reporter.strategies = append(reporter.strategies, strategies...) + } +} + +func WithReporterErrorHandle(errorHandle func(reporter *Reporter, err error)) ReporterOption { + return func(reporter *Reporter) { + reporter.errorHandle = errorHandle + } +} diff --git a/report/reporter_strategy.go b/report/reporter_strategy.go new file mode 100644 index 0000000..d76143b --- /dev/null +++ b/report/reporter_strategy.go @@ -0,0 +1,41 @@ +package report + +import ( + "fmt" + "github.com/kercylan98/minotaur/utils/timer" + "time" +) + +// ReporterStrategy 上报器策略 +type ReporterStrategy func(reporter *Reporter) + +// ReportStrategyLoop 循环上报 +// - 将在创建后上报一次,并且在每隔一段时间后继续上报 +func ReportStrategyLoop(t time.Duration) ReporterStrategy { + return func(reporter *Reporter) { + reporter.ticker.Loop(fmt.Sprintf("ReportStrategyLoop_%d", t.Milliseconds()), timer.Instantly, t, timer.Forever, func() { + if err := reporter.Report(); err != nil && reporter.errorHandle != nil { + reporter.errorHandle(reporter, err) + } + }) + } +} + +// ReportStrategyFixedTime 将在每天的固定时间上报 +func ReportStrategyFixedTime(hour, min, sec int) ReporterStrategy { + return func(reporter *Reporter) { + now := time.Now() + current := now.Unix() + next := time.Date(now.Year(), now.Month(), now.Day(), hour, min, sec, 0, now.Location()) + target := next.Unix() + if current >= target { + next = next.AddDate(0, 0, 1) + target = next.Unix() + } + reporter.ticker.Loop(fmt.Sprintf("ReportStrategyFixedTime_%d_%d_%d", hour, min, sec), time.Duration(target-current)*time.Second, 24*time.Hour, -1, func() { + if err := reporter.Report(); err != nil && reporter.errorHandle != nil { + reporter.errorHandle(reporter, err) + } + }) + } +}