From e745ccec60a654224f3f44e1154ac953113fcff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Flc=E3=82=9B?= Date: Sat, 17 Feb 2024 17:23:22 +0800 Subject: [PATCH 1/2] feat(foundation): Added two system-level events. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Flc゛ --- event/dispatcher.go | 4 ++ foundation/README.md | 103 ++++++++++++++++++++++++++++++++++++++++++ foundation/boot.go | 34 ++++++++++++++ foundation/options.go | 25 ++++++++++ foundation/shut.go | 34 ++++++++++++++ 5 files changed, 200 insertions(+) create mode 100644 foundation/README.md create mode 100644 foundation/boot.go create mode 100644 foundation/options.go create mode 100644 foundation/shut.go diff --git a/event/dispatcher.go b/event/dispatcher.go index a3bcf503..0d5e2e18 100644 --- a/event/dispatcher.go +++ b/event/dispatcher.go @@ -10,6 +10,10 @@ import ( type Event string +func (e Event) String() string { + return string(e) +} + type Listener interface { Listen() []Event Handle(event Event, data interface{}) diff --git a/foundation/README.md b/foundation/README.md new file mode 100644 index 00000000..400747b7 --- /dev/null +++ b/foundation/README.md @@ -0,0 +1,103 @@ +# Foundation + +## Example + +```go +package main + +import ( + "fmt" + "log" + + "github.com/go-kratos/kratos/v2" + "github.com/robfig/cron/v3" + + "github.com/go-kratos-ecosystem/components/v2/coordinator" + v2 "github.com/go-kratos-ecosystem/components/v2/crontab/v2" + "github.com/go-kratos-ecosystem/components/v2/event" + "github.com/go-kratos-ecosystem/components/v2/foundation" +) + +type listener struct{} + +var _ event.Listener = (*listener)(nil) + +func (l *listener) Listen() []event.Event { + return []event.Event{ + foundation.BootName, + foundation.ShutName, + } +} + +func (l *listener) Handle(event event.Event, data interface{}) { + if event.String() == foundation.BootName { + if e, ok := data.(*foundation.BootEvent); ok { + fmt.Println("bootstrap done, and the app start time: ", e.Time) + } + } + + if event.String() == foundation.ShutName { + if e, ok := data.(*foundation.ShutEvent); ok { + fmt.Println("shutdown done, and the app end time: ", e.Time) + } + } +} + +func main() { + m := coordinator.NewManager() + d := event.NewDispatcher() + + go func() { + if <-m.Until(foundation.BootName).Done(); true { + fmt.Println("bootstrap done") + } + }() + + go func() { + if <-m.Until(foundation.ShutName).Done(); true { + fmt.Println("shutdown done") + } + }() + + d.AddListener(&listener{}) + + app := kratos.New( + kratos.Server(newCrontabServer()), + kratos.BeforeStart(foundation.NewBoot( + foundation.WithManager(m), + foundation.WithDispatcher(d), + )), + kratos.AfterStop(foundation.NewShut( + foundation.WithManager(m), + foundation.WithDispatcher(d), + )), + ) + + if err := app.Run(); err != nil { + log.Fatal(err) + } +} + +func newCrontabServer() *v2.Server { + srv := v2.NewServer( + cron.New(cron.WithSeconds()), + ) + + srv.AddFunc("* * * * * *", func() { //nolint:errcheck + println("hello") + }) + + return srv +} +``` + +output: + +```bash +bootstrap done +bootstrap done, and the app start time: 2024-02-17 17:21:52.309688 +0800 CST m=+0.003057710 +hello +hello +hello +^Cshutdown done, and the app end time: 2024-02-17 17:21:55.951264 +0800 CST m=+3.644671418 +``` \ No newline at end of file diff --git a/foundation/boot.go b/foundation/boot.go new file mode 100644 index 00000000..175be3ac --- /dev/null +++ b/foundation/boot.go @@ -0,0 +1,34 @@ +package foundation + +import ( + "context" + "time" +) + +const BootName = "foundation.boot" + +type BootEvent struct { + Time time.Time +} + +func NewBoot(opts ...Option) func(context.Context) error { + opt := options{} + + for _, o := range opts { + o(&opt) + } + + return func(context.Context) error { + if opt.m != nil { + opt.m.Close(BootName) + } + + if opt.d != nil { + opt.d.Dispatch(BootName, &BootEvent{ + Time: time.Now(), + }) + } + + return nil + } +} diff --git a/foundation/options.go b/foundation/options.go new file mode 100644 index 00000000..dbb99a6e --- /dev/null +++ b/foundation/options.go @@ -0,0 +1,25 @@ +package foundation + +import ( + "github.com/go-kratos-ecosystem/components/v2/coordinator" + "github.com/go-kratos-ecosystem/components/v2/event" +) + +type options struct { + m *coordinator.Manager + d *event.Dispatcher +} + +type Option func(*options) + +func WithManager(m *coordinator.Manager) Option { + return func(o *options) { + o.m = m + } +} + +func WithDispatcher(d *event.Dispatcher) Option { + return func(o *options) { + o.d = d + } +} diff --git a/foundation/shut.go b/foundation/shut.go new file mode 100644 index 00000000..e9246aef --- /dev/null +++ b/foundation/shut.go @@ -0,0 +1,34 @@ +package foundation + +import ( + "context" + "time" +) + +const ShutName = "foundation.shut" + +type ShutEvent struct { + Time time.Time +} + +func NewShut(opts ...Option) func(context.Context) error { + opt := options{} + + for _, o := range opts { + o(&opt) + } + + return func(context.Context) error { + if opt.m != nil { + opt.m.Close(ShutName) + } + + if opt.d != nil { + opt.d.Dispatch(ShutName, &ShutEvent{ + Time: time.Now(), + }) + } + + return nil + } +} From 1105485a77486e49f0299040b5a00ca4bd2bc23b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Flc=E3=82=9B?= Date: Sat, 17 Feb 2024 17:48:36 +0800 Subject: [PATCH 2/2] feat(foundation): Added two system-level events. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Flc゛ --- coordinator/manager.go | 7 +++ foundation/README.md | 29 ++++++----- foundation/boot.go | 34 ------------- foundation/bootstrap.go | 34 +++++++++++++ foundation/foundation_test.go | 90 +++++++++++++++++++++++++++++++++++ foundation/shut.go | 34 ------------- foundation/shutdown.go | 34 +++++++++++++ 7 files changed, 181 insertions(+), 81 deletions(-) delete mode 100644 foundation/boot.go create mode 100644 foundation/bootstrap.go create mode 100644 foundation/foundation_test.go delete mode 100644 foundation/shut.go create mode 100644 foundation/shutdown.go diff --git a/coordinator/manager.go b/coordinator/manager.go index 823a05bf..b6d94f4e 100644 --- a/coordinator/manager.go +++ b/coordinator/manager.go @@ -33,7 +33,14 @@ func (m *Manager) Close(identifier string) { if c, ok := m.coordinators[identifier]; ok { c.Close() + return } + + // If the coordinator does not exist, create a new one and close it immediately. + // Fix when first Close and then Until, the coordinator will not be closed. + c := NewCoordinator() + m.coordinators[identifier] = c + c.Close() } func (m *Manager) Clear() { diff --git a/foundation/README.md b/foundation/README.md index 400747b7..3475daad 100644 --- a/foundation/README.md +++ b/foundation/README.md @@ -24,20 +24,20 @@ var _ event.Listener = (*listener)(nil) func (l *listener) Listen() []event.Event { return []event.Event{ - foundation.BootName, - foundation.ShutName, + foundation.BootstrapName, + foundation.ShutdownName, } } func (l *listener) Handle(event event.Event, data interface{}) { - if event.String() == foundation.BootName { - if e, ok := data.(*foundation.BootEvent); ok { + if event.String() == foundation.BootstrapName { + if e, ok := data.(*foundation.BootstrapEvent); ok { fmt.Println("bootstrap done, and the app start time: ", e.Time) } } - if event.String() == foundation.ShutName { - if e, ok := data.(*foundation.ShutEvent); ok { + if event.String() == foundation.ShutdownName { + if e, ok := data.(*foundation.ShutdownEvent); ok { fmt.Println("shutdown done, and the app end time: ", e.Time) } } @@ -48,13 +48,13 @@ func main() { d := event.NewDispatcher() go func() { - if <-m.Until(foundation.BootName).Done(); true { + if <-m.Until(foundation.BootstrapName).Done(); true { fmt.Println("bootstrap done") } }() go func() { - if <-m.Until(foundation.ShutName).Done(); true { + if <-m.Until(foundation.ShutdownName).Done(); true { fmt.Println("shutdown done") } }() @@ -63,11 +63,11 @@ func main() { app := kratos.New( kratos.Server(newCrontabServer()), - kratos.BeforeStart(foundation.NewBoot( + kratos.BeforeStart(foundation.NewBootstrap( foundation.WithManager(m), foundation.WithDispatcher(d), )), - kratos.AfterStop(foundation.NewShut( + kratos.AfterStop(foundation.NewShutdown( foundation.WithManager(m), foundation.WithDispatcher(d), )), @@ -76,6 +76,8 @@ func main() { if err := app.Run(); err != nil { log.Fatal(err) } + + time.Sleep(time.Second) } func newCrontabServer() *v2.Server { @@ -89,15 +91,16 @@ func newCrontabServer() *v2.Server { return srv } + ``` output: ```bash +bootstrap done, and the app start time: 2024-02-17 17:42:45.626551 +0800 CST m=+0.002061459 bootstrap done -bootstrap done, and the app start time: 2024-02-17 17:21:52.309688 +0800 CST m=+0.003057710 -hello hello hello -^Cshutdown done, and the app end time: 2024-02-17 17:21:55.951264 +0800 CST m=+3.644671418 +^Cshutdown done, and the app end time: 2024-02-17 17:42:47.121271 +0800 CST m=+1.496789626 +shutdown done ``` \ No newline at end of file diff --git a/foundation/boot.go b/foundation/boot.go deleted file mode 100644 index 175be3ac..00000000 --- a/foundation/boot.go +++ /dev/null @@ -1,34 +0,0 @@ -package foundation - -import ( - "context" - "time" -) - -const BootName = "foundation.boot" - -type BootEvent struct { - Time time.Time -} - -func NewBoot(opts ...Option) func(context.Context) error { - opt := options{} - - for _, o := range opts { - o(&opt) - } - - return func(context.Context) error { - if opt.m != nil { - opt.m.Close(BootName) - } - - if opt.d != nil { - opt.d.Dispatch(BootName, &BootEvent{ - Time: time.Now(), - }) - } - - return nil - } -} diff --git a/foundation/bootstrap.go b/foundation/bootstrap.go new file mode 100644 index 00000000..a69169a5 --- /dev/null +++ b/foundation/bootstrap.go @@ -0,0 +1,34 @@ +package foundation + +import ( + "context" + "time" +) + +const BootstrapName = "foundation.bootstrap" + +type BootstrapEvent struct { + Time time.Time +} + +func NewBootstrap(opts ...Option) func(context.Context) error { + o := &options{} + + for _, opt := range opts { + opt(o) + } + + return func(context.Context) error { + if o.m != nil { + o.m.Close(BootstrapName) + } + + if o.d != nil { + o.d.Dispatch(BootstrapName, &BootstrapEvent{ + Time: time.Now(), + }) + } + + return nil + } +} diff --git a/foundation/foundation_test.go b/foundation/foundation_test.go new file mode 100644 index 00000000..a53e18a4 --- /dev/null +++ b/foundation/foundation_test.go @@ -0,0 +1,90 @@ +package foundation + +import ( + "context" + "fmt" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/go-kratos-ecosystem/components/v2/coordinator" + "github.com/go-kratos-ecosystem/components/v2/event" +) + +var ( + wg sync.WaitGroup + c = make(chan string, 8) +) + +type listener struct{} + +var _ event.Listener = (*listener)(nil) + +func (l *listener) Listen() []event.Event { + return []event.Event{ + BootstrapName, + ShutdownName, + } +} + +func (l *listener) Handle(event event.Event, data interface{}) { + if event.String() == BootstrapName { + if _, ok := data.(*BootstrapEvent); ok { + c <- "bootstrap done from listener" + wg.Done() + } + } + + if event.String() == ShutdownName { + if _, ok := data.(*ShutdownEvent); ok { + c <- "shutdown done from listener" + wg.Done() + } + } +} + +func TestBootAndShut(t *testing.T) { + var ( + m = coordinator.NewManager() + d = event.NewDispatcher() + boot = NewBootstrap(WithManager(m), WithDispatcher(d)) + shut = NewShutdown(WithManager(m), WithDispatcher(d)) + ) + wg.Add(4) + + go func() { + defer wg.Done() + if <-m.Until(BootstrapName).Done(); true { + fmt.Println("bootstrap done") + c <- "bootstrap done" + } + }() + + go func() { + defer wg.Done() + if <-m.Until(ShutdownName).Done(); true { + c <- "shutdown done" + } + }() + + d.AddListener(&listener{}) + + assert.NoError(t, boot(context.Background())) + assert.NoError(t, shut(context.Background())) + + // wg.Wait() + + // assert.Equal(t, 4, len(c)) + time.Sleep(time.Second) + + for { + select { + case v := <-c: + fmt.Println(v) + default: + return + } + } +} diff --git a/foundation/shut.go b/foundation/shut.go deleted file mode 100644 index e9246aef..00000000 --- a/foundation/shut.go +++ /dev/null @@ -1,34 +0,0 @@ -package foundation - -import ( - "context" - "time" -) - -const ShutName = "foundation.shut" - -type ShutEvent struct { - Time time.Time -} - -func NewShut(opts ...Option) func(context.Context) error { - opt := options{} - - for _, o := range opts { - o(&opt) - } - - return func(context.Context) error { - if opt.m != nil { - opt.m.Close(ShutName) - } - - if opt.d != nil { - opt.d.Dispatch(ShutName, &ShutEvent{ - Time: time.Now(), - }) - } - - return nil - } -} diff --git a/foundation/shutdown.go b/foundation/shutdown.go new file mode 100644 index 00000000..fc0e35c6 --- /dev/null +++ b/foundation/shutdown.go @@ -0,0 +1,34 @@ +package foundation + +import ( + "context" + "time" +) + +const ShutdownName = "foundation.shutdown" + +type ShutdownEvent struct { + Time time.Time +} + +func NewShutdown(opts ...Option) func(context.Context) error { + o := &options{} + + for _, opt := range opts { + opt(o) + } + + return func(context.Context) error { + if o.m != nil { + o.m.Close(ShutdownName) + } + + if o.d != nil { + o.d.Dispatch(ShutdownName, &ShutdownEvent{ + Time: time.Now(), + }) + } + + return nil + } +}