在这个例子中,我们将有两种类型的Golang的事件驱动编程例子。它们彼此非常相似,但两者的主要区别在于通道的使用。第一个例子不使用通道。你永远不会接触到internal/pkg/event 文件夹,所以你可以把它看作是一个外部库包。user 文件夹是你的代码。
这是一个有主见但非常直接的 "甚至驱动 "的例子。事件以某种方式被派发,然而处理的方式由你决定。派遣事件可以用阻塞或非阻塞的方式处理(如goroutine)。它有三个简单的步骤,如下所列:
-
创建一个事件调度器的实例。(在应用程序启动时完成)
-
注册事件和监听器。(在应用启动阶段完成)
-
派遣事件
属性
-
一个调度器可以注册许多监听器
-
一个调度器可以为一个特定的听众注册许多事件
-
一个调度器可以调度许多事件
-
一个监听器可以监听许多事件
-
一个事件可以链接到一个事件类型
-
调度器防止注册重复的事件
-
调度器防止调度非注册的事件
例子1 - 没有通道
结构
├── cmd
│ └── client
│ └── main.go
└── internal
├── pkg
│ └── event
│ ├── dispatcher.go
│ ├── event.go
│ └── listener.go
└── user
├── created.go
├── deleted.go
├── listener.go
└── updated.go
文件
cmd/client/main.go
package main
import (
"context"
"log"
"time"
"github.com/inanzzz/client/internal/pkg/event"
"github.com/inanzzz/client/internal/user"
)
func main() {
// ----------------------------------------------------------------------------------------
// Register events with listeners at application boot
dispatcher := event.NewDispatcher()
if err := dispatcher.Register(user.Listener{}, user.Created, user.Updated); err != nil {
log.Fatalln(err)
}
// ----------------------------------------------------------------------------------------
// Dispatch registered events. Valid.
go func() {
err := dispatcher.Dispatch(context.Background(), user.Created, user.CreatedEvent{
Time: time.Now().UTC(),
ID: "111",
})
if err != nil {
log.Println(err)
}
}()
go func() {
err := dispatcher.Dispatch(context.Background(), user.Updated, user.UpdatedEvent{
Time: time.Now().UTC(),
ID: "222",
Key: "name",
OldValue: "inanzzz",
NewValue: "zzznani",
})
if err != nil {
log.Println(err)
}
}()
// ----------------------------------------------------------------------------------------
// Dispatch a valid event type to unregistered event name. Error.
go func() {
err := dispatcher.Dispatch(context.Background(), user.Deleted, user.DeletedEvent{
Time: time.Now().UTC(),
ID: "333",
Who: "admin",
})
if err != nil {
log.Println(err)
}
}()
// ----------------------------------------------------------------------------------------
// Dispatch a wrong event type to registered event name. Error.
go dispatcher.Dispatch(context.Background(), user.Created, nil)
go dispatcher.Dispatch(context.Background(), user.Updated, "hi")
go dispatcher.Dispatch(context.Background(), user.Created, 123)
go dispatcher.Dispatch(context.Background(), user.Updated, struct{}{})
go dispatcher.Dispatch(context.Background(), user.Created, make(chan int))
select {}
}
internal/pkg/event/dispatcher.go
package event
import (
"context"
"fmt"
)
type Dispatcher struct {
events map[Name]Listener
}
func NewDispatcher() *Dispatcher {
return &Dispatcher{
events: make(map[Name]Listener),
}
}
func (d *Dispatcher) Register(listener Listener, names ...Name) error {
for _, name := range names {
if _, ok := d.events[name]; ok {
return fmt.Errorf("the '%s' event is already registered", name)
}
d.events[name] = listener
}
return nil
}
func (d *Dispatcher) Dispatch(ctx context.Context, name Name, event interface{}) error {
if _, ok := d.events[name]; !ok {
return fmt.Errorf("the '%s' event is not registered", name)
}
d.events[name].Listen(ctx, event)
return nil
}
internal/pkg/event/event.go
package event
import (
"context"
)
// All custom events names must be of this type.
type Name string
// All custom event types must satisfy this interface.
type Event interface {
Handle(ctx context.Context)
}
internal/pkg/event/listener.go
package event
import (
"context"
)
// All custom event listeners must satisfy this interface.
type Listener interface {
Listen(ctx context.Context, event interface{})
}
internal/user/created.go
package user
import (
"context"
"log"
"time"
"github.com/inanzzz/client/internal/pkg/event"
)
const Created event.Name = "user.created"
type CreatedEvent struct {
Time time.Time
ID string
}
func (e CreatedEvent) Handle(ctx context.Context) {
log.Printf("creating: %+v\n", e)
}
internal/user/deleted.go
package user
import (
"context"
"log"
"time"
"github.com/inanzzz/client/internal/pkg/event"
)
const Deleted event.Name = "user.deleted"
type DeletedEvent struct {
Time time.Time
ID string
Who string
}
func (e DeletedEvent) Handle(ctx context.Context) {
log.Printf("deleting: %+v\n", e)
}
internal/user/updated.go
package user
import (
"context"
"log"
"time"
"github.com/inanzzz/client/internal/pkg/event"
)
const Updated event.Name = "user.updated"
type UpdatedEvent struct {
Time time.Time
ID string
Key string
OldValue string
NewValue string
}
func (e UpdatedEvent) Handle(ctx context.Context) {
log.Printf("updating: %+v\n", e)
}
internal/user/listener.go
package user
import (
"context"
"log"
)
type Listener struct{}
func (u Listener) Listen(ctx context.Context, event interface{}) {
switch event := event.(type) {
case CreatedEvent:
event.Handle(ctx)
case UpdatedEvent:
event.Handle(ctx)
case DeletedEvent:
event.Handle(ctx)
default:
log.Printf("registered an invalid user event: %T\n", event)
}
}
测试
2020/05/27 21:08:53 creating: {Time:2020-05-27 20:08:53.272564 +0000 UTC ID:111}
2020/05/27 21:08:53 registered an invalid user event:
2020/05/27 21:08:53 the 'user.deleted' event is not registered
2020/05/27 21:08:53 registered an invalid user event: string
2020/05/27 21:08:53 updating: {Time:2020-05-27 20:08:53.273203 +0000 UTC ID:222 Key:name OldValue:inanzzz NewValue:zzznani}
2020/05/27 21:08:53 registered an invalid user event: int
2020/05/27 21:08:53 registered an invalid user event: struct {}
2020/05/27 21:08:53 registered an invalid user event: chan int
例子2--有渠道(优先)的例子
如果你想实现通道优先选项,你可以看看我之前写的博文。**注意:**查看下面的第三个例子(后期添加)。
结构
├── cmd
│ └── client
│ └── main.go
└── internal
├── pkg
│ └── event
│ ├── dispatcher.go
│ ├── event.go
│ ├── job.go
│ └── listener.go
└── user
├── created.go
├── deleted.go
├── listener.go
└── updated.go
文件
cmd/client/main.go
package main
import (
"log"
"time"
"github.com/inanzzz/client/internal/pkg/event"
"github.com/inanzzz/client/internal/user"
)
func main() {
// ----------------------------------------------------------------------------------------
// Register events with listeners at application boot
dispatcher := event.NewDispatcher()
if err := dispatcher.Register(user.Listener{}, user.Created, user.Updated); err != nil {
log.Fatalln(err)
}
// ----------------------------------------------------------------------------------------
// Dispatch registered events. Valid.
go func() {
err := dispatcher.Dispatch(user.Created, user.CreatedEvent{
Time: time.Now().UTC(),
ID: "111",
})
if err != nil {
log.Println(err)
}
}()
go func() {
err := dispatcher.Dispatch(user.Updated, user.UpdatedEvent{
Time: time.Now().UTC(),
ID: "222",
Key: "name",
OldValue: "inanzzz",
NewValue: "zzznani",
})
if err != nil {
log.Println(err)
}
}()
// ----------------------------------------------------------------------------------------
// Dispatch a valid event type to unregistered event name. Error.
go func() {
err := dispatcher.Dispatch(user.Deleted, user.DeletedEvent{
Time: time.Now().UTC(),
ID: "333",
Who: "admin",
})
if err != nil {
log.Println(err)
}
}()
// ----------------------------------------------------------------------------------------
// Dispatch a wrong event type to registered event name. Error.
go dispatcher.Dispatch(user.Created, nil)
go dispatcher.Dispatch(user.Updated, "hi")
go dispatcher.Dispatch(user.Created, 123)
go dispatcher.Dispatch(user.Updated, struct{}{})
go dispatcher.Dispatch(user.Created, make(chan int))
select {}
}
internal/pkg/event/dispatcher.go
package event
import (
"fmt"
)
type Dispatcher struct {
jobs chan job
events map[Name]Listener
}
func NewDispatcher() *Dispatcher {
d := &Dispatcher{
jobs: make(chan job),
events: make(map[Name]Listener),
}
go d.consume()
return d
}
func (d *Dispatcher) Register(listener Listener, names ...Name) error {
for _, name := range names {
if _, ok := d.events[name]; ok {
return fmt.Errorf("the '%s' event is already registered", name)
}
d.events[name] = listener
}
return nil
}
func (d *Dispatcher) Dispatch(name Name, event interface{}) error {
if _, ok := d.events[name]; !ok {
return fmt.Errorf("the '%s' event is not registered", name)
}
d.jobs <- job{eventName: name, eventType: event}
return nil
}
func (d *Dispatcher) consume() {
for job := range d.jobs {
d.events[job.eventName].Listen(job.eventType)
}
}
internal/pkg/event/event.go
package event
// All custom events names must be of this type.
type Name string
// All custom event types must satisfy this interface.
type Event interface {
Handle()
}
内部/pkg/event/job.go
package event
// job represents events. When a new event is dispatched, it
// gets tuned into a job and put into `Dispatcher.jobs` channel.
type job struct {
eventName Name
eventType interface{}
}
internal/pkg/event/listener.go
package event
// All custom event listeners must satisfy this interface.
type Listener interface {
Listen(event interface{})
}
internal/user/created.go
package user
import (
"log"
"time"
"github.com/inanzzz/client/internal/pkg/event"
)
const Created event.Name = "user.created"
type CreatedEvent struct {
Time time.Time
ID string
}
func (e CreatedEvent) Handle() {
log.Printf("creating: %+v\n", e)
}
internal/user/deleted.go
package user
import (
"log"
"time"
"github.com/inanzzz/client/internal/pkg/event"
)
const Deleted event.Name = "user.deleted"
type DeletedEvent struct {
Time time.Time
ID string
Who string
}
func (e DeletedEvent) Handle() {
log.Printf("deleting: %+v\n", e)
}
internal/user/updated.go
package user
import (
"log"
"time"
"github.com/inanzzz/client/internal/pkg/event"
)
const Updated event.Name = "user.updated"
type UpdatedEvent struct {
Time time.Time
ID string
Key string
OldValue string
NewValue string
}
func (e UpdatedEvent) Handle() {
log.Printf("updating: %+v\n", e)
}
internal/user/listener.go
package user
import (
"log"
)
type Listener struct{}
func (u Listener) Listen(event interface{}) {
switch event := event.(type) {
case CreatedEvent:
event.Handle()
case UpdatedEvent:
event.Handle()
case DeletedEvent:
event.Handle()
default:
log.Printf("registered an invalid user event: %T\n", event)
}
}
测试
2020/05/27 21:12:01 creating: {Time:2020-05-27 20:12:01.556947 +0000 UTC ID:111}
2020/05/27 21:12:01 the 'user.deleted' event is not registered
2020/05/27 21:12:01 updating: {Time:2020-05-27 20:12:01.557059 +0000 UTC ID:222 Key:name OldValue:inanzzz NewValue:zzznani}
2020/05/27 21:12:01 registered an invalid user event:
2020/05/27 21:12:01 registered an invalid user event: string
2020/05/27 21:12:01 registered an invalid user event: int
2020/05/27 21:12:01 registered an invalid user event: struct {}
2020/05/27 21:12:01 registered an invalid user event: chan int
例子3--没有通道
只是想让你知道,这是本文章的一个最新补充。与上面两个例子相比,这是一个简化版本。你不需要实现你的监听器。你所要做的就是,创建你的事件并像往常一样注册。这就是全部。如果你在这个例子中加入了通道功能,你可以认为它是首选版本无论怎样,这都是我的首选。
结构
├── cmd
│ └── client
│ └── main.go
├── go.mod
└── internal
├── pkg
│ └── event
│ ├── dispatcher.go
│ └── event.go
└── user
├── create.go
├── delete.go
└── update.go
文件
cmd/client/main.go
package main
import (
"context"
"log"
"time"
"github.com/inanzzz/client/internal/pkg/event"
"github.com/inanzzz/client/internal/user"
)
func main() {
// ----------------------------------------------------------------------------------------
// Register events with dispatcher at application boot
dispatcher := event.NewDispatcher()
dispatcher.Register(user.AfterCreate{}, user.BeforeUpdate{}, user.AfterUpdate{})
// ----------------------------------------------------------------------------------------
// Dispatch registered events. Valid.
go func() {
err := dispatcher.Dispatch(context.Background(), user.AfterCreate{
Time: time.Now().UTC(),
ID: "111",
})
if err != nil {
log.Println(err)
}
}()
go func() {
err := dispatcher.Dispatch(context.Background(), user.BeforeUpdate{
Time: time.Now().UTC(),
ID: "222",
Key: "name",
OldValue: "inanzzz",
})
if err != nil {
log.Println(err)
}
}()
go func() {
err := dispatcher.Dispatch(context.Background(), user.AfterUpdate{
Time: time.Now().UTC(),
ID: "222",
Key: "name",
NewValue: "zzznani",
})
if err != nil {
log.Println(err)
}
}()
// ----------------------------------------------------------------------------------------
// Dispatch unregistered event. Error.
go func() {
err := dispatcher.Dispatch(context.Background(), user.AfterDelete{
Time: time.Now().UTC(),
ID: "333",
Who: "admin",
})
if err != nil {
log.Println(err)
}
}()
select {}
}
internal/pkg/event/dispatcher.go
package event
import (
"context"
"fmt"
"reflect"
)
type Dispatcher struct {
events []string
}
func NewDispatcher() *Dispatcher {
return &Dispatcher{}
}
func (d *Dispatcher) Register(events ...Event) {
for _, v := range events {
d.events = append(d.events, reflect.TypeOf(v).String())
}
}
func (d *Dispatcher) Dispatch(ctx context.Context, event Event) error {
name := reflect.TypeOf(event).String()
for _, v := range d.events {
if v == name {
return event.Handle(ctx)
}
}
return fmt.Errorf("%s is not a registered event", name)
}
internal/pkg/event/event.go
package event
import (
"context"
)
// All custom events must satisfy this interface.
type Event interface {
Handle(ctx context.Context) error
}
internal/user/create.go
package user
import (
"context"
"log"
"time"
)
type AfterCreate struct {
Time time.Time
ID string
}
func (event AfterCreate) Handle(ctx context.Context) error {
log.Printf("after create: %+v\n", event)
return nil
}
internal/user/delete.go
package user
import (
"context"
"log"
"time"
)
type AfterDelete struct {
Time time.Time
ID string
Who string
}
func (event AfterDelete) Handle(ctx context.Context) error {
log.Printf("after delete: %+v\n", event)
return nil
}
internal/user/update.go
package user
import (
"context"
"log"
"time"
)
type BeforeUpdate struct {
Time time.Time
ID string
Key string
OldValue string
}
func (event BeforeUpdate) Handle(ctx context.Context) error {
log.Printf("before update: %+v\n", event)
return nil
}
type AfterUpdate struct {
Time time.Time
ID string
Key string
NewValue string
}
func (event AfterUpdate) Handle(ctx context.Context) error {
log.Printf("after update: %+v\n", event)
return nil
}
测试
2020/07/08 15:40:10 after create: {Time:2020-07-08 14:40:10.024471 +0000 UTC ID:111}
2020/07/08 15:40:10 before update: {Time:2020-07-08 14:40:10.024654 +0000 UTC ID:222 Key:name OldValue:inanzzz}
2020/07/08 15:40:10 after update: {Time:2020-07-08 14:40:10.024816 +0000 UTC ID:222 Key:name NewValue:zzznani}
2020/07/08 15:40:10 the 'user.AfterDelete' event is not registered