Skip to content

Commit

Permalink
feat(foundation): Added two system-level events. (#102)
Browse files Browse the repository at this point in the history
* feat(foundation): Added two system-level events.

Signed-off-by: Flc゛ <[email protected]>

* feat(foundation): Added two system-level events.

Signed-off-by: Flc゛ <[email protected]>

---------

Signed-off-by: Flc゛ <[email protected]>
  • Loading branch information
flc1125 authored Feb 17, 2024
1 parent 5360c7e commit feb640a
Show file tree
Hide file tree
Showing 7 changed files with 300 additions and 0 deletions.
7 changes: 7 additions & 0 deletions coordinator/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
4 changes: 4 additions & 0 deletions event/dispatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{})
Expand Down
106 changes: 106 additions & 0 deletions foundation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# 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.BootstrapName,
foundation.ShutdownName,
}
}

func (l *listener) Handle(event event.Event, data interface{}) {
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.ShutdownName {
if e, ok := data.(*foundation.ShutdownEvent); 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.BootstrapName).Done(); true {
fmt.Println("bootstrap done")
}
}()

go func() {
if <-m.Until(foundation.ShutdownName).Done(); true {
fmt.Println("shutdown done")
}
}()

d.AddListener(&listener{})

app := kratos.New(
kratos.Server(newCrontabServer()),
kratos.BeforeStart(foundation.NewBootstrap(
foundation.WithManager(m),
foundation.WithDispatcher(d),
)),
kratos.AfterStop(foundation.NewShutdown(
foundation.WithManager(m),
foundation.WithDispatcher(d),
)),
)

if err := app.Run(); err != nil {
log.Fatal(err)
}

time.Sleep(time.Second)
}

func newCrontabServer() *v2.Server {
srv := v2.NewServer(
cron.New(cron.WithSeconds()),
)

srv.AddFunc("* * * * * *", func() { //nolint:errcheck
println("hello")
})

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
hello
hello
^Cshutdown done, and the app end time: 2024-02-17 17:42:47.121271 +0800 CST m=+1.496789626
shutdown done
```
34 changes: 34 additions & 0 deletions foundation/bootstrap.go
Original file line number Diff line number Diff line change
@@ -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
}
}
90 changes: 90 additions & 0 deletions foundation/foundation_test.go
Original file line number Diff line number Diff line change
@@ -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
}
}
}
25 changes: 25 additions & 0 deletions foundation/options.go
Original file line number Diff line number Diff line change
@@ -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
}
}
34 changes: 34 additions & 0 deletions foundation/shutdown.go
Original file line number Diff line number Diff line change
@@ -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
}
}

0 comments on commit feb640a

Please sign in to comment.