// Sources for https://watermill.io/learn/getting-started/ package main import ( "context" "fmt" "log" "time" "github.com/ThreeDotsLabs/watermill" "github.com/ThreeDotsLabs/watermill/message" "github.com/ThreeDotsLabs/watermill/message/router/middleware" "github.com/ThreeDotsLabs/watermill/message/router/plugin" "github.com/ThreeDotsLabs/watermill/pubsub/gochannel" ) var ( // For this example, we're using just a simple logger implementation, // You probably want to ship your own implementation of `watermill.LoggerAdapter`. logger = watermill.NewStdLogger(false, false) ) func main() { router, err := message.NewRouter(message.RouterConfig{}, logger) if err != nil { panic(err) } // SignalsHandler will gracefully shutdown Router when SIGTERM is received. // You can also close the router by just calling `r.Close()`. router.AddPlugin(plugin.SignalsHandler) // Router level middleware are executed for every message sent to the router router.AddMiddleware( // CorrelationID will copy the correlation id from the incoming message's metadata to the produced messages middleware.CorrelationID, // The handler function is retried if it returns an error. // After MaxRetries, the message is Nacked and it's up to the PubSub to resend it. middleware.Retry{ MaxRetries: 3, InitialInterval: time.Millisecond * 100, Logger: logger, }.Middleware, // Recoverer handles panics from handlers. // In this case, it passes them as errors to the Retry middleware. middleware.Recoverer, ) // For simplicity, we are using the gochannel Pub/Sub here, // You can replace it with any Pub/Sub implementation, it will work the same. pubSub := gochannel.NewGoChannel(gochannel.Config{}, logger) // Producing some incoming messages in background go publishMessages(pubSub) // AddHandler returns a handler which can be used to add handler level middleware // or to stop handler. handler := router.AddHandler( "struct_handler", // handler name, must be unique "incoming_messages_topic", // topic from which we will read events pubSub, "outgoing_messages_topic", // topic to which we will publish events pubSub, structHandler{}.Handler, ) // Handler level middleware is only executed for a specific handler // Such middleware can be added the same way the router level ones handler.AddMiddleware(func(h message.HandlerFunc) message.HandlerFunc { return func(message *message.Message) ([]*message.Message, error) { log.Println("executing handler specific middleware for ", message.UUID) return h(message) } }) // just for debug, we are printing all messages received on `incoming_messages_topic` router.AddConsumerHandler( "print_incoming_messages", "incoming_messages_topic", pubSub, printMessages, ) // just for debug, we are printing all events sent to `outgoing_messages_topic` router.AddConsumerHandler( "print_outgoing_messages", "outgoing_messages_topic", pubSub, printMessages, ) // Now that all handlers are registered, we're running the Router. // Run is blocking while the router is running. ctx := context.Background() if err := router.Run(ctx); err != nil { panic(err) } } func publishMessages(publisher message.Publisher) { for { msg := message.NewMessage(watermill.NewUUID(), []byte("Hello, world!")) middleware.SetCorrelationID(watermill.NewUUID(), msg) log.Printf("sending message %s, correlation id: %s\n", msg.UUID, middleware.MessageCorrelationID(msg)) if err := publisher.Publish("incoming_messages_topic", msg); err != nil { panic(err) } time.Sleep(time.Second) } } func printMessages(msg *message.Message) error { fmt.Printf( "\n> Received message: %s\n> %s\n> metadata: %v\n\n", msg.UUID, string(msg.Payload), msg.Metadata, ) return nil } type structHandler struct { // we can add some dependencies here } func (s structHandler) Handler(msg *message.Message) ([]*message.Message, error) { log.Println("structHandler received message", msg.UUID) msg = message.NewMessage(watermill.NewUUID(), []byte("message produced by structHandler")) return message.Messages{msg}, nil }